mirror of
https://github.com/YatopiaMC/Yatopia.git
synced 2024-12-03 16:13:21 +01:00
1066 lines
27 KiB
Diff
1066 lines
27 KiB
Diff
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||
|
From: Mykyta Komarn <nkomarn@hotmail.com>
|
||
|
Date: Wed, 30 Sep 2020 18:03:27 -0700
|
||
|
Subject: [PATCH] Add GlueList
|
||
|
|
||
|
This is the fastest List implementation I could find out there.
|
||
|
|
||
|
diff --git a/src/main/java/net/yatopia/server/list/GlueList.java b/src/main/java/net/yatopia/server/list/GlueList.java
|
||
|
new file mode 100644
|
||
|
index 0000000000000000000000000000000000000000..aa3064985f9d9b29660fec40c5532b02a898aa04
|
||
|
--- /dev/null
|
||
|
+++ b/src/main/java/net/yatopia/server/list/GlueList.java
|
||
|
@@ -0,0 +1,1051 @@
|
||
|
+package net.yatopia.server.list;
|
||
|
+
|
||
|
+/**
|
||
|
+ * Copyright 2015 Ertuğrul Çetin
|
||
|
+ * <p/>
|
||
|
+ * Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
+ * you may not use this file except in compliance with the License.
|
||
|
+ * You may obtain a copy of the License at
|
||
|
+ * <p/>
|
||
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
||
|
+ * <p/>
|
||
|
+ * Unless required by applicable law or agreed to in writing, software
|
||
|
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
+ * See the License for the specific language governing permissions and
|
||
|
+ * limitations under the License.
|
||
|
+ */
|
||
|
+
|
||
|
+import java.io.IOException;
|
||
|
+import java.io.ObjectInputStream;
|
||
|
+import java.io.ObjectOutputStream;
|
||
|
+import java.io.Serializable;
|
||
|
+import java.util.AbstractList;
|
||
|
+import java.util.ArrayList;
|
||
|
+import java.util.Arrays;
|
||
|
+import java.util.Collection;
|
||
|
+import java.util.ConcurrentModificationException;
|
||
|
+import java.util.Iterator;
|
||
|
+import java.util.LinkedList;
|
||
|
+import java.util.List;
|
||
|
+import java.util.ListIterator;
|
||
|
+import java.util.NoSuchElementException;
|
||
|
+import java.util.Objects;
|
||
|
+
|
||
|
+import static java.lang.Math.abs;
|
||
|
+import static java.lang.Math.max;
|
||
|
+import static java.lang.Math.min;
|
||
|
+
|
||
|
+
|
||
|
+/**
|
||
|
+ * GlueList is a brand new List implementation which is way faster than ArrayList and LinkedList.
|
||
|
+ * This implementation inspired from ArrayList and LinkedList working mechanism.
|
||
|
+ * <br>
|
||
|
+ * Nodes holding data in arrays, in the beginning the world just like ArrayList ,inserts data into array one by one when there is no space for insertion to array
|
||
|
+ * new Node will be created and linked with the last Node.
|
||
|
+ * <br>
|
||
|
+ * The array which belongs to newly created node has half of the size of list , just like ArrayList.
|
||
|
+ * In ArrayList when there is no space for it it creates new array with double of old size and inserts old data into new one.
|
||
|
+ * Unlike ArrayList GlueList does it dynamically way with creating new node so old data does NOT have to be moved to another array.
|
||
|
+ * You can think that GlueList is dynamic version of ArrayList.
|
||
|
+ * <br>
|
||
|
+ * Adding and removing operations much faster than ArrayList and LinkedList.
|
||
|
+ * Searching operations nearly same with ArrayList and way better than LinkedList.
|
||
|
+ * <p/>
|
||
|
+ * Best Case<br>
|
||
|
+ * Add O(1)<br>
|
||
|
+ * Remove O(1)<br>
|
||
|
+ * Search O(1)<br>
|
||
|
+ * Access O(1)
|
||
|
+ * <br><br>
|
||
|
+ * "m" number of created nodes.<br>
|
||
|
+ * "n" size of node array.<br>
|
||
|
+ * If you insert 10_000_000 record into List there will be just 36 nodes.<br><br>
|
||
|
+ * Worst Case<br>
|
||
|
+ * Add O(n*m)<br>
|
||
|
+ * Remove O(n*m)<br>
|
||
|
+ * Search O(m)<br>
|
||
|
+ * Access O(m)
|
||
|
+ * <p/>
|
||
|
+ * version v1.0
|
||
|
+ * <p/>
|
||
|
+ * Date: 03.11.2015
|
||
|
+ * <p/>
|
||
|
+ *
|
||
|
+ * @author Ertuğrul Çetin ~ ertu.ctn@gmail.com
|
||
|
+ * @see Collection
|
||
|
+ * @see List
|
||
|
+ * @see LinkedList
|
||
|
+ * @see ArrayList
|
||
|
+ * @param <T> the type of elements held in this collection
|
||
|
+ */
|
||
|
+public class GlueList<T> extends AbstractList<T> implements List<T>, Cloneable, Serializable {
|
||
|
+
|
||
|
+ transient Node<T> first;
|
||
|
+ transient Node<T> last;
|
||
|
+
|
||
|
+ int size;
|
||
|
+
|
||
|
+ int initialCapacity;
|
||
|
+
|
||
|
+ private static final int DEFAULT_CAPACITY = 10;
|
||
|
+
|
||
|
+ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
|
||
|
+
|
||
|
+ public GlueList() {
|
||
|
+
|
||
|
+ Node<T> initNode = new Node<>(null, null, 0, DEFAULT_CAPACITY);
|
||
|
+
|
||
|
+ first = initNode;
|
||
|
+ last = initNode;
|
||
|
+ }
|
||
|
+
|
||
|
+ public GlueList(int initialCapacity) {
|
||
|
+
|
||
|
+ this.initialCapacity = (initialCapacity > MAX_ARRAY_SIZE) ? MAX_ARRAY_SIZE : initialCapacity;
|
||
|
+
|
||
|
+ Node<T> initNode = new Node<>(null, null, 0, initialCapacity);
|
||
|
+
|
||
|
+ first = initNode;
|
||
|
+ last = initNode;
|
||
|
+ }
|
||
|
+
|
||
|
+ public GlueList(Collection<? extends T> c) {
|
||
|
+
|
||
|
+ Objects.requireNonNull(c);
|
||
|
+
|
||
|
+ Object[] arr = c.toArray();
|
||
|
+
|
||
|
+ int len = arr.length;
|
||
|
+
|
||
|
+ if (len != 0) {
|
||
|
+
|
||
|
+ Node<T> initNode = new Node<>(null, null, 0, len);
|
||
|
+
|
||
|
+ first = initNode;
|
||
|
+ last = initNode;
|
||
|
+
|
||
|
+ System.arraycopy(arr, 0, last.elementData, 0, len);
|
||
|
+
|
||
|
+ last.elementDataPointer += len;
|
||
|
+ } else {
|
||
|
+
|
||
|
+ Node<T> initNode = new Node<>(null, null, 0, DEFAULT_CAPACITY);
|
||
|
+
|
||
|
+ first = initNode;
|
||
|
+ last = initNode;
|
||
|
+ }
|
||
|
+
|
||
|
+ modCount++;
|
||
|
+ size += len;
|
||
|
+ }
|
||
|
+
|
||
|
+ @Override
|
||
|
+ public boolean add(T element) {
|
||
|
+
|
||
|
+ Node<T> l = last;
|
||
|
+
|
||
|
+ if (l.isAddable()) {
|
||
|
+ l.add(element);
|
||
|
+ } else {
|
||
|
+ Node<T> newNode = new Node<>(l, null, size);
|
||
|
+ newNode.add(element);
|
||
|
+ last = newNode;
|
||
|
+ l.next = last;
|
||
|
+ }
|
||
|
+
|
||
|
+ modCount++;
|
||
|
+ size++;
|
||
|
+
|
||
|
+ return true;
|
||
|
+ }
|
||
|
+
|
||
|
+ @SuppressWarnings("unchecked")
|
||
|
+ @Override
|
||
|
+ public void add(int index, T element) {
|
||
|
+
|
||
|
+ rangeCheckForAdd(index);
|
||
|
+
|
||
|
+ Node<T> node = getNodeForAdd(index);
|
||
|
+
|
||
|
+ if (node == null) {
|
||
|
+
|
||
|
+ Node<T> l = last;
|
||
|
+
|
||
|
+ Node<T> newNode = new Node<>(l, null, size);
|
||
|
+
|
||
|
+ last = newNode;
|
||
|
+
|
||
|
+ l.next = last;
|
||
|
+
|
||
|
+ node = newNode;
|
||
|
+ }
|
||
|
+
|
||
|
+ //if it is last and has extra space for element...
|
||
|
+ if (node == last && node.elementData.length - node.elementDataPointer > 0) {
|
||
|
+
|
||
|
+ int nodeArrIndex = index - node.startingIndex;
|
||
|
+
|
||
|
+ System.arraycopy(node.elementData, nodeArrIndex, node.elementData, nodeArrIndex + 1, node.elementDataPointer - nodeArrIndex);
|
||
|
+
|
||
|
+ node.elementData[nodeArrIndex] = element;
|
||
|
+
|
||
|
+ if (nodeArrIndex > 0) {
|
||
|
+ System.arraycopy(node.elementData, 0, node.elementData, 0, nodeArrIndex);
|
||
|
+ }
|
||
|
+
|
||
|
+ node.elementDataPointer++;
|
||
|
+ } else {
|
||
|
+
|
||
|
+ int newLen = node.elementData.length + 1;
|
||
|
+ T[] newElementData = (T[]) new Object[newLen];
|
||
|
+
|
||
|
+ int nodeArrIndex = index - node.startingIndex;
|
||
|
+
|
||
|
+ System.arraycopy(node.elementData, nodeArrIndex, newElementData, nodeArrIndex + 1, node.elementDataPointer - nodeArrIndex);
|
||
|
+
|
||
|
+ newElementData[nodeArrIndex] = element;
|
||
|
+
|
||
|
+ if (nodeArrIndex > 0) {
|
||
|
+ System.arraycopy(node.elementData, 0, newElementData, 0, nodeArrIndex);
|
||
|
+ }
|
||
|
+
|
||
|
+ node.elementData = newElementData;
|
||
|
+ node.endingIndex++;
|
||
|
+ node.elementDataPointer++;
|
||
|
+ }
|
||
|
+
|
||
|
+ updateNodesAfterAdd(node);
|
||
|
+
|
||
|
+ modCount++;
|
||
|
+ size++;
|
||
|
+ }
|
||
|
+
|
||
|
+ private void rangeCheckForAdd(int index) {
|
||
|
+
|
||
|
+ if (index > size || index < 0) {
|
||
|
+ throw new ArrayIndexOutOfBoundsException(index);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ private void updateNodesAfterAdd(Node<T> nodeFrom) {
|
||
|
+
|
||
|
+ for (Node<T> node = nodeFrom.next; node != null; node = node.next) {
|
||
|
+
|
||
|
+ node.startingIndex++;
|
||
|
+ node.endingIndex++;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ @SuppressWarnings("unchecked")
|
||
|
+ @Override
|
||
|
+ public boolean addAll(Collection<? extends T> c) {
|
||
|
+
|
||
|
+ Objects.requireNonNull(c);
|
||
|
+
|
||
|
+ Object[] collection = c.toArray();
|
||
|
+
|
||
|
+ int len = collection.length;
|
||
|
+
|
||
|
+ if (len == 0) {
|
||
|
+ return false;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (size == 0) {
|
||
|
+
|
||
|
+ if (initialCapacity >= len) {
|
||
|
+ System.arraycopy(collection, 0, last.elementData, 0, len);
|
||
|
+ } else {
|
||
|
+ last.elementData = Arrays.copyOf((T[]) collection, len);
|
||
|
+ last.endingIndex = len - 1;
|
||
|
+ }
|
||
|
+
|
||
|
+ last.elementDataPointer += len;
|
||
|
+
|
||
|
+ modCount++;
|
||
|
+ size += len;
|
||
|
+
|
||
|
+ return true;
|
||
|
+ }
|
||
|
+
|
||
|
+ int elementDataLen = last.elementData.length;
|
||
|
+ int elementSize = last.elementDataPointer;
|
||
|
+
|
||
|
+ int remainedStorage = elementDataLen - elementSize;
|
||
|
+
|
||
|
+ if (remainedStorage == 0) {
|
||
|
+
|
||
|
+ Node<T> l = last;
|
||
|
+
|
||
|
+ int newLen = (size >>> 1);
|
||
|
+ int initialLen = (len > newLen) ? len : newLen;
|
||
|
+
|
||
|
+ Node<T> newNode = new Node<>(l, null, size, initialLen);
|
||
|
+
|
||
|
+ System.arraycopy(collection, 0, newNode.elementData, 0, len);
|
||
|
+
|
||
|
+ newNode.elementDataPointer += len;
|
||
|
+
|
||
|
+ last = newNode;
|
||
|
+ l.next = last;
|
||
|
+
|
||
|
+ modCount++;
|
||
|
+ size += len;
|
||
|
+
|
||
|
+ return true;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (len <= remainedStorage) {
|
||
|
+
|
||
|
+ System.arraycopy(collection, 0, last.elementData, elementSize, len);
|
||
|
+
|
||
|
+ last.elementDataPointer += len;
|
||
|
+
|
||
|
+ modCount++;
|
||
|
+ size += len;
|
||
|
+
|
||
|
+ return true;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (len > remainedStorage) {
|
||
|
+
|
||
|
+ System.arraycopy(collection, 0, last.elementData, elementSize, remainedStorage);
|
||
|
+
|
||
|
+ last.elementDataPointer += remainedStorage;
|
||
|
+ size += remainedStorage;
|
||
|
+
|
||
|
+ int newLen = (size >>> 1);
|
||
|
+ int remainedDataLen = len - remainedStorage;
|
||
|
+
|
||
|
+ int initialLen = (newLen > remainedDataLen) ? newLen : remainedDataLen;
|
||
|
+
|
||
|
+ Node<T> l = last;
|
||
|
+
|
||
|
+ Node<T> newNode = new Node<>(l, null, size, initialLen);
|
||
|
+
|
||
|
+ System.arraycopy(collection, remainedStorage, newNode.elementData, 0, remainedDataLen);
|
||
|
+
|
||
|
+ newNode.elementDataPointer += remainedDataLen;
|
||
|
+
|
||
|
+ last = newNode;
|
||
|
+ l.next = last;
|
||
|
+
|
||
|
+ modCount++;
|
||
|
+ size += remainedDataLen;
|
||
|
+
|
||
|
+ return true;
|
||
|
+ }
|
||
|
+
|
||
|
+ return false;
|
||
|
+ }
|
||
|
+
|
||
|
+ @Override
|
||
|
+ public T set(int index, T element) {
|
||
|
+
|
||
|
+ rangeCheck(index);
|
||
|
+
|
||
|
+ Node<T> node = getNode(index);
|
||
|
+
|
||
|
+ int nodeArrIndex = index - node.startingIndex;
|
||
|
+
|
||
|
+ T oldValue = node.elementData[nodeArrIndex];
|
||
|
+
|
||
|
+ node.elementData[nodeArrIndex] = element;
|
||
|
+
|
||
|
+ return oldValue;
|
||
|
+ }
|
||
|
+
|
||
|
+ @Override
|
||
|
+ public T get(int index) {
|
||
|
+
|
||
|
+ rangeCheck(index);
|
||
|
+
|
||
|
+ Node<T> node = getNode(index);
|
||
|
+
|
||
|
+ return node.elementData[index - node.startingIndex];
|
||
|
+ }
|
||
|
+
|
||
|
+ @Override
|
||
|
+ public int indexOf(Object o) {
|
||
|
+
|
||
|
+ int index = 0;
|
||
|
+
|
||
|
+ if (o == null) {
|
||
|
+
|
||
|
+ for (Node<T> node = first; node != null; node = node.next) {
|
||
|
+ for (int i = 0; i < node.elementDataPointer; i++) {
|
||
|
+ if (node.elementData[i] == null) {
|
||
|
+ return index;
|
||
|
+ }
|
||
|
+ index++;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ } else {
|
||
|
+
|
||
|
+ for (Node<T> node = first; node != null; node = node.next) {
|
||
|
+ for (int i = 0; i < node.elementDataPointer; i++) {
|
||
|
+ if (o.equals(node.elementData[i])) {
|
||
|
+ return index;
|
||
|
+ }
|
||
|
+ index++;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+
|
||
|
+ @Override
|
||
|
+ public int lastIndexOf(Object o) {
|
||
|
+
|
||
|
+ int index = size - 1;
|
||
|
+
|
||
|
+ if (o == null) {
|
||
|
+ for (Node<T> node = last; node != null; node = node.pre) {
|
||
|
+ for (int i = node.elementDataPointer - 1; i >= 0; i--) {
|
||
|
+ if (node.elementData[i] == null) {
|
||
|
+ return index;
|
||
|
+ }
|
||
|
+ index--;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ } else {
|
||
|
+
|
||
|
+ for (Node<T> node = last; node != null; node = node.pre) {
|
||
|
+ for (int i = node.elementDataPointer - 1; i >= 0; i--) {
|
||
|
+ if (o.equals(node.elementData[i])) {
|
||
|
+ return index;
|
||
|
+ }
|
||
|
+ index--;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return -1;
|
||
|
+ }
|
||
|
+
|
||
|
+ @Override
|
||
|
+ public boolean contains(Object o) {
|
||
|
+ return indexOf(o) != -1;
|
||
|
+ }
|
||
|
+
|
||
|
+ @SuppressWarnings("unchecked")
|
||
|
+ @Override
|
||
|
+ public T remove(int index) {
|
||
|
+
|
||
|
+ rangeCheck(index);
|
||
|
+
|
||
|
+ Node<T> node;
|
||
|
+
|
||
|
+ if (size == 2 && first != last) {
|
||
|
+
|
||
|
+ Node<T> newNode = new Node<>(null, null, 0, 2);
|
||
|
+ newNode.add(first.elementData[0]);
|
||
|
+ newNode.add(last.elementData[0]);
|
||
|
+
|
||
|
+ node = first = last = newNode;
|
||
|
+ } else {
|
||
|
+ node = getNode(index);
|
||
|
+ }
|
||
|
+
|
||
|
+ T[] elementData = node.elementData;
|
||
|
+
|
||
|
+ int elementSize = node.elementDataPointer;
|
||
|
+
|
||
|
+ int nodeArrIndex = index - node.startingIndex;
|
||
|
+
|
||
|
+ T oldValue = elementData[nodeArrIndex];
|
||
|
+
|
||
|
+ int numMoved = elementSize - nodeArrIndex - 1;
|
||
|
+
|
||
|
+ if (numMoved > 0) {
|
||
|
+ System.arraycopy(node.elementData, nodeArrIndex + 1, node.elementData, nodeArrIndex, numMoved);
|
||
|
+ }
|
||
|
+
|
||
|
+ if (first == last || node == last) {
|
||
|
+ node.elementData[elementSize - 1] = null;
|
||
|
+ } else {
|
||
|
+ node.elementData = Arrays.copyOf(node.elementData, elementSize - 1);
|
||
|
+ node.endingIndex = (--node.endingIndex < 0) ? 0 : node.endingIndex;
|
||
|
+ }
|
||
|
+
|
||
|
+ node.elementDataPointer--;
|
||
|
+
|
||
|
+ updateNodesAfterRemove(node);
|
||
|
+
|
||
|
+ if (node.elementDataPointer == 0 && first != last) {
|
||
|
+
|
||
|
+ Node<T> next = node.next;
|
||
|
+ Node<T> prev = node.pre;
|
||
|
+
|
||
|
+ if (prev == null) {
|
||
|
+ first = next;
|
||
|
+ } else {
|
||
|
+ prev.next = next;
|
||
|
+ node.pre = null;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (next == null) {
|
||
|
+ last = prev;
|
||
|
+ } else {
|
||
|
+ next.pre = prev;
|
||
|
+ node.next = null;
|
||
|
+ }
|
||
|
+
|
||
|
+ node.elementData = null;
|
||
|
+ }
|
||
|
+
|
||
|
+ size--;
|
||
|
+ modCount++;
|
||
|
+
|
||
|
+ return oldValue;
|
||
|
+ }
|
||
|
+
|
||
|
+ @Override
|
||
|
+ public boolean removeAll(Collection<?> c) {
|
||
|
+
|
||
|
+ Objects.requireNonNull(c);
|
||
|
+
|
||
|
+ Object[] arr = c.toArray();
|
||
|
+ if (arr.length == 0) {
|
||
|
+ return false;
|
||
|
+ }
|
||
|
+
|
||
|
+ boolean isModified = false;
|
||
|
+
|
||
|
+ for (Object o : arr) {
|
||
|
+ isModified |= remove(o);
|
||
|
+ }
|
||
|
+
|
||
|
+ return isModified;
|
||
|
+ }
|
||
|
+
|
||
|
+ @Override
|
||
|
+ public boolean retainAll(Collection<?> c) {
|
||
|
+
|
||
|
+ Objects.requireNonNull(c);
|
||
|
+
|
||
|
+ Object[] arr = c.toArray();
|
||
|
+ if (arr.length == 0) {
|
||
|
+ return false;
|
||
|
+ }
|
||
|
+
|
||
|
+ boolean isModified = false;
|
||
|
+
|
||
|
+ Object[] elements = toArray();
|
||
|
+
|
||
|
+ for (Object element : elements) {
|
||
|
+
|
||
|
+ if (!c.contains(element)) {
|
||
|
+ isModified |= remove(element);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return isModified;
|
||
|
+ }
|
||
|
+
|
||
|
+ @Override
|
||
|
+ public boolean remove(Object o) {
|
||
|
+
|
||
|
+ int index = indexOf(o);
|
||
|
+
|
||
|
+ if (index != -1) {
|
||
|
+ remove(index);
|
||
|
+ return true;
|
||
|
+ } else {
|
||
|
+ return false;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ private void updateNodesAfterRemove(Node<T> fromNode) {
|
||
|
+
|
||
|
+ for (Node<T> node = fromNode.next; node != null; node = node.next) {
|
||
|
+
|
||
|
+ node.startingIndex = (--node.startingIndex < 0) ? 0 : node.startingIndex;
|
||
|
+ node.endingIndex = (--node.endingIndex < 0) ? 0 : node.endingIndex;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ private Node<T> getNode(int index) {
|
||
|
+
|
||
|
+ int firstStartingIndex = first.startingIndex;
|
||
|
+ int firstEndingIndex = first.endingIndex;
|
||
|
+
|
||
|
+ int firstMinDistance = min(abs(index - firstStartingIndex), abs(index - firstEndingIndex));
|
||
|
+
|
||
|
+ int lastStartingIndex = last.startingIndex;
|
||
|
+ int lastEndingIndex = last.endingIndex;
|
||
|
+
|
||
|
+ int lastMinDistance = min(abs(index - lastStartingIndex), abs(index - lastEndingIndex));
|
||
|
+
|
||
|
+ if (firstMinDistance <= lastMinDistance) {
|
||
|
+
|
||
|
+ Node<T> node = first;
|
||
|
+ do {
|
||
|
+
|
||
|
+ if (node.startingIndex <= index && index <= node.endingIndex) {
|
||
|
+ return node;
|
||
|
+ }
|
||
|
+
|
||
|
+ node = node.next;
|
||
|
+ } while (true);
|
||
|
+ } else {
|
||
|
+
|
||
|
+ Node<T> node = last;
|
||
|
+ do {
|
||
|
+
|
||
|
+ if (node.startingIndex <= index && index <= node.endingIndex) {
|
||
|
+ return node;
|
||
|
+ }
|
||
|
+
|
||
|
+ node = node.pre;
|
||
|
+ } while (true);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ private Node<T> getNodeForAdd(int index) {
|
||
|
+
|
||
|
+ if (index == size && !(last.startingIndex <= index && index <= last.endingIndex)) {
|
||
|
+ return null;
|
||
|
+ }
|
||
|
+
|
||
|
+ return getNode(index);
|
||
|
+ }
|
||
|
+
|
||
|
+ private void rangeCheck(int index) {
|
||
|
+
|
||
|
+ if (index >= size || index < 0) {
|
||
|
+ throw new ArrayIndexOutOfBoundsException(index);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ @Override
|
||
|
+ public void clear() {
|
||
|
+
|
||
|
+ for (Node<T> node = first; node != null; ) {
|
||
|
+
|
||
|
+ Node<T> next = node.next;
|
||
|
+
|
||
|
+ node.next = null;
|
||
|
+ node.pre = null;
|
||
|
+ node.elementData = null;
|
||
|
+
|
||
|
+ node = next;
|
||
|
+ }
|
||
|
+
|
||
|
+ first = last = null;
|
||
|
+
|
||
|
+ int capacity = min(MAX_ARRAY_SIZE, max(size, max(initialCapacity, DEFAULT_CAPACITY)));
|
||
|
+
|
||
|
+ Node<T> initNode = new Node<>(null, null, 0, capacity);
|
||
|
+
|
||
|
+ initialCapacity = capacity;
|
||
|
+
|
||
|
+ first = initNode;
|
||
|
+ last = initNode;
|
||
|
+
|
||
|
+ modCount++;
|
||
|
+ size = 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ public void trimToSize() {
|
||
|
+
|
||
|
+ int pointer = last.elementDataPointer;
|
||
|
+ int arrLen = last.elementData.length;
|
||
|
+
|
||
|
+ if (pointer < arrLen && arrLen > 2) {
|
||
|
+
|
||
|
+ if (pointer < 2) {
|
||
|
+ last.elementData = Arrays.copyOf(last.elementData, 2);
|
||
|
+ last.endingIndex -= arrLen - 2;
|
||
|
+ } else {
|
||
|
+ last.elementData = Arrays.copyOf(last.elementData, pointer);
|
||
|
+ last.endingIndex -= arrLen - pointer;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ @Override
|
||
|
+ public List<T> subList(int fromIndex, int toIndex) {
|
||
|
+ return super.subList(fromIndex, toIndex);
|
||
|
+ }
|
||
|
+
|
||
|
+ @Override
|
||
|
+ public Object[] toArray() {
|
||
|
+
|
||
|
+ Object[] objects = new Object[size];
|
||
|
+
|
||
|
+ int i = 0;
|
||
|
+ for (Node<T> node = first; node != null; node = node.next) {
|
||
|
+
|
||
|
+ int len = node.elementDataPointer;
|
||
|
+
|
||
|
+ if (len > 0) {
|
||
|
+ System.arraycopy(node.elementData, 0, objects, i, len);
|
||
|
+ }
|
||
|
+
|
||
|
+ i += len;
|
||
|
+ }
|
||
|
+
|
||
|
+ return objects;
|
||
|
+ }
|
||
|
+
|
||
|
+ @SuppressWarnings("unchecked")
|
||
|
+ @Override
|
||
|
+ public <T> T[] toArray(T[] a) {
|
||
|
+ return (T[]) Arrays.copyOf(toArray(), size, a.getClass());
|
||
|
+ }
|
||
|
+
|
||
|
+ public boolean isEmpty() {
|
||
|
+ return size == 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ @Override
|
||
|
+ public Iterator<T> iterator() {
|
||
|
+ return new Itr();
|
||
|
+ }
|
||
|
+
|
||
|
+ private class Itr implements Iterator<T> {
|
||
|
+
|
||
|
+ Node<T> node = first;
|
||
|
+
|
||
|
+ int i = 0;//inner-array index
|
||
|
+ int j = 0;//total index -> cursor
|
||
|
+
|
||
|
+ int lastReturn = -1;
|
||
|
+
|
||
|
+ int expectedModCount = modCount;
|
||
|
+ int elementDataPointer = node.elementDataPointer;
|
||
|
+
|
||
|
+ @Override
|
||
|
+ public boolean hasNext() {
|
||
|
+ return j != size;
|
||
|
+ }
|
||
|
+
|
||
|
+ @Override
|
||
|
+ public T next() {
|
||
|
+
|
||
|
+ checkForComodification();
|
||
|
+
|
||
|
+ if (j >= size) {
|
||
|
+ throw new NoSuchElementException();
|
||
|
+ }
|
||
|
+
|
||
|
+ if (j >= last.endingIndex + 1) {
|
||
|
+ throw new ConcurrentModificationException();
|
||
|
+ }
|
||
|
+
|
||
|
+ if (j == 0) {// it's for listIterator.when node becomes null.
|
||
|
+ node = first;
|
||
|
+ elementDataPointer = node.elementDataPointer;
|
||
|
+ i = 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ T val = node.elementData[i++];
|
||
|
+
|
||
|
+ if (i >= elementDataPointer) {
|
||
|
+ node = node.next;
|
||
|
+ i = 0;
|
||
|
+ elementDataPointer = (node != null) ? node.elementDataPointer : 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ lastReturn = j++;
|
||
|
+
|
||
|
+ return val;
|
||
|
+ }
|
||
|
+
|
||
|
+ @Override
|
||
|
+ public void remove() {
|
||
|
+
|
||
|
+ if (lastReturn < 0) {
|
||
|
+ throw new IllegalStateException();
|
||
|
+ }
|
||
|
+
|
||
|
+ checkForComodification();
|
||
|
+
|
||
|
+ try {
|
||
|
+ GlueList.this.remove(lastReturn);
|
||
|
+
|
||
|
+ j = lastReturn;
|
||
|
+
|
||
|
+ lastReturn = -1;
|
||
|
+
|
||
|
+ i = (--i < 0) ? 0 : i;
|
||
|
+
|
||
|
+ elementDataPointer = (node != null) ? node.elementDataPointer : 0;
|
||
|
+
|
||
|
+ expectedModCount = modCount;
|
||
|
+ } catch (IndexOutOfBoundsException e) {
|
||
|
+ throw new ConcurrentModificationException();
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ void checkForComodification() {
|
||
|
+ if (modCount != expectedModCount) {
|
||
|
+ throw new ConcurrentModificationException();
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ @Override
|
||
|
+ public ListIterator<T> listIterator(int index) {
|
||
|
+
|
||
|
+ checkPositionIndex(index);
|
||
|
+
|
||
|
+ return new ListItr(index);
|
||
|
+ }
|
||
|
+
|
||
|
+ private void checkPositionIndex(int index) {
|
||
|
+
|
||
|
+ if (!(index >= 0 && index <= size)) {
|
||
|
+ throw new ArrayIndexOutOfBoundsException(index);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ @Override
|
||
|
+ public ListIterator<T> listIterator() {
|
||
|
+ return new ListItr(0);
|
||
|
+ }
|
||
|
+
|
||
|
+ private class ListItr extends Itr implements ListIterator<T> {
|
||
|
+
|
||
|
+ public ListItr(int index) {
|
||
|
+ node = (index == size) ? last : getNode(index);
|
||
|
+ j = index;
|
||
|
+ i = index - node.startingIndex;
|
||
|
+ elementDataPointer = node.elementDataPointer;
|
||
|
+ }
|
||
|
+
|
||
|
+ @Override
|
||
|
+ public boolean hasPrevious() {
|
||
|
+ return j != 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ @Override
|
||
|
+ public T previous() {
|
||
|
+
|
||
|
+ checkForComodification();
|
||
|
+
|
||
|
+ int temp = j - 1;
|
||
|
+
|
||
|
+ if (temp < 0) {
|
||
|
+ throw new NoSuchElementException();
|
||
|
+ }
|
||
|
+
|
||
|
+ if (temp >= last.endingIndex + 1) {
|
||
|
+ throw new ConcurrentModificationException();
|
||
|
+ }
|
||
|
+
|
||
|
+ if (j == size) {
|
||
|
+
|
||
|
+ node = last;
|
||
|
+
|
||
|
+ elementDataPointer = node.elementDataPointer;
|
||
|
+
|
||
|
+ i = elementDataPointer;
|
||
|
+ }
|
||
|
+
|
||
|
+ int index = j - node.startingIndex;
|
||
|
+ if (index == 0) {
|
||
|
+
|
||
|
+ node = node.pre;
|
||
|
+
|
||
|
+ elementDataPointer = node.elementDataPointer;
|
||
|
+
|
||
|
+ i = elementDataPointer;
|
||
|
+ }
|
||
|
+
|
||
|
+ T val = node.elementData[--i];
|
||
|
+
|
||
|
+ if (i < 0) {
|
||
|
+ node = node.pre;
|
||
|
+ i = (node != null) ? node.elementDataPointer : 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ j = temp;
|
||
|
+
|
||
|
+ lastReturn = j;
|
||
|
+
|
||
|
+ return val;
|
||
|
+ }
|
||
|
+
|
||
|
+ @Override
|
||
|
+ public int nextIndex() {
|
||
|
+ return j;
|
||
|
+ }
|
||
|
+
|
||
|
+ @Override
|
||
|
+ public int previousIndex() {
|
||
|
+ return j - 1;
|
||
|
+ }
|
||
|
+
|
||
|
+ @Override
|
||
|
+ public void set(T t) {
|
||
|
+
|
||
|
+ if (lastReturn < 0) {
|
||
|
+ throw new IllegalStateException();
|
||
|
+ }
|
||
|
+
|
||
|
+ checkForComodification();
|
||
|
+
|
||
|
+ try {
|
||
|
+ GlueList.this.set(lastReturn, t);
|
||
|
+ } catch (IndexOutOfBoundsException e) {
|
||
|
+ throw new ConcurrentModificationException();
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ @Override
|
||
|
+ public void add(T t) {
|
||
|
+
|
||
|
+ checkForComodification();
|
||
|
+
|
||
|
+ try {
|
||
|
+ int temp = j;
|
||
|
+
|
||
|
+ GlueList.this.add(temp, t);
|
||
|
+
|
||
|
+ j = temp + 1;
|
||
|
+
|
||
|
+ lastReturn = -1;
|
||
|
+
|
||
|
+ i++;
|
||
|
+ elementDataPointer = (node != null) ? node.elementDataPointer : 0;
|
||
|
+
|
||
|
+ expectedModCount = modCount;
|
||
|
+ } catch (IndexOutOfBoundsException e) {
|
||
|
+ throw new ConcurrentModificationException();
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ @Override
|
||
|
+ public int size() {
|
||
|
+ return size;
|
||
|
+ }
|
||
|
+
|
||
|
+ @SuppressWarnings("unchecked")
|
||
|
+ @Override
|
||
|
+ public Object clone() {
|
||
|
+
|
||
|
+ try {
|
||
|
+ GlueList<T> clone = (GlueList<T>) super.clone();
|
||
|
+
|
||
|
+ clone.first = clone.last = null;
|
||
|
+
|
||
|
+ int capacity = min(MAX_ARRAY_SIZE, max(clone.size, max(clone.initialCapacity, DEFAULT_CAPACITY)));
|
||
|
+
|
||
|
+ Node<T> initNode = new Node<>(null, null, 0, capacity);
|
||
|
+
|
||
|
+ clone.initialCapacity = capacity;
|
||
|
+
|
||
|
+ clone.first = clone.last = initNode;
|
||
|
+
|
||
|
+ clone.modCount = 0;
|
||
|
+ clone.size = 0;
|
||
|
+
|
||
|
+ for (Node<T> node = first; node != null; node = node.next) {
|
||
|
+
|
||
|
+ for (int i = 0; i < node.elementDataPointer; i++) {
|
||
|
+ clone.add(node.elementData[i]);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return clone;
|
||
|
+ } catch (CloneNotSupportedException e) {
|
||
|
+ throw new InternalError();
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ private void writeObject(ObjectOutputStream s) throws IOException {
|
||
|
+
|
||
|
+ int expectedModCount = modCount;
|
||
|
+
|
||
|
+ s.defaultWriteObject();
|
||
|
+
|
||
|
+ s.writeInt(size);
|
||
|
+
|
||
|
+ for (Node<T> node = first; node != null; node = node.next) {
|
||
|
+ for (int i = 0; i < node.elementDataPointer; i++) {
|
||
|
+ s.writeObject(node.elementData[i]);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ if (modCount != expectedModCount) {
|
||
|
+ throw new ConcurrentModificationException();
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+
|
||
|
+ @SuppressWarnings("unchecked")
|
||
|
+ private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
|
||
|
+
|
||
|
+ clear();
|
||
|
+
|
||
|
+ s.defaultReadObject();
|
||
|
+
|
||
|
+ int size = s.readInt();
|
||
|
+
|
||
|
+ for (int i = 0; i < size; i++) {
|
||
|
+ last.add((T) s.readObject());
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ static class Node<T> {
|
||
|
+
|
||
|
+ Node<T> pre;
|
||
|
+ Node<T> next;
|
||
|
+
|
||
|
+ int listSize;
|
||
|
+
|
||
|
+ int startingIndex;
|
||
|
+ int endingIndex;
|
||
|
+
|
||
|
+ T[] elementData;
|
||
|
+ int elementDataPointer;
|
||
|
+
|
||
|
+ @SuppressWarnings("unchecked")
|
||
|
+ Node(Node<T> pre, Node<T> next, int listSize) {
|
||
|
+ this.pre = pre;
|
||
|
+ this.next = next;
|
||
|
+ this.listSize = listSize;
|
||
|
+ this.elementData = (T[]) new Object[listSize >>> 1];
|
||
|
+ this.startingIndex = listSize;
|
||
|
+ this.endingIndex = listSize + elementData.length - 1;
|
||
|
+ }
|
||
|
+
|
||
|
+ Node(Node<T> pre, Node<T> next, int listSize, int initialCapacity) {
|
||
|
+ this.pre = pre;
|
||
|
+ this.next = next;
|
||
|
+ this.listSize = listSize;
|
||
|
+ this.elementData = createElementData(initialCapacity);
|
||
|
+ this.startingIndex = listSize;
|
||
|
+ this.endingIndex = listSize + elementData.length - 1;
|
||
|
+ }
|
||
|
+
|
||
|
+ @SuppressWarnings("unchecked")
|
||
|
+ T[] createElementData(int capacity) {
|
||
|
+
|
||
|
+ if (capacity == 0 || capacity == 1) {
|
||
|
+ return (T[]) new Object[DEFAULT_CAPACITY];
|
||
|
+ } else if (capacity > 1) {
|
||
|
+ return (T[]) new Object[capacity];
|
||
|
+ } else {
|
||
|
+ throw new IllegalArgumentException("Illegal Capacity: " + capacity);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ boolean isAddable() {
|
||
|
+ return elementDataPointer < elementData.length;
|
||
|
+ }
|
||
|
+
|
||
|
+ void add(T element) {
|
||
|
+ elementData[elementDataPointer++] = element;
|
||
|
+ }
|
||
|
+
|
||
|
+ @Override
|
||
|
+ public String toString() {
|
||
|
+ return String.format("[sIndex: %d - eIndex: %d | elementDataPointer: %d | elementDataLength: %d]", startingIndex, endingIndex, elementDataPointer, elementData.length);
|
||
|
+ }
|
||
|
+ }
|
||
|
+}
|
||
|
\ No newline at end of file
|