Sometimes there is a need for such a scenario, and a circular linked list is required to do some repetitive work. For example, when we design a timed task, we need to read the timed task forward one per second, then we need a circular linked list. To do such a data structure, and java does not provide such a data structure, I also encountered such a problem during project development, we need to manage timed tasks and use a trigger to trigger these tasks.
Interface definition
package com.lee.berries.common.list; /** * Circular linked list interface definition * @author Liuxianwei * */ public interface CircularLinkedList<E> { /** * Insert an element into the linked list, by default at the end * @param item */ void add(E item); /** * Insert an element at the specified position in the linked list * @param index * @param item */ void add(int index, E item); /** * Insert an element into the linked list, by default at the end * @param item */ void addLast(E item); /** * Insert an element to the head of the linked list * @param item */ void addFirst(E item); /** * Delete the element at the current position of the linked list pointer * @return */ E remove(); /** * delete the item element in the linked list * @param item */ void remove(E item); /** * Delete the element at index position in the linked list * @param index * @return */ E remove(int index); /** * delete the head element of the linked list * @return */ E removeFirst(); /** * delete the element at the end of the linked list * @return */ E removeLast(); /** * Move the current position pointer of the linked list to the next position */ void next(); /** * Return the current position of the linked list * @return */ int currentIndex(); /** * Returns the element at the current position of the linked list * @return */ E current(); /** * Return the head element of the linked list * @return */ E first(); /** * Return the last element of the linked list * @return */ E last(); /** * Get the element at the index position of the linked list * @param index * @return */ E get(int index); /** * Clear the linked list */ void clear(); /** * Return the length of the linked list * @return */ int size(); /** * Whether the current pointer is in the head * @return */ boolean isFirst(); /** * Whether the current pointer is at the end * @return */ boolean isLast(); /** * Determine if the linked list is empty * @return */ boolean isEmpty(); }
Next is the implementation class, I made a thread-safe implementation. Because it needs to be used in a multi-threaded environment. Reentrant locks are used here to ensure thread safety
package com.lee.berries.common.list; import java.util.Collection; import java.util.concurrent.locks.ReentrantLock; /** * Implement a thread-safe circular linked list * @author Liuxianwei * * @param <E> */ public class ConcurrentCircularLinkedList<E> implements CircularLinkedList<E> { static class Node<E>{ E item; Node<E> next; Node(E item){ this.item = item; } } final ReentrantLock lock = new ReentrantLock(); private Node<E> first; private Node<E> last; private Node<E> current; private int currentIndex; private int count = 0; private int capacity; public ConcurrentCircularLinkedList(){ this(Integer.MAX_VALUE); } public ConcurrentCircularLinkedList(int capacity){ this.capacity = capacity; current = first = last = new Node<E>(null); currentIndex = -1; } public ConcurrentCircularLinkedList(Collection<? extends E> c){ this(Integer.MAX_VALUE); for(E item:c){ addLast(item); } } @Override public void add(E item) { addLast(item); } @Override public void add(int index, E item) { lock.lock(); if(index < 0 || index > size()){ throw new ArrayIndexOutOfBoundsException(); } if(count >= capacity){ throw new IllegalArgumentException(); } try{ Node<E> node = new Node<E>(item); /** * When the linked list is null, first, last, and current all point to the first element */ if(this.isEmpty()){ first = node; last = node; current = first; last.next = first; currentIndex = 0; } else{ /** * When the head is inserted */ if(index == 0){ node.next = first; first = node; last.next = node; } /** * Tail insertion */ else if(index == size()){ last.next = node; node.next = first; last = node; } else{ Node<E> n = this.first; for(int i = 0; i < index; i++){ n = n.next; } node.next = n.next; n.next = node; } if(index <= this.currentIndex){ this.currentIndex ++; } } count++; } finally{ lock.unlock(); } } @Override public void addLast(E item) { if(count == 0){ add(0, item); } else{ add(count, item); } } @Override public void addFirst(E item) { add(0, item); } private Node<E> getNode(int index){ if(index < 0 || index > size()){ throw new ArrayIndexOutOfBoundsException(); } Node<E> node = first; for(int i = 0; i < index; i++){ node = node.next; } return node; } @Override public E remove() { return remove(currentIndex); } @Override public void remove(E item) { lock.lock(); try{ Node<E> n = this.first; for(int i = 0; i < size(); i++){ if(n.item.equals(item)){ remove(i); break; } } } finally{ lock.unlock(); } } @Override public E remove(int index) { E item = null; lock.lock(); try{ if(index < 0 || index > size()){ throw new ArrayIndexOutOfBoundsException(); } if(count == 0){ throw new IllegalArgumentException(); } /** * There is only one element left in the linked list */ if(first.next == first){ current = first = last = new Node<E>(null); currentIndex = -1; } else{ /** * remove header */ if(index == 0){ item = first.item; if(current == first){ current = first.next; } Node<E> node = first; first = first.next; last.next = first; node.next = null; } /** * delete tail */ else if(index == (size() - 1)){ item = last.item; Node<E> pre = getNode(index - 1); if(current == last){ current = pre; currentIndex--; } pre.next = first; last.next = null; last = pre; } else{ Node<E> pre = getNode(index - 1); Node<E> node = pre.next; item = node.item; if(node == current){ current = node.next; } pre.next = node.next; node.next = null; if(index < currentIndex){ currentIndex --; } } } count --; } finally{ lock.unlock(); } return item; } @Override public E removeFirst() { return remove(0); } @Override public E removeLast() { return remove(size()-1); } @Override public void next() { lock.lock(); try{ current = current.next; currentIndex++; if(current == first){ currentIndex = 0; } } finally{ lock.unlock(); } } @Override public int currentIndex() { return this.currentIndex; } @Override public E current() { return get(currentIndex); } @Override public E first() { return first.item; } @Override public E last() { return last.item; } @Override public E get(int index){ return null; } @Override public void clear() { } @Override public int size() { return count; } @Override public boolean isEmpty() { return count == 0; } @Override public boolean isFirst() { return this.currentIndex == 0; } @Override public boolean isLast() { return this.currentIndex == (size() - 1); } @Override public String toString() { if(isEmpty()){ return "[]"; } StringBuffer buffer = new StringBuffer(); buffer.append("["); Node<E> node = first; while(true){ buffer.append(node.item); buffer.append(", "); node = node.next; if(node.next == first){ if(node != first){ buffer.append(node.item); } break; } } buffer.append("]"); return buffer.toString(); } }
Then do a simple algorithm example to verify the usability of the function,
100 people form a circle, start counting from 1, exit when shouting 3 people, repeat. until the last person left.
import org.junit.Test; import com.lee.berries.common.list.CircularLinkedList; import com.lee.berries.common.list.ConcurrentCircularLinkedList; public class CircularLinkedListTest { @Test public void Test(){ CircularLinkedList<String> list = new ConcurrentCircularLinkedList<String>(); for(int i = 1; i < 101; i++){ list.add("" + i); } int count = 1; while(list.size() > 1){ list.next(); count++; if(count % 3 == 0){ System.out.println(list.remove() + "退出!"); count++; } } System.out.println(list.toString()); } }
See the code attached or go to my github to download https://git.oschina.net/liuxianwei/berries/tree/master/berries-common/src/main/java/com/lee/berries/common/list