Adapt RedisList to API changes in Java 21 SequencedCollections.
Closes #2602 Original pull request: #2608
This commit is contained in:
@@ -17,6 +17,7 @@ package org.springframework.data.redis.support.collections;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
@@ -39,42 +40,32 @@ import org.springframework.util.Assert;
|
||||
* @author Costin Leau
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @author John Blum
|
||||
*/
|
||||
public class DefaultRedisList<E> extends AbstractRedisCollection<E> implements RedisList<E> {
|
||||
|
||||
private final BoundListOperations<String, E> listOps;
|
||||
|
||||
private volatile int maxSize = 0;
|
||||
private volatile boolean capped = false;
|
||||
|
||||
private class DefaultRedisListIterator extends RedisIterator<E> {
|
||||
private volatile int maxSize = 0;
|
||||
|
||||
public DefaultRedisListIterator(Iterator<E> delegate) {
|
||||
super(delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeFromRedisStorage(E item) {
|
||||
DefaultRedisList.this.remove(item);
|
||||
}
|
||||
}
|
||||
private final BoundListOperations<String, E> listOps;
|
||||
|
||||
/**
|
||||
* Constructs a new, uncapped {@link DefaultRedisList} instance.
|
||||
*
|
||||
* @param key Redis key of this list.
|
||||
* @param operations {@link RedisOperations} for the value type of this list.
|
||||
* @param operations {@link RedisOperations} used to retrieve values of the declared {@link E type} from this list.
|
||||
*/
|
||||
public DefaultRedisList(String key, RedisOperations<String, E> operations) {
|
||||
this(operations.boundListOps(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link DefaultRedisList} instance.
|
||||
* Constructs a new {@link DefaultRedisList} instance constrained to the given {@link Integer max size}.
|
||||
*
|
||||
* @param key Redis key of this list.
|
||||
* @param operations {@link RedisOperations} for the value type of this list.
|
||||
* @param maxSize
|
||||
* @param operations {@link RedisOperations} used to retrieve values of the declared {@link E type} from this list.
|
||||
* @param maxSize {@link Integer maximum number of elements} allowed to be stored in this list.
|
||||
* @since 2.6
|
||||
*/
|
||||
public DefaultRedisList(String key, RedisOperations<String, E> operations, int maxSize) {
|
||||
@@ -91,10 +82,10 @@ public class DefaultRedisList<E> extends AbstractRedisCollection<E> implements R
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link DefaultRedisList} instance.
|
||||
* Constructs a new {@link DefaultRedisList} instance constrained to the given {@link Integer max size}.
|
||||
*
|
||||
* @param boundOps {@link BoundListOperations} for the value type of this list.
|
||||
* @param maxSize
|
||||
* @param maxSize {@link Integer maximum number of elements} allowed to be stored in this list.
|
||||
*/
|
||||
public DefaultRedisList(BoundListOperations<String, E> boundOps, int maxSize) {
|
||||
|
||||
@@ -165,6 +156,7 @@ public class DefaultRedisList<E> extends AbstractRedisCollection<E> implements R
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void potentiallyCap(RedisList<E> destination) {
|
||||
if (destination instanceof DefaultRedisList) {
|
||||
((DefaultRedisList<Object>) destination).cap();
|
||||
@@ -217,11 +209,12 @@ public class DefaultRedisList<E> extends AbstractRedisCollection<E> implements R
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
Long result = listOps.remove(1, o);
|
||||
return (result != null && result.longValue() > 0);
|
||||
return result != null && result > 0L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, E element) {
|
||||
|
||||
if (index == 0) {
|
||||
listOps.leftPush(element);
|
||||
cap();
|
||||
@@ -244,23 +237,26 @@ public class DefaultRedisList<E> extends AbstractRedisCollection<E> implements R
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(int index, Collection<? extends E> c) {
|
||||
public boolean addAll(int index, Collection<? extends E> collection) {
|
||||
|
||||
// insert collection in reverse
|
||||
if (index == 0) {
|
||||
Collection<? extends E> reverseC = CollectionUtils.reverse(c);
|
||||
|
||||
for (E e : reverseC) {
|
||||
listOps.leftPush(e);
|
||||
Collection<? extends E> reverseCollection = CollectionUtils.reverse(collection);
|
||||
|
||||
for (E element : reverseCollection) {
|
||||
listOps.leftPush(element);
|
||||
cap();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int size = size();
|
||||
|
||||
if (index == size()) {
|
||||
for (E e : c) {
|
||||
listOps.rightPush(e);
|
||||
for (E element : collection) {
|
||||
listOps.rightPush(element);
|
||||
cap();
|
||||
}
|
||||
return true;
|
||||
@@ -275,34 +271,40 @@ public class DefaultRedisList<E> extends AbstractRedisCollection<E> implements R
|
||||
|
||||
@Override
|
||||
public E get(int index) {
|
||||
|
||||
if (index < 0 || index > size()) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
return listOps.index(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(Object o) {
|
||||
@SuppressWarnings("unchecked")
|
||||
public int indexOf(Object element) {
|
||||
|
||||
Long index = listOps.indexOf((E) element);
|
||||
|
||||
Long index = listOps.indexOf((E) o);
|
||||
return index != null ? index.intValue() : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int lastIndexOf(Object o) {
|
||||
@SuppressWarnings("unchecked")
|
||||
public int lastIndexOf(Object element) {
|
||||
|
||||
Long index = listOps.lastIndexOf((E) element);
|
||||
|
||||
Long index = listOps.lastIndexOf((E) o);
|
||||
return index != null ? index.intValue() : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<E> listIterator() {
|
||||
throw new UnsupportedOperationException();
|
||||
return listIterator(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<E> listIterator(int index) {
|
||||
throw new UnsupportedOperationException();
|
||||
return new ListItr(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -311,10 +313,10 @@ public class DefaultRedisList<E> extends AbstractRedisCollection<E> implements R
|
||||
}
|
||||
|
||||
@Override
|
||||
public E set(int index, E e) {
|
||||
public E set(int index, E element) {
|
||||
|
||||
E object = get(index);
|
||||
listOps.set(index, e);
|
||||
listOps.set(index, element);
|
||||
return object;
|
||||
}
|
||||
|
||||
@@ -338,8 +340,8 @@ public class DefaultRedisList<E> extends AbstractRedisCollection<E> implements R
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offer(E e) {
|
||||
listOps.rightPush(e);
|
||||
public boolean offer(E element) {
|
||||
listOps.rightPush(element);
|
||||
cap();
|
||||
return true;
|
||||
}
|
||||
@@ -372,14 +374,14 @@ public class DefaultRedisList<E> extends AbstractRedisCollection<E> implements R
|
||||
//
|
||||
|
||||
@Override
|
||||
public void addFirst(E e) {
|
||||
listOps.leftPush(e);
|
||||
public void addFirst(E element) {
|
||||
listOps.leftPush(element);
|
||||
cap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLast(E e) {
|
||||
add(e);
|
||||
public void addLast(E element) {
|
||||
add(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -396,22 +398,22 @@ public class DefaultRedisList<E> extends AbstractRedisCollection<E> implements R
|
||||
|
||||
@Override
|
||||
public E getLast() {
|
||||
E e = peekLast();
|
||||
if (e == null) {
|
||||
E element = peekLast();
|
||||
if (element == null) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
return e;
|
||||
return element;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offerFirst(E e) {
|
||||
addFirst(e);
|
||||
public boolean offerFirst(E element) {
|
||||
addFirst(element);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offerLast(E e) {
|
||||
addLast(e);
|
||||
public boolean offerLast(E element) {
|
||||
addLast(element);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -442,16 +444,16 @@ public class DefaultRedisList<E> extends AbstractRedisCollection<E> implements R
|
||||
@Override
|
||||
public E pop() {
|
||||
|
||||
E e = poll();
|
||||
if (e == null) {
|
||||
E element = poll();
|
||||
if (element == null) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
return e;
|
||||
return element;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void push(E e) {
|
||||
addFirst(e);
|
||||
public void push(E element) {
|
||||
addFirst(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -460,52 +462,52 @@ public class DefaultRedisList<E> extends AbstractRedisCollection<E> implements R
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeFirstOccurrence(Object o) {
|
||||
return remove(o);
|
||||
public boolean removeFirstOccurrence(Object element) {
|
||||
return remove(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public E removeLast() {
|
||||
E e = pollLast();
|
||||
if (e == null) {
|
||||
E element = pollLast();
|
||||
if (element == null) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
return e;
|
||||
return element;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeLastOccurrence(Object o) {
|
||||
Long result = listOps.remove(-1, o);
|
||||
return (result != null && result.longValue() > 0);
|
||||
public boolean removeLastOccurrence(Object element) {
|
||||
Long result = listOps.remove(-1, element);
|
||||
return result != null && result > 0L;
|
||||
}
|
||||
|
||||
//
|
||||
// BlockingQueue
|
||||
//
|
||||
@Override
|
||||
public int drainTo(Collection<? super E> c, int maxElements) {
|
||||
if (this.equals(c)) {
|
||||
public int drainTo(Collection<? super E> collection, int maxElements) {
|
||||
|
||||
if (this.equals(collection)) {
|
||||
throw new IllegalArgumentException("Cannot drain a queue to itself");
|
||||
}
|
||||
|
||||
int size = size();
|
||||
int loop = (size >= maxElements ? maxElements : size);
|
||||
int loop = (Math.min(size(), maxElements));
|
||||
|
||||
for (int index = 0; index < loop; index++) {
|
||||
c.add(poll());
|
||||
collection.add(poll());
|
||||
}
|
||||
|
||||
return loop;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int drainTo(Collection<? super E> c) {
|
||||
return drainTo(c, size());
|
||||
public int drainTo(Collection<? super E> collection) {
|
||||
return drainTo(collection, size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException {
|
||||
return offer(e);
|
||||
public boolean offer(E element, long timeout, TimeUnit unit) throws InterruptedException {
|
||||
return offer(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -515,8 +517,8 @@ public class DefaultRedisList<E> extends AbstractRedisCollection<E> implements R
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(E e) throws InterruptedException {
|
||||
offer(e);
|
||||
public void put(E element) throws InterruptedException {
|
||||
offer(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -535,13 +537,13 @@ public class DefaultRedisList<E> extends AbstractRedisCollection<E> implements R
|
||||
//
|
||||
|
||||
@Override
|
||||
public boolean offerFirst(E e, long timeout, TimeUnit unit) throws InterruptedException {
|
||||
return offerFirst(e);
|
||||
public boolean offerFirst(E element, long timeout, TimeUnit unit) {
|
||||
return offerFirst(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offerLast(E e, long timeout, TimeUnit unit) throws InterruptedException {
|
||||
return offerLast(e);
|
||||
public boolean offerLast(E element, long timeout, TimeUnit unit) {
|
||||
return offerLast(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -552,18 +554,18 @@ public class DefaultRedisList<E> extends AbstractRedisCollection<E> implements R
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public E pollLast(long timeout, TimeUnit unit) throws InterruptedException {
|
||||
public E pollLast(long timeout, TimeUnit unit) {
|
||||
return listOps.rightPop(timeout, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putFirst(E e) throws InterruptedException {
|
||||
add(e);
|
||||
public void putFirst(E element) {
|
||||
add(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putLast(E e) throws InterruptedException {
|
||||
put(e);
|
||||
public void putLast(E element) throws InterruptedException {
|
||||
put(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -574,7 +576,7 @@ public class DefaultRedisList<E> extends AbstractRedisCollection<E> implements R
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public E takeLast() throws InterruptedException {
|
||||
public E takeLast() {
|
||||
return pollLast(0, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@@ -592,4 +594,131 @@ public class DefaultRedisList<E> extends AbstractRedisCollection<E> implements R
|
||||
listOps.trim(0, maxSize - 1);
|
||||
}
|
||||
}
|
||||
|
||||
private class DefaultRedisListIterator extends RedisIterator<E> {
|
||||
|
||||
public DefaultRedisListIterator(Iterator<E> delegate) {
|
||||
super(delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeFromRedisStorage(E item) {
|
||||
DefaultRedisList.this.remove(item);
|
||||
}
|
||||
}
|
||||
|
||||
private class Itr implements Iterator<E> {
|
||||
|
||||
/**
|
||||
* Index of the {@link E element} in this iteration to be returned by subsequent call to {@link #next()}.
|
||||
*/
|
||||
int cursor = 0;
|
||||
|
||||
/**
|
||||
* Index of the {@link E element} in this iteration last returned by a call to {@link #next()}.
|
||||
*/
|
||||
int lastReturnedElementIndex = -1;
|
||||
|
||||
@Nullable
|
||||
E lastReturnedElement;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return this.cursor < size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E next() {
|
||||
|
||||
try {
|
||||
int index = this.cursor;
|
||||
this.lastReturnedElement = get(index);
|
||||
this.cursor = index + 1;
|
||||
this.lastReturnedElementIndex = index;
|
||||
return this.lastReturnedElement;
|
||||
} catch (IndexOutOfBoundsException cause) {
|
||||
throw new NoSuchElementException(cause);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("all")
|
||||
public void remove() {
|
||||
|
||||
Assert.state(this.lastReturnedElement != null,
|
||||
"Next must be called before remove");
|
||||
|
||||
if (!DefaultRedisList.this.remove(this.lastReturnedElement)) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
|
||||
this.lastReturnedElementIndex = -1;
|
||||
this.lastReturnedElement = null;
|
||||
this.cursor--;
|
||||
}
|
||||
}
|
||||
|
||||
private class ListItr extends Itr implements ListIterator<E> {
|
||||
|
||||
ListItr(int index) {
|
||||
this.cursor = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPrevious() {
|
||||
return this.cursor > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextIndex() {
|
||||
return this.cursor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int previousIndex() {
|
||||
return this.cursor - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(E element) {
|
||||
|
||||
try {
|
||||
int index = this.cursor;
|
||||
DefaultRedisList.this.add(index, element);
|
||||
this.lastReturnedElementIndex = -1;
|
||||
this.lastReturnedElement = null;
|
||||
this.cursor = index + 1;
|
||||
} catch (IndexOutOfBoundsException cause) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public E previous() {
|
||||
|
||||
try {
|
||||
int index = this.cursor - 1;
|
||||
this.lastReturnedElement = get(index);
|
||||
this.lastReturnedElementIndex = index;
|
||||
this.cursor = index;
|
||||
return this.lastReturnedElement;
|
||||
} catch (IndexOutOfBoundsException cause) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
public void set(E element) {
|
||||
|
||||
Assert.state(this.lastReturnedElement != null,
|
||||
"next() or previous() must be called before set(:E)");
|
||||
|
||||
try {
|
||||
DefaultRedisList.this.set(this.lastReturnedElementIndex, element);
|
||||
} catch (IndexOutOfBoundsException cause) {
|
||||
throw new ConcurrentModificationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.redis.support.collections;
|
||||
|
||||
import static org.springframework.data.redis.connection.RedisListCommands.*;
|
||||
import static org.springframework.data.redis.connection.RedisListCommands.Direction;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Deque;
|
||||
@@ -36,6 +36,7 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Mark Paluch
|
||||
* @author John Blum
|
||||
*/
|
||||
public interface RedisList<E> extends RedisCollection<E>, List<E>, BlockingDeque<E> {
|
||||
|
||||
@@ -51,11 +52,12 @@ public interface RedisList<E> extends RedisCollection<E>, List<E>, BlockingDeque
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link RedisList} instance.
|
||||
* Factory method used to construct a new {@link RedisList} from a Redis list reference by the given {@link String
|
||||
* key}.
|
||||
*
|
||||
* @param key Redis key of this list.
|
||||
* @param operations {@link RedisOperations} for the value type of this list.
|
||||
* @param maxSize
|
||||
* @param maxSize {@link Integer} used to constrain the size of the list.
|
||||
* @since 2.6
|
||||
*/
|
||||
static <E> RedisList<E> create(String key, RedisOperations<String, E> operations, int maxSize) {
|
||||
@@ -73,10 +75,10 @@ public interface RedisList<E> extends RedisCollection<E>, List<E>, BlockingDeque
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link DefaultRedisList} instance.
|
||||
* Constructs a new {@link DefaultRedisList}.
|
||||
*
|
||||
* @param boundOps {@link BoundListOperations} for the value type of this list.
|
||||
* @param maxSize
|
||||
* @param maxSize {@link Integer} constraining the size of the list.
|
||||
* @since 2.6
|
||||
*/
|
||||
static <E> RedisList<E> create(BoundListOperations<String, E> boundOps, int maxSize) {
|
||||
@@ -98,6 +100,25 @@ public interface RedisList<E> extends RedisCollection<E>, List<E>, BlockingDeque
|
||||
@Nullable
|
||||
E moveFirstTo(RedisList<E> destination, Direction destinationPosition);
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the first element of the list stored at the bound key, and pushes the element at the
|
||||
* first/last element (head/tail depending on the {@link Direction destinationPosition} argument) of the list stored
|
||||
* at {@link RedisList destination}.
|
||||
* <p>
|
||||
* <b>Blocks connection</b> until element available or {@code timeout} reached.
|
||||
*
|
||||
* @param destination must not be {@literal null}.
|
||||
* @param destinationPosition must not be {@literal null}.
|
||||
* @param timeout
|
||||
* @param unit must not be {@literal null}.
|
||||
* @return
|
||||
* @since 2.6
|
||||
* @see Direction#first()
|
||||
* @see Direction#last()
|
||||
*/
|
||||
@Nullable
|
||||
E moveFirstTo(RedisList<E> destination, Direction destinationPosition, long timeout, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the first element of the list stored at the bound key, and pushes the element at the
|
||||
* first/last element (head/tail depending on the {@link Direction destinationPosition} argument) of the list stored
|
||||
@@ -123,25 +144,6 @@ public interface RedisList<E> extends RedisCollection<E>, List<E>, BlockingDeque
|
||||
TimeoutUtils.toMillis(timeout.toMillis(), TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the first element of the list stored at the bound key, and pushes the element at the
|
||||
* first/last element (head/tail depending on the {@link Direction destinationPosition} argument) of the list stored
|
||||
* at {@link RedisList destination}.
|
||||
* <p>
|
||||
* <b>Blocks connection</b> until element available or {@code timeout} reached.
|
||||
*
|
||||
* @param destination must not be {@literal null}.
|
||||
* @param destinationPosition must not be {@literal null}.
|
||||
* @param timeout
|
||||
* @param unit must not be {@literal null}.
|
||||
* @return
|
||||
* @since 2.6
|
||||
* @see Direction#first()
|
||||
* @see Direction#last()
|
||||
*/
|
||||
@Nullable
|
||||
E moveFirstTo(RedisList<E> destination, Direction destinationPosition, long timeout, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the last element of the list stored at the bound key, and pushes the element at the
|
||||
* first/last element (head/tail depending on the {@link Direction destinationPosition} argument) of the list stored
|
||||
@@ -157,6 +159,25 @@ public interface RedisList<E> extends RedisCollection<E>, List<E>, BlockingDeque
|
||||
@Nullable
|
||||
E moveLastTo(RedisList<E> destination, Direction destinationPosition);
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the last element of the list stored at the bound key, and pushes the element at the
|
||||
* first/last element (head/tail depending on the {@link Direction destinationPosition} argument) of the list stored
|
||||
* at {@link RedisList destination}.
|
||||
* <p>
|
||||
* <b>Blocks connection</b> until element available or {@code timeout} reached.
|
||||
*
|
||||
* @param destination must not be {@literal null}.
|
||||
* @param destinationPosition must not be {@literal null}.
|
||||
* @param timeout
|
||||
* @param unit must not be {@literal null}.
|
||||
* @return
|
||||
* @since 2.6
|
||||
* @see Direction#first()
|
||||
* @see Direction#last()
|
||||
*/
|
||||
@Nullable
|
||||
E moveLastTo(RedisList<E> destination, Direction destinationPosition, long timeout, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the last element of the list stored at the bound key, and pushes the element at the
|
||||
* first/last element (head/tail depending on the {@link Direction destinationPosition} argument) of the list stored
|
||||
@@ -182,25 +203,6 @@ public interface RedisList<E> extends RedisCollection<E>, List<E>, BlockingDeque
|
||||
TimeoutUtils.toMillis(timeout.toMillis(), TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the last element of the list stored at the bound key, and pushes the element at the
|
||||
* first/last element (head/tail depending on the {@link Direction destinationPosition} argument) of the list stored
|
||||
* at {@link RedisList destination}.
|
||||
* <p>
|
||||
* <b>Blocks connection</b> until element available or {@code timeout} reached.
|
||||
*
|
||||
* @param destination must not be {@literal null}.
|
||||
* @param destinationPosition must not be {@literal null}.
|
||||
* @param timeout
|
||||
* @param unit must not be {@literal null}.
|
||||
* @return
|
||||
* @since 2.6
|
||||
* @see Direction#first()
|
||||
* @see Direction#last()
|
||||
*/
|
||||
@Nullable
|
||||
E moveLastTo(RedisList<E> destination, Direction destinationPosition, long timeout, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Get elements between {@code start} and {@code end} from list at the bound key.
|
||||
*
|
||||
@@ -229,4 +231,92 @@ public interface RedisList<E> extends RedisCollection<E>, List<E>, BlockingDeque
|
||||
* @see <a href="https://redis.io/commands/ltrim">Redis Documentation: LTRIM</a>
|
||||
*/
|
||||
RedisList<E> trim(long start, long end);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* This method is forward-compatible with Java 21 {@literal SequencedCollections}.
|
||||
*
|
||||
* @param element element to be added to the head of the collection.
|
||||
*/
|
||||
@Override
|
||||
default void addFirst(E element) {
|
||||
add(0, element);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* This method is forward-compatible with Java 21 {@literal SequencedCollections}.
|
||||
*
|
||||
* @param element element to be added to be added the end of the collection.
|
||||
*/
|
||||
default void addLast(E element) {
|
||||
add(element);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* This method is forward-compatible with Java 21 {@literal SequencedCollections}.
|
||||
*
|
||||
* @return the head of this {@link Deque}.
|
||||
*/
|
||||
@Nullable
|
||||
default E getFirst() {
|
||||
return peekFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* This method is forward-compatible with Java 21 {@literal SequencedCollections}.
|
||||
*
|
||||
* @return the tail of this {@link Deque}.
|
||||
*/
|
||||
@Nullable
|
||||
default E getLast() {
|
||||
return peekLast();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* This method is forward-compatible with Java 21 {@literal SequencedCollections}.
|
||||
*
|
||||
* @return the head of this {@link Deque}.
|
||||
*/
|
||||
@Nullable
|
||||
default E removeFirst() {
|
||||
return pollFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* This method is forward-compatible with Java 21 {@literal SequencedCollections}.
|
||||
*
|
||||
* @return the tail of this {@link Deque}.
|
||||
*/
|
||||
@Nullable
|
||||
default E removeLast() {
|
||||
return pollLast();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reverse-ordered view of this collection.
|
||||
* <p>
|
||||
* The encounter order of elements returned by the view is the inverse of the encounter order of the elements
|
||||
* stored in this collection. The reverse ordering affects all order-sensitive operations, including any operations
|
||||
* on further views of the returned view. If the collection implementation permits modifications to this view,
|
||||
* the modifications "write-through" to the underlying collection. Changes to the underlying collection might
|
||||
* or might not be visible in this reversed view, depending upon the implementation.
|
||||
* <p>
|
||||
* This method is forward-compatible with Java 21 {@literal SequencedCollections}.
|
||||
*
|
||||
* @return a reverse-ordered view of this collection.
|
||||
*/
|
||||
default RedisList<E> reversed() {
|
||||
return new ReversedRedisListView<>(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,739 @@
|
||||
/*
|
||||
* Copyright 2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.support.collections;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
import java.util.Spliterator;
|
||||
import java.util.Spliterators;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.IntFunction;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.UnaryOperator;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.springframework.data.redis.connection.DataType;
|
||||
import org.springframework.data.redis.connection.RedisListCommands.Direction;
|
||||
import org.springframework.data.redis.core.RedisOperations;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Implementation and view of an existing {@link RedisList} where the elements in the list (deque) are returned in
|
||||
* reverse order.
|
||||
*
|
||||
* @author John Blum
|
||||
* @author Mark Paluch
|
||||
* @param <E> {@link Class type} of the {@link Object elements} contained in the underlying, wrapped {@link RedisList}.
|
||||
* @since 3.2
|
||||
*/
|
||||
class ReversedRedisListView<E> implements RedisList<E> {
|
||||
|
||||
private final RedisList<E> base;
|
||||
|
||||
ReversedRedisListView(RedisList<E> list) {
|
||||
this.base = list;
|
||||
}
|
||||
|
||||
// ========== RedisCollection ==========
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return this.base.getKey();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Long getExpire() {
|
||||
return this.base.getExpire();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisOperations<String, ?> getOperations() {
|
||||
return this.base.getOperations();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DataType getType() {
|
||||
return this.base.getType();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Boolean expire(long timeout, TimeUnit unit) {
|
||||
return this.base.expire(timeout, unit);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Boolean expireAt(Date date) {
|
||||
return this.base.expireAt(date);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Boolean persist() {
|
||||
return this.base.persist();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public E moveFirstTo(RedisList<E> destination, Direction destinationPosition) {
|
||||
return this.base.moveLastTo(destination, destinationPosition);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public E moveFirstTo(RedisList<E> destination, Direction destinationPosition, long timeout, TimeUnit unit) {
|
||||
return base.moveLastTo(destination, destinationPosition, timeout, unit);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public E moveLastTo(RedisList<E> destination, Direction destinationPosition) {
|
||||
return base.moveFirstTo(destination, destinationPosition);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public E moveLastTo(RedisList<E> destination, Direction destinationPosition, long timeout, TimeUnit unit) {
|
||||
return base.moveFirstTo(destination, destinationPosition, timeout, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<E> range(long start, long end) {
|
||||
return this.base.range(end, start);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rename(String newKey) {
|
||||
this.base.rename(newKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisList<E> trim(int start, int end) {
|
||||
this.base.trim(end, start);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisList<E> trim(long start, long end) {
|
||||
this.base.trim(end, start);
|
||||
return this;
|
||||
}
|
||||
|
||||
// ========== Iterable ==========
|
||||
|
||||
@Override
|
||||
public void forEach(Consumer<? super E> action) {
|
||||
for (E e : this)
|
||||
action.accept(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return new DescendingIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spliterator<E> spliterator() {
|
||||
return Spliterators.spliteratorUnknownSize(new DescendingIterator(), 0);
|
||||
}
|
||||
|
||||
// ========== Collection ==========
|
||||
|
||||
@Override
|
||||
public boolean add(E element) {
|
||||
this.base.add(0, element);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean addAll(Collection<? extends E> collection) {
|
||||
|
||||
return !org.springframework.util.CollectionUtils.isEmpty(collection)
|
||||
&& this.base.addAll(0, Arrays.asList(reverse((E[]) collection.toArray())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
this.base.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object element) {
|
||||
return this.base.contains(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> collection) {
|
||||
return this.base.containsAll(collection);
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(obj instanceof List<?> that)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ListIterator<E> thisListIterator = this.listIterator();
|
||||
ListIterator<?> thatListIterator = that.listIterator();
|
||||
|
||||
while (thisListIterator.hasNext() && thatListIterator.hasNext()) {
|
||||
if (!(Objects.equals(thisListIterator.next(), thatListIterator.next()))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return !(thisListIterator.hasNext() || thatListIterator.hasNext());
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hashCode = 1;
|
||||
for (E element : this) {
|
||||
hashCode = 31 * hashCode + Objects.hashCode(element);
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return this.base.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<E> parallelStream() {
|
||||
return StreamSupport.stream(spliterator(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object element) {
|
||||
|
||||
Iterator<E> it = iterator();
|
||||
|
||||
while (it.hasNext()) {
|
||||
if (Objects.equals(element, it.next())) {
|
||||
it.remove();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> collection) {
|
||||
|
||||
Objects.requireNonNull(collection);
|
||||
|
||||
Iterator<?> it = iterator();
|
||||
boolean modified = false;
|
||||
|
||||
while (it.hasNext()) {
|
||||
if (collection.contains(it.next())) {
|
||||
it.remove();
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> collection) {
|
||||
|
||||
Objects.requireNonNull(collection);
|
||||
|
||||
Iterator<E> it = iterator();
|
||||
boolean modified = false;
|
||||
|
||||
while (it.hasNext()) {
|
||||
if (!collection.contains(it.next())) {
|
||||
it.remove();
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return this.base.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<E> stream() {
|
||||
return StreamSupport.stream(spliterator(), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
return reverse(this.base.toArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T[] toArray(T[] array) {
|
||||
return toArrayReversed(this.base, array);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T[] toArray(IntFunction<T[]> generator) {
|
||||
return reverse(this.base.toArray(generator));
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
||||
Iterator<E> it = iterator();
|
||||
|
||||
if (!it.hasNext()) {
|
||||
return "[]";
|
||||
}
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder("[");
|
||||
|
||||
for (;;) {
|
||||
E element = it.next();
|
||||
stringBuilder.append(element == this ? "(this Collection)" : element);
|
||||
if (!it.hasNext()) {
|
||||
return stringBuilder.append(']').toString();
|
||||
}
|
||||
stringBuilder.append(',').append(' ');
|
||||
}
|
||||
}
|
||||
|
||||
// ========== List ==========
|
||||
|
||||
@Override
|
||||
public void add(int index, E element) {
|
||||
|
||||
int size = this.base.size();
|
||||
|
||||
checkClosedRange(index, size);
|
||||
this.base.add(size - index, element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(int index, Collection<? extends E> c) {
|
||||
|
||||
int size = base.size();
|
||||
|
||||
checkClosedRange(index, size);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
E[] adds = (E[]) c.toArray();
|
||||
|
||||
if (adds.length == 0) {
|
||||
return false;
|
||||
} else {
|
||||
base.addAll(size - index, Arrays.asList(reverse(adds)));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public E get(int index) {
|
||||
int size = base.size();
|
||||
Objects.checkIndex(index, size);
|
||||
return this.base.get(size - index - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("all")
|
||||
public int indexOf(Object element) {
|
||||
int lastIndex = this.base.lastIndexOf(element);
|
||||
return lastIndex == -1 ? -1 : this.base.size() - lastIndex - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("all")
|
||||
public int lastIndexOf(Object element) {
|
||||
int index = this.base.indexOf(element);
|
||||
return index == -1 ? -1 : this.base.size() - index - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<E> listIterator() {
|
||||
return new DescendingListIterator(base.size(), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<E> listIterator(int index) {
|
||||
int size = this.base.size();
|
||||
checkClosedRange(index, size);
|
||||
return new DescendingListIterator(size, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public E remove(int index) {
|
||||
int size = this.base.size();
|
||||
Objects.checkIndex(index, size);
|
||||
return this.base.remove(size - index - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeIf(Predicate<? super E> filter) {
|
||||
return this.base.removeIf(filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceAll(UnaryOperator<E> operator) {
|
||||
this.base.replaceAll(operator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sort(Comparator<? super E> comparator) {
|
||||
this.base.sort(Collections.reverseOrder(comparator));
|
||||
}
|
||||
|
||||
@Override
|
||||
public E set(int index, E element) {
|
||||
int size = this.base.size();
|
||||
Objects.checkIndex(index, size);
|
||||
return this.base.set(size - index - 1, element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<E> subList(int fromIndex, int toIndex) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
// ========== BlockingDeque ==========
|
||||
|
||||
@Override
|
||||
public E element() {
|
||||
return peekLast();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offer(E element) {
|
||||
return this.base.offerFirst(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offer(E element, long timeout, TimeUnit unit) throws InterruptedException {
|
||||
return this.base.offerFirst(element, timeout, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offerFirst(E element) {
|
||||
return this.base.offerLast(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offerFirst(E element, long timeout, TimeUnit unit) throws InterruptedException {
|
||||
return this.base.offerLast(element, timeout, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offerLast(E element) {
|
||||
return this.base.offerFirst(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offerLast(E element, long timeout, TimeUnit unit) throws InterruptedException {
|
||||
return this.base.offerFirst(element, timeout, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putFirst(E element) throws InterruptedException {
|
||||
this.base.putLast(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putLast(E element) throws InterruptedException {
|
||||
this.base.putFirst(element);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public E pollFirst(long timeout, TimeUnit unit) throws InterruptedException {
|
||||
return this.base.pollLast(timeout, unit);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public E pollLast(long timeout, TimeUnit unit) throws InterruptedException {
|
||||
return this.base.pollFirst(timeout, unit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(E element) throws InterruptedException {
|
||||
this.base.offerFirst(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeFirstOccurrence(Object element) {
|
||||
return this.base.removeLastOccurrence(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeLastOccurrence(Object element) {
|
||||
return this.base.removeFirstOccurrence(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public E take() throws InterruptedException {
|
||||
return takeFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E takeFirst() throws InterruptedException {
|
||||
return this.base.takeLast();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E takeLast() throws InterruptedException {
|
||||
return this.base.takeFirst();
|
||||
}
|
||||
|
||||
// ========== Deque ==========
|
||||
|
||||
@Override
|
||||
public Iterator<E> descendingIterator() {
|
||||
return this.base.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int drainTo(Collection<? super E> collection) {
|
||||
return drainTo(collection, size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int drainTo(Collection<? super E> collection, int maxElements) {
|
||||
|
||||
if (this.equals(collection)) {
|
||||
throw new IllegalArgumentException("Cannot drain a queue to itself");
|
||||
}
|
||||
|
||||
int loop = Math.min(size(), maxElements);
|
||||
|
||||
for (int index = 0; index < loop; index++) {
|
||||
collection.add(poll());
|
||||
}
|
||||
|
||||
return loop;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E peek() {
|
||||
return peekLast();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E peekFirst() {
|
||||
return this.base.peekLast();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E peekLast() {
|
||||
return this.base.peekFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E poll() {
|
||||
return pollLast();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
|
||||
return pollLast(0, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public E pollFirst() {
|
||||
return this.base.pollLast();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E pollLast() {
|
||||
return this.base.pollFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E pop() {
|
||||
|
||||
E element = poll();
|
||||
|
||||
if (element == null) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void push(E element) {
|
||||
this.base.addLast(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int remainingCapacity() {
|
||||
return this.base.remainingCapacity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E remove() {
|
||||
return pollLast();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisList<E> reversed() {
|
||||
return this.base;
|
||||
}
|
||||
|
||||
class DescendingIterator implements Iterator<E> {
|
||||
|
||||
final ListIterator<E> it = base.listIterator(base.size());
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return this.it.hasPrevious();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E next() {
|
||||
return this.it.previous();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
this.it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
class DescendingListIterator implements ListIterator<E> {
|
||||
|
||||
final ListIterator<E> it;
|
||||
|
||||
DescendingListIterator(int size, int position) {
|
||||
|
||||
if (position < 0 || position > size) {
|
||||
String message = String.format("Position [%d] is out of bounds: [0, %d]", position, size);
|
||||
throw new IndexOutOfBoundsException(message);
|
||||
}
|
||||
|
||||
this.it = base.listIterator(size - position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return it.hasPrevious();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E next() {
|
||||
return it.previous();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPrevious() {
|
||||
return it.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E previous() {
|
||||
return it.next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextIndex() {
|
||||
return base.size() - it.nextIndex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int previousIndex() {
|
||||
return nextIndex() - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
it.remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(E e) {
|
||||
it.set(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(E e) {
|
||||
it.add(e);
|
||||
it.previous();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverses the elements of an array in-place.
|
||||
*
|
||||
* @param <T> the array component type
|
||||
* @param array the array to be reversed
|
||||
* @return the reversed array, always the same array as the argument
|
||||
*/
|
||||
static <T> T[] reverse(T[] array) {
|
||||
|
||||
int limit = array.length / 2;
|
||||
|
||||
for (int i = 0, j = array.length - 1; i < limit; i++, j--) {
|
||||
swap(array, i, j);
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
private static <T> void swap(T[] array, int indexOne, int indexTwo) {
|
||||
T element = array[indexOne];
|
||||
array[indexOne] = array[indexTwo];
|
||||
array[indexTwo] = element;
|
||||
}
|
||||
|
||||
static <T> T[] toArrayReversed(Collection<?> collection, T[] array) {
|
||||
|
||||
T[] newArray = reverse(collection.toArray(Arrays.copyOfRange(array, 0, 0)));
|
||||
|
||||
if (newArray.length > array.length) {
|
||||
return newArray;
|
||||
} else {
|
||||
System.arraycopy(newArray, 0, array, 0, newArray.length);
|
||||
if (array.length > newArray.length) {
|
||||
array[newArray.length] = null;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
}
|
||||
|
||||
static void checkClosedRange(int index, int size) {
|
||||
if (index < 0 || index > size) {
|
||||
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user