package org.opentripplanner.analyst.broker; import java.util.Iterator; import java.util.Spliterator; import java.util.function.Consumer; import java.util.function.Predicate; /** * */ public class CircularList<T> implements Iterable<T> { Node<T> head = null; public class CircularListIterator implements Iterator<T> { Node<T> curr = head; @Override public boolean hasNext() { return curr != null; } @Override public T next() { T currElement = curr.element; curr = curr.next; if (curr == head) { curr = null; // No more elements } return currElement; } } @Override public Iterator<T> iterator() { return new CircularListIterator(); } @Override public void forEach(Consumer<? super T> action) { Node<T> curr = head; do { action.accept(curr.element); curr = curr.next; } while (curr != head); } @Override public Spliterator<T> spliterator() { throw new UnsupportedOperationException(); } private static class Node<T> { Node prev; Node next; T element; } /** Insert an element at the tail of the circular list (the position just previous to the head). */ public void insertAtTail (T element) { Node<T> newNode = new Node<>(); newNode.element = element; if (head == null) { newNode.next = newNode; newNode.prev = newNode; head = newNode; } else { newNode.prev = head.prev; newNode.next = head; head.prev.next = newNode; head.prev = newNode; } } /** Insert a new element at the head of the circular list. */ public void insertAtHead (T element) { // Add at tail and then back the head up one element, wrapping around to the tail. insertAtTail(element); head = head.prev; } /** Get the element at the head of the list without removing it. */ public T peek () { return head.element; } /** Take an element off the head of the list, removing it from the list. */ public T pop () { if (head == null) { return null; } T element = head.element; if (head == head.next) { // Last node consumed. List is now empty. head = null; } else { head.prev.next = head.next; head.next.prev = head.prev; head = head.next; } return element; } /** * Advance the head of the circular list forward to the next element. * Return the element at the head of the list, leaving that element in the list. */ public T advance() { if (head == null) { return null; } T headElement = head.element; head = head.next; return headElement; } /** * Advances through the circular list returning the first element for which the predicate evaluates to true. * If there is no such element, returns null leaving the head of the list back in the same place. */ public T advanceToElement (Predicate<T> predicate) { Node<T> start = head; if (head == null) return null; do { T currElement = advance(); if (predicate.test(currElement)) { return currElement; } } while (head != start); return null; } /** * Remove an item from this circular list. */ public boolean remove (T obj) { // handle the zero-length and one-length case if (head == null) return false; if (head.prev == head) { if (head.element.equals(obj)) { head = null; return true; } else return false; } Node<T> start = head; Node<T> current = head; do { if (current.element.equals(obj)) { // remove from rotation current.prev.next = current.next; current.next.prev = current.prev; if (current == head) head = current.next; return true; } current = current.next; } while (current != start); return false; } }