/*
 * 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(); } }