All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.strobel.collections.ImmutableList Maven / Gradle / Ivy

/*
 * ImmutableList.java
 *
 * Copyright (c) 2012 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 com.strobel.core.StringUtilities;

import java.lang.reflect.Array;
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.NoSuchElementException;

/**
 * A class for generic linked lists. Links are supposed to be
 * immutable, the only exception being the incremental construction of
 * lists via ListBuffers.  List is the main container class in
 * GJC. Most data structures and algorithms in GJC use lists rather
 * than arrays.
 * 

*

Lists are always trailed by a sentinel element, whose head and tail * are both null. *

*

This is NOT part of any supported API. * If you write code that depends on this, you do so at your own risk. * This code and its internal interfaces are subject to change or * deletion without notice. */ @SuppressWarnings("PublicField") public class ImmutableList extends AbstractCollection implements java.util.List { /** * The first element of the list, supposed to be immutable. */ public A head; /** * The remainder of the list except for its first element, supposed * to be immutable. */ //@Deprecated public ImmutableList tail; /** * Construct a list given its head and tail. */ ImmutableList(final A head, final ImmutableList tail) { this.tail = tail; this.head = head; } /** * Construct an empty list. */ @SuppressWarnings("unchecked") public static ImmutableList empty() { return (ImmutableList)EMPTY_LIST; } private static final ImmutableList EMPTY_LIST = new ImmutableList(null, null) { public ImmutableList setTail(final ImmutableList tail) { throw new UnsupportedOperationException(); } public boolean isEmpty() { return true; } }; /** * Construct a list consisting of given element. */ public static ImmutableList of(final A x1) { return new ImmutableList<>(x1, ImmutableList.empty()); } /** * Construct a list consisting of given elements. */ @SafeVarargs public static ImmutableList of(final A x1, final A... rest) { return new ImmutableList<>(x1, from(rest)); } /** * Construct a list consisting of given elements. */ public static ImmutableList of(final A x1, final A x2) { return new ImmutableList<>(x1, of(x2)); } /** * Construct a list consisting of given elements. */ public static ImmutableList of(final A x1, final A x2, final A x3) { return new ImmutableList<>(x1, of(x2, x3)); } /** * Construct a list consisting of given elements. */ @SuppressWarnings({"varargs", "unchecked"}) public static ImmutableList of(final A x1, final A x2, final A x3, final A... rest) { return new ImmutableList<>(x1, new ImmutableList<>(x2, new ImmutableList<>(x3, from(rest)))); } /** * Construct a list consisting all elements of given array. * @param array an array; if {@code null} return an empty list */ public static ImmutableList from(final A[] array) { ImmutableList xs = empty(); if (array != null) { for (int i = array.length - 1; i >= 0; i--) { xs = new ImmutableList<>(array[i], xs); } } return xs; } /** * Construct a list consisting of a given number of identical elements. * @param len The number of elements in the list. * @param init The value of each element. */ @Deprecated public static ImmutableList fill(final int len, final A init) { ImmutableList l = empty(); for (int i = 0; i < len; i++) { l = new ImmutableList<>(init, l); } return l; } /** * Does list have no elements? */ @Override public boolean isEmpty() { return tail == null; } /** * Does list have elements? */ //@Deprecated public boolean nonEmpty() { return tail != null; } /** * Return the number of elements in this list. */ //@Deprecated public int length() { ImmutableList l = this; int len = 0; while (l.tail != null) { l = l.tail; len++; } return len; } @Override public int size() { return length(); } public ImmutableList setTail(final ImmutableList tail) { this.tail = tail; return tail; } /** * Prepend given element to front of list, forming and returning * a new list. */ public ImmutableList prepend(final A x) { return new ImmutableList<>(x, this); } /** * Prepend given list of elements to front of list, forming and returning * a new list. */ public ImmutableList prependList(final ImmutableList xs) { if (this.isEmpty()) { return xs; } if (xs.isEmpty()) { return this; } if (xs.tail.isEmpty()) { return prepend(xs.head); } // Return this.prependList(xs.tail).prepend(xs.head); ImmutableList result = this; ImmutableList rev = xs.reverse(); assert rev != xs; // since xs.reverse() returned a new list, we can reuse the // individual List objects, instead of allocating new ones. while (rev.nonEmpty()) { final ImmutableList h = rev; rev = rev.tail; h.setTail(result); result = h; } return result; } /** * Reverse list. * If the list is empty or a singleton, then the same list is returned. * Otherwise a new list is formed. */ public ImmutableList reverse() { // if it is empty or a singleton, return itself if (isEmpty() || tail.isEmpty()) { return this; } ImmutableList rev = empty(); for (ImmutableList l = this; l.nonEmpty(); l = l.tail) { rev = new ImmutableList<>(l.head, rev); } return rev; } /** * Append given element at length, forming and returning * a new list. */ public ImmutableList append(final A x) { return of(x).prependList(this); } /** * Append given list at length, forming and returning * a new list. */ public ImmutableList appendList(final ImmutableList x) { return x.prependList(this); } /** * Append given list buffer at length, forming and returning a new * list. */ public ImmutableList appendList(final ListBuffer x) { return appendList(x.toList()); } /** * Copy successive elements of this list into given vector until * list is exhausted or end of vector is reached. */ @NotNull @Override @SuppressWarnings("unchecked") public T[] toArray(@NotNull final T[] vec) { int i = 0; ImmutableList l = this; while (l.nonEmpty() && i < vec.length) { vec[i] = (T)l.head; l = l.tail; i++; } if (l.isEmpty()) { if (i < vec.length) { vec[i] = null; } return vec; } return toArray((T[])Array.newInstance(vec.getClass().getComponentType(), size())); } @NotNull public Object[] toArray() { return toArray(new Object[size()]); } /** * Form a string listing all elements with given separator character. */ public String toString(final String sep) { if (isEmpty()) { return StringUtilities.EMPTY; } else { final StringBuilder buffer = new StringBuilder(); buffer.append(head); for (ImmutableList l = tail; l.nonEmpty(); l = l.tail) { buffer.append(sep); buffer.append(l.head); } return buffer.toString(); } } /** * Form a string listing all elements with comma as the separator character. */ @Override public String toString() { return toString(","); } /** * Compute a hash code, overrides Object * @see java.util.List#hashCode */ @Override public int hashCode() { ImmutableList l = this; int h = 1; while (l.tail != null) { h = h * 31 + (l.head == null ? 0 : l.head.hashCode()); l = l.tail; } return h; } /** * Is this list the same as other list? * @see java.util.List#equals */ @Override public boolean equals(final Object other) { if (other instanceof ImmutableList) { return equals(this, (ImmutableList)other); } if (other instanceof java.util.List) { ImmutableList t = this; final Iterator it = ((java.util.List)other).iterator(); while (t.tail != null && it.hasNext()) { final Object o = it.next(); if (!(t.head == null ? o == null : t.head.equals(o))) { return false; } t = t.tail; } return (t.isEmpty() && !it.hasNext()); } return false; } /** * Are the two lists the same? */ public static boolean equals(ImmutableList xs, ImmutableList ys) { while (xs.tail != null && ys.tail != null) { if (xs.head == null) { if (ys.head != null) { return false; } } else { if (!xs.head.equals(ys.head)) { return false; } } xs = xs.tail; ys = ys.tail; } return xs.tail == null && ys.tail == null; } /** * Does the list contain the specified element? */ @Override public boolean contains(final Object x) { ImmutableList l = this; while (l.tail != null) { if (x == null) { if (l.head == null) { return true; } } else { if (l.head.equals(x)) { return true; } } l = l.tail; } return false; } /** * The last element in the list, if any, or null. */ public A last() { A last = null; ImmutableList t = this; while (t.tail != null) { last = t.head; t = t.tail; } return last; } @SuppressWarnings("unchecked") public static ImmutableList convert(final Class type, final ImmutableList list) { if (list == null) { return null; } for (final Object o : list) { type.cast(o); } return (ImmutableList)list; } private static final Iterator EMPTY_ITERATOR = new Iterator() { public boolean hasNext() { return false; } public Object next() { throw new java.util.NoSuchElementException(); } public void remove() { throw new UnsupportedOperationException(); } }; @SuppressWarnings("unchecked") private static Iterator emptyIterator() { return (Iterator)EMPTY_ITERATOR; } @NotNull @Override public Iterator iterator() { if (tail == null) { return emptyIterator(); } return new Iterator() { private ImmutableList _elements = ImmutableList.this; public boolean hasNext() { return _elements.tail != null; } public A next() { if (_elements.tail == null) { throw new NoSuchElementException(); } final A result = _elements.head; _elements = _elements.tail; return result; } public void remove() { throw new UnsupportedOperationException(); } }; } public A get(final int index) { if (index < 0) { throw new IndexOutOfBoundsException(String.valueOf(index)); } ImmutableList l = this; int i = index; while (i-- > 0 && !l.isEmpty()) { l = l.tail; } if (l.isEmpty()) { throw new IndexOutOfBoundsException( "Index: " + index + ", " + "Size: " + size() ); } return l.head; } public boolean addAll(final int index, @NotNull final Collection c) { if (c.isEmpty()) { return false; } throw new UnsupportedOperationException(); } public A set(final int index, final A element) { throw new UnsupportedOperationException(); } public void add(final int index, final A element) { throw new UnsupportedOperationException(); } public A remove(final int index) { throw new UnsupportedOperationException(); } public int indexOf(final Object o) { int i = 0; for (ImmutableList l = this; l.tail != null; l = l.tail, i++) { if (l.head == null ? o == null : l.head.equals(o)) { return i; } } return -1; } public int lastIndexOf(final Object o) { int last = -1; int i = 0; for (ImmutableList l = this; l.tail != null; l = l.tail, i++) { if (l.head == null ? o == null : l.head.equals(o)) { last = i; } } return last; } @NotNull public ListIterator listIterator() { return Collections.unmodifiableList(new ArrayList<>(this)).listIterator(); } @NotNull public ListIterator listIterator(final int index) { return Collections.unmodifiableList(new ArrayList<>(this)).listIterator(index); } @NotNull public java.util.List subList(final int fromIndex, final int toIndex) { if (fromIndex < 0 || toIndex > size() || fromIndex > toIndex) { throw new IllegalArgumentException(); } final ArrayList a = new ArrayList<>(toIndex - fromIndex); int i = 0; for (ImmutableList l = this; l.tail != null; l = l.tail, i++) { if (i == toIndex) { break; } if (i >= fromIndex) { a.add(l.head); } } return Collections.unmodifiableList(a); } }