
com.strobel.collections.SmartList Maven / Gradle / Ivy
/*
* SmartList.java
*
* Copyright (c) 2013 Mike Strobel
*
* This source code is subject to terms and conditions of the Apache License, Version 2.0.
* A copy of the license can be found in the License.html file at the root of this distribution.
* By using this source code in any fashion, you are agreeing to be bound by the terms of the
* Apache License, Version 2.0.
*
* You must not remove this notice, or any other, from this software.
*/
package com.strobel.collections;
import com.strobel.annotations.NotNull;
import java.util.AbstractList;
import java.util.Arrays;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
/**
* A {@link List} that is optimised for the sizes of 0 and 1, in which cases no array is allocated.
*/
@SuppressWarnings({ "unchecked" })
public class SmartList extends AbstractList {
//
// Backing data for the list contents. May be:
// 1. `null` if _size == 0
// 2. `E` if _size == 1
// 3. `Object[]` if _size >= 2
//
private Object _data = null;
private int _size = 0;
public SmartList() {
}
public SmartList(final E element) {
add(element);
}
public SmartList(@NotNull final java.util.Collection extends E> elements) {
final int size = elements.size();
if (size == 1) {
final E element = elements instanceof List ? (E) ((List) elements).get(0)
: elements.iterator().next();
add(element);
}
else if (size > 0) {
_size = size;
_data = elements.toArray(new Object[size]);
}
}
public SmartList(@NotNull final E... elements) {
if (elements.length == 1) {
add(elements[0]);
}
else if (elements.length > 0) {
_size = elements.length;
_data = Arrays.copyOf(elements, _size);
}
}
@Override
public E get(final int index) {
if (index < 0 || index >= _size) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + _size);
}
if (_size == 1) {
return (E) _data;
}
return (E) ((Object[]) _data)[index];
}
@Override
public boolean add(final E e) {
switch (_size) {
case 0: {
_data = e;
break;
}
case 1: {
final Object[] array = new Object[2];
array[0] = _data;
array[1] = e;
_data = array;
break;
}
default: {
Object[] array = (Object[]) _data;
final int oldCapacity = array.length;
if (_size >= oldCapacity) {
//
// Resize the backing array.
//
int newCapacity = oldCapacity * 3 / 2 + 1;
final int minCapacity = _size + 1;
if (newCapacity < minCapacity) {
newCapacity = minCapacity;
}
final Object[] oldArray = array;
_data = array = new Object[newCapacity];
System.arraycopy(oldArray, 0, array, 0, oldCapacity);
}
array[_size] = e;
break;
}
}
_size++;
modCount++;
return true;
}
@Override
public void add(final int index, final E e) {
if (index < 0 || index > _size) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + _size);
}
switch (_size) {
case 0: {
_data = e;
break;
}
case 1: {
if (index == 0) {
final Object[] array = new Object[2];
array[0] = e;
array[1] = _data;
_data = array;
break;
}
//
// If index != 0, fall through to default.
//
}
default: {
final Object[] array = new Object[_size + 1];
if (_size == 1) {
array[0] = _data; // index == 1
}
else {
final Object[] oldArray = (Object[]) _data;
System.arraycopy(oldArray, 0, array, 0, index);
System.arraycopy(oldArray, index, array, index + 1, _size - index);
}
array[index] = e;
_data = array;
}
}
_size++;
modCount++;
}
@Override
public int size() {
return _size;
}
@Override
public void clear() {
_data = null;
_size = 0;
modCount++;
}
@Override
public E set(final int index, final E element) {
if (index < 0 || index >= _size) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + _size);
}
final E oldValue;
if (_size == 1) {
oldValue = (E) _data;
_data = element;
}
else {
final Object[] array = (Object[]) _data;
oldValue = (E) array[index];
array[index] = element;
}
return oldValue;
}
@Override
public E remove(final int index) {
if (index < 0 || index >= _size) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + _size);
}
final E oldValue;
if (_size == 1) {
oldValue = (E) _data;
_data = null;
}
else {
final Object[] array = (Object[]) _data;
oldValue = (E) array[index];
if (_size == 2) {
_data = array[1 - index];
}
else {
final int numMoved = _size - index - 1;
if (numMoved > 0) {
System.arraycopy(array, index + 1, array, index, numMoved);
}
array[_size - 1] = null;
}
}
_size--;
modCount++;
return oldValue;
}
@NotNull
@Override
public java.util.Iterator iterator() {
switch (_size) {
case 0:
return Collections.emptyIterator();
case 1:
return new SingletonIterator();
default:
return super.iterator();
}
}
private final class SingletonIterator implements Iterator {
private boolean _visited;
private final int _initialModCount;
public SingletonIterator() {
_initialModCount = modCount;
}
@Override
public boolean hasNext() {
return !_visited;
}
@Override
public E next() {
if (_visited) {
throw new NoSuchElementException();
}
_visited = true;
if (modCount != _initialModCount) {
throw new ConcurrentModificationException("ModCount: " + modCount + "; expected: " + _initialModCount);
}
return (E) _data;
}
@Override
public void remove() {
if (modCount != _initialModCount) {
throw new ConcurrentModificationException("ModCount: " + modCount + "; expected: " + _initialModCount);
}
clear();
}
}
public void sort(@NotNull final java.util.Comparator super E> comparator) {
if (_size >= 2) {
Arrays.sort((E[]) _data, 0, _size, comparator);
}
}
public int getModificationCount() {
return modCount;
}
@NotNull
@Override
public T[] toArray(@NotNull final T[] a) {
if (_size == 1) {
final int length = a.length;
if (length != 0) {
a[0] = (T) _data;
if (length > 1) {
a[1] = null;
}
return a;
}
}
//noinspection SuspiciousToArrayCall
return super.toArray(a);
}
/**
* Trims the capacity of this list to be the
* list's current size. An application can use this operation to minimize
* the storage of a list instance.
*/
public void trimToSize() {
if (_size < 2) {
return;
}
final Object[] array = (Object[]) _data;
final int oldCapacity = array.length;
if (_size < oldCapacity) {
modCount++;
_data = Arrays.copyOf(array, _size);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy