Yatopia/patches/server/0021-Add-GlueList.patch
budgidiere c09ee99f7e
Hydrinity optimizations & Lithium generation patches & more
Closes #257 

Ports 2 patches from Purpur: Infinity-bow-settings & Allow-infinite-and-mending-enchantments-together
Added an option for infinity with no arrows too.

Option for custom locale has come! You can put a locale.json file in your server folder to change it. 

We've got the finest patches from Hydrinity ( Mykyta approved & allowed ) too.

We have some amazing new options in yatopia.yml, we're gonna have documentation for them soon so stay tuned!
Last but not least, chunk generation patches. We've tested them extensively so no weirdness happens.

Thanks for using Yatopia as your production server software.

Co-authored-by: Ivan Pekov <ivan@mrivanplays.com>
2020-10-27 21:15:13 +02:00

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