/*
* Copyright (c) 2011-2019 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
* which is available at https://www.apache.org/licenses/LICENSE-2.0.
*
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
*/
package io.vertx.core.impl.utils;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
A concurrent cyclic sequence of elements that can be used for round robin.
The sequence is immutable and modifications are done with copy-on-write using add(Object)
and remove(Object)
to return a modified copy of the current instance.
The internal counter uses a volatile index, so it can be incremented concurrently by several
threads without locking.
Author: Tim Fox, Julien Viet
/**
* A concurrent cyclic sequence of elements that can be used for round robin.
* <p/>
* The sequence is immutable and modifications are done with copy-on-write using
* {@link #add(Object)} and {@link #remove(Object)} to return a modified copy of the current instance.
* <p/>
* The internal counter uses a volatile index, so it can be incremented concurrently by several
* threads without locking.
*
* @author <a href="http://tfox.org">Tim Fox</a>
* @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
*/
public class ConcurrentCyclicSequence<T> implements Iterable<T>, Iterator<T> {
private static final Object[] EMPTY_ARRAY = new Object[0];
private final AtomicInteger pos;
private final Object[] elements;
Create a new empty sequence.
/**
* Create a new empty sequence.
*/
public ConcurrentCyclicSequence() {
this(0, EMPTY_ARRAY);
}
Create a new empty sequence.
/**
* Create a new empty sequence.
*/
@SafeVarargs
public ConcurrentCyclicSequence(T... elements) {
this(0, Arrays.copyOf(elements, elements.length, Object[].class));
}
private ConcurrentCyclicSequence(int pos, Object[] elements) {
this.pos = new AtomicInteger(pos);
this.elements = elements;
}
Returns: the current index
/**
* @return the current index
*/
public int index() {
return elements.length > 0 ? pos.get() % elements.length : 0;
}
Returns: the first element or null
when the sequence is empty
/**
* @return the first element or {@code null} when the sequence is empty
*/
@SuppressWarnings("unchecked")
public T first() {
return (T) (elements.length > 0 ? elements[0] : null);
}
Copy the current sequence, add element
at the tail of this sequence and returns it. Params: - element – the element to add
Returns: the resulting sequence
/**
* Copy the current sequence, add {@code element} at the tail of this sequence and returns it.
* @param element the element to add
* @return the resulting sequence
*/
public ConcurrentCyclicSequence<T> add(T element) {
int len = elements.length;
Object[] copy = Arrays.copyOf(elements, len + 1);
copy[len] = element;
return new ConcurrentCyclicSequence<>(pos.get(), copy);
}
Remove the first occurrence of element
in this sequence and returns it. If the sequence does not contains element
, this instance is returned instead. Params: - element – the element to remove
Returns: the resulting sequence
/**
* Remove the first occurrence of {@code element} in this sequence and returns it.
* <p/>
* If the sequence does not contains {@code element}, this instance is returned instead.
*
* @param element the element to remove
* @return the resulting sequence
*/
public ConcurrentCyclicSequence<T> remove(T element) {
int len = elements.length;
for (int i = 0;i < len;i++) {
if (Objects.equals(element, elements[i])) {
if (len > 1) {
Object[] copy = new Object[len - 1];
System.arraycopy(elements,0, copy, 0, i);
System.arraycopy(elements, i + 1, copy, i, len - i - 1);
return new ConcurrentCyclicSequence<>(pos.get() % copy.length, copy);
} else {
return new ConcurrentCyclicSequence<>();
}
}
}
return this;
}
Returns: always true
/**
* @return always {@code true}
*/
@Override
public boolean hasNext() {
return true;
}
Returns: the next element in the sequence
/**
* @return the next element in the sequence
*/
@SuppressWarnings("unchecked")
@Override
public T next() {
int len = elements.length;
switch (len) {
case 0:
return null;
case 1:
return (T) elements[0];
default:
int p;
p = pos.getAndIncrement();
return (T) elements[Math.abs(p % len)];
}
}
Returns: the size of this sequence
/**
* @return the size of this sequence
*/
public int size() {
return elements.length;
}
Returns: an iterator starting at the first element of the sequence, the iterator will not throw ConcurrentModificationException
/**
* @return an iterator starting at the first element of the sequence, the iterator will not throw {@link ConcurrentModificationException}
*/
@Override
@SuppressWarnings("unchecked")
public Iterator<T> iterator() {
return Arrays.asList((T[]) elements).iterator();
}
}