Home | History | Annotate | Download | only in collect
      1 /*
      2  * Copyright (C) 2009 Google Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.common.collect;
     18 
     19 import com.google.common.annotations.GwtCompatible;
     20 import com.google.common.base.Preconditions;
     21 
     22 import java.util.List;
     23 import java.util.ListIterator;
     24 import java.util.NoSuchElementException;
     25 
     26 import javax.annotation.Nullable;
     27 
     28 /**
     29  * Implementation of {@link ImmutableList} with one or more elements.
     30  *
     31  * @author Kevin Bourrillion
     32  */
     33 @GwtCompatible(serializable = true)
     34 @SuppressWarnings("serial") // uses writeReplace(), not default serialization
     35 class RegularImmutableList<E> extends ImmutableList<E> {
     36   private final transient int offset;
     37   private final transient int size;
     38   private final transient Object[] array;
     39 
     40   RegularImmutableList(Object[] array, int offset, int size) {
     41     this.offset = offset;
     42     this.size = size;
     43     this.array = array;
     44   }
     45 
     46   RegularImmutableList(Object[] array) {
     47     this(array, 0, array.length);
     48   }
     49 
     50   public int size() {
     51     return size;
     52   }
     53 
     54   @Override public boolean isEmpty() {
     55     return false;
     56   }
     57 
     58   @Override public boolean contains(Object target) {
     59     return indexOf(target) != -1;
     60   }
     61 
     62   // The fake cast to E is safe because the creation methods only allow E's
     63   @SuppressWarnings("unchecked")
     64   @Override public UnmodifiableIterator<E> iterator() {
     65     return (UnmodifiableIterator<E>) Iterators.forArray(array, offset, size);
     66   }
     67 
     68   @Override public Object[] toArray() {
     69     Object[] newArray = new Object[size()];
     70     Platform.unsafeArrayCopy(array, offset, newArray, 0, size);
     71     return newArray;
     72   }
     73 
     74   @Override public <T> T[] toArray(T[] other) {
     75     if (other.length < size) {
     76       other = ObjectArrays.newArray(other, size);
     77     } else if (other.length > size) {
     78       other[size] = null;
     79     }
     80     Platform.unsafeArrayCopy(array, offset, other, 0, size);
     81     return other;
     82   }
     83 
     84   // The fake cast to E is safe because the creation methods only allow E's
     85   @SuppressWarnings("unchecked")
     86   public E get(int index) {
     87     Preconditions.checkElementIndex(index, size);
     88     return (E) array[index + offset];
     89   }
     90 
     91   @Override public int indexOf(Object target) {
     92     if (target != null) {
     93       for (int i = offset; i < offset + size; i++) {
     94         if (array[i].equals(target)) {
     95           return i - offset;
     96         }
     97       }
     98     }
     99     return -1;
    100   }
    101 
    102   @Override public int lastIndexOf(Object target) {
    103     if (target != null) {
    104       for (int i = offset + size - 1; i >= offset; i--) {
    105         if (array[i].equals(target)) {
    106           return i - offset;
    107         }
    108       }
    109     }
    110     return -1;
    111   }
    112 
    113   @Override public ImmutableList<E> subList(int fromIndex, int toIndex) {
    114     Preconditions.checkPositionIndexes(fromIndex, toIndex, size);
    115     return (fromIndex == toIndex)
    116         ? ImmutableList.<E>of()
    117         : new RegularImmutableList<E>(
    118             array, offset + fromIndex, toIndex - fromIndex);
    119   }
    120 
    121   public ListIterator<E> listIterator() {
    122     return listIterator(0);
    123   }
    124 
    125   public ListIterator<E> listIterator(final int start) {
    126     Preconditions.checkPositionIndex(start, size);
    127 
    128     return new ListIterator<E>() {
    129       int index = start;
    130 
    131       public boolean hasNext() {
    132         return index < size;
    133       }
    134       public boolean hasPrevious() {
    135         return index > 0;
    136       }
    137 
    138       public int nextIndex() {
    139         return index;
    140       }
    141       public int previousIndex() {
    142         return index - 1;
    143       }
    144 
    145       public E next() {
    146         E result;
    147         try {
    148           result = get(index);
    149         } catch (IndexOutOfBoundsException rethrown) {
    150           throw new NoSuchElementException();
    151         }
    152         index++;
    153         return result;
    154       }
    155       public E previous() {
    156         E result;
    157         try {
    158           result = get(index - 1);
    159         } catch (IndexOutOfBoundsException rethrown) {
    160           throw new NoSuchElementException();
    161         }
    162         index--;
    163         return result;
    164       }
    165 
    166       public void set(E o) {
    167         throw new UnsupportedOperationException();
    168       }
    169       public void add(E o) {
    170         throw new UnsupportedOperationException();
    171       }
    172       public void remove() {
    173         throw new UnsupportedOperationException();
    174       }
    175     };
    176   }
    177 
    178   @Override public boolean equals(@Nullable Object object) {
    179     if (object == this) {
    180       return true;
    181     }
    182     if (!(object instanceof List)) {
    183       return false;
    184     }
    185 
    186     List<?> that = (List<?>) object;
    187     if (this.size() != that.size()) {
    188       return false;
    189     }
    190 
    191     int index = offset;
    192     if (object instanceof RegularImmutableList) {
    193       RegularImmutableList<?> other = (RegularImmutableList<?>) object;
    194       for (int i = other.offset; i < other.offset + other.size; i++) {
    195         if (!array[index++].equals(other.array[i])) {
    196           return false;
    197         }
    198       }
    199     } else {
    200       for (Object element : that) {
    201         if (!array[index++].equals(element)) {
    202           return false;
    203         }
    204       }
    205     }
    206     return true;
    207   }
    208 
    209   @Override public int hashCode() {
    210     // not caching hash code since it could change if the elements are mutable
    211     // in a way that modifies their hash codes
    212     int hashCode = 1;
    213     for (int i = offset; i < offset + size; i++) {
    214       hashCode = 31 * hashCode + array[i].hashCode();
    215     }
    216     return hashCode;
    217   }
    218 
    219   @Override public String toString() {
    220     StringBuilder sb = new StringBuilder(size() * 16);
    221     sb.append('[').append(array[offset]);
    222     for (int i = offset + 1; i < offset + size; i++) {
    223       sb.append(", ").append(array[i]);
    224     }
    225     return sb.append(']').toString();
    226   }
    227 
    228   int offset() {
    229     return offset;
    230   }
    231 
    232   Object[] array() {
    233     return array;
    234   }
    235 }
    236