RegularImmutableList.java revision bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dc
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
17package com.google.common.collect;
18
19import com.google.common.annotations.GwtCompatible;
20import com.google.common.base.Preconditions;
21
22import java.util.List;
23import java.util.ListIterator;
24import java.util.NoSuchElementException;
25
26import 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
35class 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