mirror of https://github.com/apache/kafka.git
MINOR: add ImplicitLinkedHashCollection#moveToEnd (#9269)
Add ImplicitLinkedHashCollection#moveToEnd. Refactor ImplicitLinkedHashCollectionIterator to be a little bit more robust against concurrent modifications to the map (which admittedly should not happen.) Reviewers: Jason Gustafson <jason@confluent.io>
This commit is contained in:
parent
c2273adc25
commit
86013dc9f8
|
@ -143,76 +143,82 @@ public class ImplicitLinkedHashCollection<E extends ImplicitLinkedHashCollection
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ImplicitLinkedHashCollectionIterator implements ListIterator<E> {
|
private class ImplicitLinkedHashCollectionIterator implements ListIterator<E> {
|
||||||
private int cursor = 0;
|
private int index = 0;
|
||||||
private Element cur = head;
|
private Element cur;
|
||||||
private int lastReturnedSlot = INVALID_INDEX;
|
private Element lastReturned;
|
||||||
|
|
||||||
ImplicitLinkedHashCollectionIterator(int index) {
|
ImplicitLinkedHashCollectionIterator(int index) {
|
||||||
|
this.cur = indexToElement(head, elements, head.next());
|
||||||
for (int i = 0; i < index; ++i) {
|
for (int i = 0; i < index; ++i) {
|
||||||
cur = indexToElement(head, elements, cur.next());
|
next();
|
||||||
cursor++;
|
|
||||||
}
|
}
|
||||||
|
this.lastReturned = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasNext() {
|
public boolean hasNext() {
|
||||||
return cursor != size;
|
return cur != head;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasPrevious() {
|
public boolean hasPrevious() {
|
||||||
return cursor != 0;
|
return indexToElement(head, elements, cur.prev()) != head;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public E next() {
|
public E next() {
|
||||||
if (cursor == size) {
|
if (!hasNext()) {
|
||||||
throw new NoSuchElementException();
|
throw new NoSuchElementException();
|
||||||
}
|
}
|
||||||
lastReturnedSlot = cur.next();
|
|
||||||
cur = indexToElement(head, elements, cur.next());
|
|
||||||
++cursor;
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
E returnValue = (E) cur;
|
E returnValue = (E) cur;
|
||||||
|
lastReturned = cur;
|
||||||
|
cur = indexToElement(head, elements, cur.next());
|
||||||
|
++index;
|
||||||
return returnValue;
|
return returnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public E previous() {
|
public E previous() {
|
||||||
if (cursor == 0) {
|
Element prev = indexToElement(head, elements, cur.prev());
|
||||||
|
if (prev == head) {
|
||||||
throw new NoSuchElementException();
|
throw new NoSuchElementException();
|
||||||
}
|
}
|
||||||
|
cur = prev;
|
||||||
|
--index;
|
||||||
|
lastReturned = cur;
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
E returnValue = (E) cur;
|
E returnValue = (E) cur;
|
||||||
cur = indexToElement(head, elements, cur.prev());
|
|
||||||
lastReturnedSlot = cur.next();
|
|
||||||
--cursor;
|
|
||||||
return returnValue;
|
return returnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int nextIndex() {
|
public int nextIndex() {
|
||||||
return cursor;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int previousIndex() {
|
public int previousIndex() {
|
||||||
return cursor - 1;
|
return index - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void remove() {
|
public void remove() {
|
||||||
if (lastReturnedSlot == INVALID_INDEX) {
|
if (lastReturned == null) {
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
|
Element nextElement = indexToElement(head, elements, lastReturned.next());
|
||||||
if (cur == indexToElement(head, elements, lastReturnedSlot)) {
|
removeFromList(head, elements, nextElement.prev());
|
||||||
cursor--;
|
size--;
|
||||||
cur = indexToElement(head, elements, cur.prev());
|
if (lastReturned == cur) {
|
||||||
|
// If the element we are removing was cur, set cur to cur->next.
|
||||||
|
cur = nextElement;
|
||||||
|
} else {
|
||||||
|
// If the element we are removing comes before cur, decrement the index,
|
||||||
|
// since there are now fewer entries before cur.
|
||||||
|
--index;
|
||||||
}
|
}
|
||||||
ImplicitLinkedHashCollection.this.removeElementAtSlot(lastReturnedSlot);
|
lastReturned = null;
|
||||||
|
|
||||||
lastReturnedSlot = INVALID_INDEX;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -561,6 +567,22 @@ public class ImplicitLinkedHashCollection<E extends ImplicitLinkedHashCollection
|
||||||
clear(elements.length);
|
clear(elements.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves an element which is already in the collection so that it comes last
|
||||||
|
* in iteration order.
|
||||||
|
*/
|
||||||
|
final public void moveToEnd(E element) {
|
||||||
|
if (element.prev() == INVALID_INDEX || element.next() == INVALID_INDEX) {
|
||||||
|
throw new RuntimeException("Element " + element + " is not in the collection.");
|
||||||
|
}
|
||||||
|
Element prevElement = indexToElement(head, elements, element.prev());
|
||||||
|
Element nextElement = indexToElement(head, elements, element.next());
|
||||||
|
int slot = prevElement.next();
|
||||||
|
prevElement.setNext(element.next());
|
||||||
|
nextElement.setPrev(element.prev());
|
||||||
|
addToListTail(head, elements, slot);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes all of the elements from this set, and resets the set capacity
|
* Removes all of the elements from this set, and resets the set capacity
|
||||||
* based on the provided expected number of elements.
|
* based on the provided expected number of elements.
|
||||||
|
|
|
@ -502,7 +502,7 @@ public class ImplicitLinkedHashCollectionTest {
|
||||||
addRandomElement(random, existing, coll);
|
addRandomElement(random, existing, coll);
|
||||||
addRandomElement(random, existing, coll);
|
addRandomElement(random, existing, coll);
|
||||||
addRandomElement(random, existing, coll);
|
addRandomElement(random, existing, coll);
|
||||||
removeRandomElement(random, existing, coll);
|
removeRandomElement(random, existing);
|
||||||
expectTraversal(coll.iterator(), existing.iterator());
|
expectTraversal(coll.iterator(), existing.iterator());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -561,8 +561,7 @@ public class ImplicitLinkedHashCollectionTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unlikely-arg-type")
|
@SuppressWarnings("unlikely-arg-type")
|
||||||
private void removeRandomElement(Random random, Collection<Integer> existing,
|
private void removeRandomElement(Random random, Collection<Integer> existing) {
|
||||||
ImplicitLinkedHashCollection<TestElement> coll) {
|
|
||||||
int removeIdx = random.nextInt(existing.size());
|
int removeIdx = random.nextInt(existing.size());
|
||||||
Iterator<Integer> iter = existing.iterator();
|
Iterator<Integer> iter = existing.iterator();
|
||||||
Integer element = null;
|
Integer element = null;
|
||||||
|
@ -582,4 +581,19 @@ public class ImplicitLinkedHashCollectionTest {
|
||||||
assertFalse(element2.equals(element1));
|
assertFalse(element2.equals(element1));
|
||||||
assertTrue(element2.elementKeysAreEqual(element1));
|
assertTrue(element2.elementKeysAreEqual(element1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMoveToEnd() {
|
||||||
|
ImplicitLinkedHashCollection<TestElement> coll = new ImplicitLinkedHashCollection<>();
|
||||||
|
TestElement e1 = new TestElement(1, 1);
|
||||||
|
TestElement e2 = new TestElement(2, 2);
|
||||||
|
TestElement e3 = new TestElement(3, 3);
|
||||||
|
assertTrue(coll.add(e1));
|
||||||
|
assertTrue(coll.add(e2));
|
||||||
|
assertTrue(coll.add(e3));
|
||||||
|
coll.moveToEnd(e1);
|
||||||
|
expectTraversal(coll.iterator(), 2, 3, 1);
|
||||||
|
Assert.assertThrows(RuntimeException.class, () ->
|
||||||
|
coll.moveToEnd(new TestElement(4, 4)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue