AbstractList.java revision fdb2704414a9ed92394ada0d1395e4db86889465
1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package java.util;
19
20/**
21 * AbstractList is an abstract implementation of the List interface, optimized
22 * for a backing store which supports random access. This implementation does
23 * not support adding or replacing. A subclass must implement the abstract
24 * methods get() and size().
25 *
26 * @since 1.2
27 */
28public abstract class AbstractList<E> extends AbstractCollection<E> implements
29        List<E> {
30
31    protected transient int modCount;
32
33    private class SimpleListIterator implements Iterator<E> {
34        int pos = -1;
35
36        int expectedModCount;
37
38        int lastPosition = -1;
39
40        SimpleListIterator() {
41            super();
42            expectedModCount = modCount;
43        }
44
45        public boolean hasNext() {
46            return pos + 1 < size();
47        }
48
49        public E next() {
50            if (expectedModCount == modCount) {
51                try {
52                    E result = get(pos + 1);
53                    lastPosition = ++pos;
54                    return result;
55                } catch (IndexOutOfBoundsException e) {
56                    throw new NoSuchElementException();
57                }
58            }
59            throw new ConcurrentModificationException();
60        }
61
62        public void remove() {
63            if (expectedModCount == modCount) {
64                try {
65                    AbstractList.this.remove(lastPosition);
66                } catch (IndexOutOfBoundsException e) {
67                    throw new IllegalStateException();
68                }
69                if (modCount != expectedModCount) {
70                    expectedModCount++;
71                }
72                if (pos == lastPosition) {
73                    pos--;
74                }
75                lastPosition = -1;
76            } else {
77                throw new ConcurrentModificationException();
78            }
79        }
80    }
81
82    private final class FullListIterator extends SimpleListIterator implements
83            ListIterator<E> {
84        FullListIterator(int start) {
85            super();
86            if (0 <= start && start <= size()) {
87                pos = start - 1;
88            } else {
89                throw new IndexOutOfBoundsException();
90            }
91        }
92
93        public void add(E object) {
94            if (expectedModCount == modCount) {
95                try {
96                    AbstractList.this.add(pos + 1, object);
97                } catch (IndexOutOfBoundsException e) {
98                    throw new NoSuchElementException();
99                }
100                pos++;
101                lastPosition = -1;
102                if (modCount != expectedModCount) {
103                    expectedModCount++;
104                }
105            } else {
106                throw new ConcurrentModificationException();
107            }
108        }
109
110        public boolean hasPrevious() {
111            return pos >= 0;
112        }
113
114        public int nextIndex() {
115            return pos + 1;
116        }
117
118        public E previous() {
119            if (expectedModCount == modCount) {
120                try {
121                    E result = get(pos);
122                    lastPosition = pos;
123                    pos--;
124                    return result;
125                } catch (IndexOutOfBoundsException e) {
126                    throw new NoSuchElementException();
127                }
128            }
129            throw new ConcurrentModificationException();
130        }
131
132        public int previousIndex() {
133            return pos;
134        }
135
136        public void set(E object) {
137            if (expectedModCount == modCount) {
138                try {
139                    AbstractList.this.set(lastPosition, object);
140                } catch (IndexOutOfBoundsException e) {
141                    throw new IllegalStateException();
142                }
143            } else {
144                throw new ConcurrentModificationException();
145            }
146        }
147    }
148
149    private static final class SubAbstractListRandomAccess<E> extends
150            SubAbstractList<E> implements RandomAccess {
151        SubAbstractListRandomAccess(AbstractList<E> list, int start, int end) {
152            super(list, start, end);
153        }
154    }
155
156    private static class SubAbstractList<E> extends AbstractList<E> {
157        private final AbstractList<E> fullList;
158
159        private int offset;
160
161        private int size;
162
163        private static final class SubAbstractListIterator<E> implements
164                ListIterator<E> {
165            private final SubAbstractList<E> subList;
166
167            private final ListIterator<E> iterator;
168
169            private int start;
170
171            private int end;
172
173            SubAbstractListIterator(ListIterator<E> it,
174                    SubAbstractList<E> list, int offset, int length) {
175                super();
176                iterator = it;
177                subList = list;
178                start = offset;
179                end = start + length;
180            }
181
182            public void add(E object) {
183                iterator.add(object);
184                subList.sizeChanged(true);
185                end++;
186            }
187
188            public boolean hasNext() {
189                return iterator.nextIndex() < end;
190            }
191
192            public boolean hasPrevious() {
193                return iterator.previousIndex() >= start;
194            }
195
196            public E next() {
197                if (iterator.nextIndex() < end) {
198                    return iterator.next();
199                }
200                throw new NoSuchElementException();
201            }
202
203            public int nextIndex() {
204                return iterator.nextIndex() - start;
205            }
206
207            public E previous() {
208                if (iterator.previousIndex() >= start) {
209                    return iterator.previous();
210                }
211                throw new NoSuchElementException();
212            }
213
214            public int previousIndex() {
215                int previous = iterator.previousIndex();
216                if (previous >= start) {
217                    return previous - start;
218                }
219                return -1;
220            }
221
222            public void remove() {
223                iterator.remove();
224                subList.sizeChanged(false);
225                end--;
226            }
227
228            public void set(E object) {
229                iterator.set(object);
230            }
231        }
232
233        SubAbstractList(AbstractList<E> list, int start, int end) {
234            super();
235            fullList = list;
236            modCount = fullList.modCount;
237            offset = start;
238            size = end - start;
239        }
240
241        @Override
242        public void add(int location, E object) {
243            if (modCount == fullList.modCount) {
244                if (0 <= location && location <= size) {
245                    fullList.add(location + offset, object);
246                    size++;
247                    modCount = fullList.modCount;
248                } else {
249                    throw new IndexOutOfBoundsException();
250                }
251            } else {
252                throw new ConcurrentModificationException();
253            }
254        }
255
256        @Override
257        public boolean addAll(int location, Collection<? extends E> collection) {
258            if (modCount == fullList.modCount) {
259                if (0 <= location && location <= size) {
260                    boolean result = fullList.addAll(location + offset,
261                            collection);
262                    if (result) {
263                        size += collection.size();
264                        modCount = fullList.modCount;
265                    }
266                    return result;
267                }
268                throw new IndexOutOfBoundsException();
269            }
270            throw new ConcurrentModificationException();
271        }
272
273        @Override
274        public boolean addAll(Collection<? extends E> collection) {
275            if (modCount == fullList.modCount) {
276                boolean result = fullList.addAll(offset + size, collection);
277                if (result) {
278                    size += collection.size();
279                    modCount = fullList.modCount;
280                }
281                return result;
282            }
283            throw new ConcurrentModificationException();
284        }
285
286        @Override
287        public E get(int location) {
288            if (modCount == fullList.modCount) {
289                if (0 <= location && location < size) {
290                    return fullList.get(location + offset);
291                }
292                throw new IndexOutOfBoundsException();
293            }
294            throw new ConcurrentModificationException();
295        }
296
297        @Override
298        public Iterator<E> iterator() {
299            return listIterator(0);
300        }
301
302        @Override
303        public ListIterator<E> listIterator(int location) {
304            if (modCount == fullList.modCount) {
305                if (0 <= location && location <= size) {
306                    return new SubAbstractListIterator<E>(fullList
307                            .listIterator(location + offset), this, offset,
308                            size);
309                }
310                throw new IndexOutOfBoundsException();
311            }
312            throw new ConcurrentModificationException();
313        }
314
315        @Override
316        public E remove(int location) {
317            if (modCount == fullList.modCount) {
318                if (0 <= location && location < size) {
319                    E result = fullList.remove(location + offset);
320                    size--;
321                    modCount = fullList.modCount;
322                    return result;
323                }
324                throw new IndexOutOfBoundsException();
325            }
326            throw new ConcurrentModificationException();
327        }
328
329        @Override
330        protected void removeRange(int start, int end) {
331            if (start != end) {
332                if (modCount == fullList.modCount) {
333                    fullList.removeRange(start + offset, end + offset);
334                    size -= end - start;
335                    modCount = fullList.modCount;
336                } else {
337                    throw new ConcurrentModificationException();
338                }
339            }
340        }
341
342        @Override
343        public E set(int location, E object) {
344            if (modCount == fullList.modCount) {
345                if (0 <= location && location < size) {
346                    return fullList.set(location + offset, object);
347                }
348                throw new IndexOutOfBoundsException();
349            }
350            throw new ConcurrentModificationException();
351        }
352
353        @Override
354        public int size() {
355            return size;
356        }
357
358        void sizeChanged(boolean increment) {
359            if (increment) {
360                size++;
361            } else {
362                size--;
363            }
364            modCount = fullList.modCount;
365        }
366    }
367
368    /**
369     * Constructs a new instance of this AbstractList.
370     *
371     */
372    protected AbstractList() {
373        super();
374    }
375
376    /**
377     * Inserts the specified object into this List at the specified location.
378     * The object is inserted before any previous element at the specified
379     * location. If the location is equal to the size of this List, the object
380     * is added at the end.
381     *
382     *
383     * @param location
384     *            the index at which to insert
385     * @param object
386     *            the object to add
387     *
388     * @exception UnsupportedOperationException
389     *                when adding to this List is not supported
390     * @exception ClassCastException
391     *                when the class of the object is inappropriate for this
392     *                List
393     * @exception IllegalArgumentException
394     *                when the object cannot be added to this List
395     * @exception IndexOutOfBoundsException
396     *                when <code>location < 0 || >= size()</code>
397     */
398    public void add(int location, E object) {
399        throw new UnsupportedOperationException();
400    }
401
402    /**
403     * Adds the specified object at the end of this List.
404     *
405     *
406     * @param object
407     *            the object to add
408     * @return true
409     *
410     * @exception UnsupportedOperationException
411     *                when adding to this List is not supported
412     * @exception ClassCastException
413     *                when the class of the object is inappropriate for this
414     *                List
415     * @exception IllegalArgumentException
416     *                when the object cannot be added to this List
417     */
418    @Override
419    public boolean add(E object) {
420        add(size(), object);
421        return true;
422    }
423
424    /**
425     * Inserts the objects in the specified Collection at the specified location
426     * in this List. The objects are added in the order they are returned from
427     * the Collection iterator.
428     *
429     *
430     * @param location
431     *            the index at which to insert
432     * @param collection
433     *            the Collection of objects
434     * @return true if this List is modified, false otherwise
435     *
436     * @exception UnsupportedOperationException
437     *                when adding to this List is not supported
438     * @exception ClassCastException
439     *                when the class of an object is inappropriate for this List
440     * @exception IllegalArgumentException
441     *                when an object cannot be added to this List
442     * @exception IndexOutOfBoundsException
443     *                when <code>location < 0 || >= size()</code>
444     */
445    public boolean addAll(int location, Collection<? extends E> collection) {
446        Iterator<? extends E> it = collection.iterator();
447        while (it.hasNext()) {
448            add(location++, it.next());
449        }
450        return !collection.isEmpty();
451    }
452
453    /**
454     * Removes all elements from this List, leaving it empty.
455     *
456     *
457     * @exception UnsupportedOperationException
458     *                when removing from this List is not supported
459     *
460     * @see List#isEmpty
461     * @see List#size
462     */
463    @Override
464    public void clear() {
465        removeRange(0, size());
466    }
467
468    /**
469     * Compares the specified object to this List and answer if they are equal.
470     * The object must be a List which contains the same objects in the same
471     * order.
472     *
473     *
474     * @param object
475     *            the object to compare with this object
476     * @return true if the specified object is equal to this List, false
477     *         otherwise
478     *
479     * @see #hashCode
480     */
481    @Override
482    public boolean equals(Object object) {
483        if (this == object) {
484            return true;
485        }
486        if (object instanceof List) {
487            List<?> list = (List<?>) object;
488            if (list.size() != size()) {
489                return false;
490            }
491
492            Iterator<?> it1 = iterator(), it2 = list.iterator();
493            while (it1.hasNext()) {
494                Object e1 = it1.next(), e2 = it2.next();
495                if (!(e1 == null ? e2 == null : e1.equals(e2))) {
496                    return false;
497                }
498            }
499            return true;
500        }
501        return false;
502    }
503
504    /**
505     * Returns the element at the specified location in this List.
506     *
507     *
508     * @param location
509     *            the index of the element to return
510     * @return the element at the specified index
511     *
512     * @exception IndexOutOfBoundsException
513     *                when <code>location < 0 || >= size()</code>
514     */
515    public abstract E get(int location);
516
517    /**
518     * Returns an integer hash code for the receiver. Objects which are equal
519     * answer the same value for this method.
520     *
521     *
522     * @return the receiver's hash
523     *
524     * @see #equals
525     */
526    @Override
527    public int hashCode() {
528        int result = 1;
529        Iterator<?> it = iterator();
530        while (it.hasNext()) {
531            Object object = it.next();
532            result = (31 * result) + (object == null ? 0 : object.hashCode());
533        }
534        return result;
535    }
536
537    /**
538     * Searches this List for the specified object and returns the index of the
539     * first occurrence.
540     *
541     *
542     * @param object
543     *            the object to search for
544     * @return the index of the first occurrence of the object
545     */
546    public int indexOf(Object object) {
547        ListIterator<?> it = listIterator();
548        if (object != null) {
549            while (it.hasNext()) {
550                if (object.equals(it.next())) {
551                    return it.previousIndex();
552                }
553            }
554        } else {
555            while (it.hasNext()) {
556                if (it.next() == null) {
557                    return it.previousIndex();
558                }
559            }
560        }
561        return -1;
562    }
563
564    /**
565     * Returns an Iterator on the elements of this List. The elements are
566     * iterated in the same order that they occur in the List.
567     *
568     *
569     * @return an Iterator on the elements of this List
570     *
571     * @see Iterator
572     */
573    @Override
574    public Iterator<E> iterator() {
575        return new SimpleListIterator();
576    }
577
578    /**
579     * Searches this List for the specified object and returns the index of the
580     * last occurrence.
581     *
582     *
583     * @param object
584     *            the object to search for
585     * @return the index of the last occurrence of the object
586     */
587    public int lastIndexOf(Object object) {
588        ListIterator<?> it = listIterator(size());
589        if (object != null) {
590            while (it.hasPrevious()) {
591                if (object.equals(it.previous())) {
592                    return it.nextIndex();
593                }
594            }
595        } else {
596            while (it.hasPrevious()) {
597                if (it.previous() == null) {
598                    return it.nextIndex();
599                }
600            }
601        }
602        return -1;
603    }
604
605    /**
606     * Returns a ListIterator on the elements of this List. The elements are
607     * iterated in the same order that they occur in the List.
608     *
609     *
610     * @return a ListIterator on the elements of this List
611     *
612     * @see ListIterator
613     */
614    public ListIterator<E> listIterator() {
615        return listIterator(0);
616    }
617
618    /**
619     * Returns a ListIterator on the elements of this List. The elements are
620     * iterated in the same order that they occur in the List. The iteration
621     * starts at the specified location.
622     *
623     *
624     * @param location
625     *            the index at which to start the iteration
626     * @return a ListIterator on the elements of this List
627     *
628     * @exception IndexOutOfBoundsException
629     *                when <code>location < 0 || >= size()</code>
630     *
631     * @see ListIterator
632     */
633    public ListIterator<E> listIterator(int location) {
634        return new FullListIterator(location);
635    }
636
637    /**
638     * Removes the object at the specified location from this List.
639     *
640     *
641     * @param location
642     *            the index of the object to remove
643     * @return the removed object
644     *
645     * @exception UnsupportedOperationException
646     *                when removing from this List is not supported
647     * @exception IndexOutOfBoundsException
648     *                when <code>location < 0 || >= size()</code>
649     */
650    public E remove(int location) {
651        throw new UnsupportedOperationException();
652    }
653
654    /**
655     * Removes the objects in the specified range from the start to the, but not
656     * including, end index.
657     *
658     *
659     * @param start
660     *            the index at which to start removing
661     * @param end
662     *            the index one past the end of the range to remove
663     *
664     * @exception UnsupportedOperationException
665     *                when removing from this List is not supported
666     * @exception IndexOutOfBoundsException
667     *                when <code>start < 0
668     */
669    protected void removeRange(int start, int end) {
670        Iterator<?> it = listIterator(start);
671        for (int i = start; i < end; i++) {
672            it.next();
673            it.remove();
674        }
675    }
676
677    /**
678     * Replaces the element at the specified location in this List with the
679     * specified object.
680     *
681     *
682     * @param location
683     *            the index at which to put the specified object
684     * @param object
685     *            the object to add
686     * @return the previous element at the index
687     *
688     * @exception UnsupportedOperationException
689     *                when replacing elements in this List is not supported
690     * @exception ClassCastException
691     *                when the class of an object is inappropriate for this List
692     * @exception IllegalArgumentException
693     *                when an object cannot be added to this List
694     * @exception IndexOutOfBoundsException
695     *                when <code>location < 0 || >= size()</code>
696     */
697    public E set(int location, E object) {
698        throw new UnsupportedOperationException();
699    }
700
701    /**
702     * Returns a part of consecutive elements of this list as a view. From start
703     * (inclusive), to end(exclusive). The returned view will be of zero length
704     * if start equals end. Any change occurs in the returned subList will be
705     * reflected to the original list, and vice-versa. All the supported
706     * optional operations by the original list will also be supported by this
707     * subList.
708     *
709     * This method can be used as a handy method to do some operations on a sub
710     * range of the original list. For example: list.subList(from, to).clear();
711     *
712     * If the original list is modified other than through the returned subList,
713     * the behavior of the returned subList becomes undefined.
714     *
715     * The returned subList is a subclass of AbstractList. The subclass stores
716     * offset, size of itself, and modCount of the original list. If the
717     * original list implements RandomAccess interface, the returned subList
718     * also implements RandomAccess interface.
719     *
720     * The subList's set(int, Object), get(int), add(int, Object), remove(int),
721     * addAll(int, Collection) and removeRange(int, int) methods first check the
722     * bounds, adjust offsets and then call the corresponding methods of the
723     * original AbstractList. addAll(Collection c) method of the returned
724     * subList calls the original addAll(offset + size, c).
725     *
726     * The listIterator(int) method of the subList wraps the original list
727     * iterator. The iterator() method of the subList invokes the original
728     * listIterator() method, and the size() method merely returns the size of
729     * the subList.
730     *
731     * All methods will throw a ConcurrentModificationException if the modCount
732     * of the original list is not equal to the expected value.
733     *
734     * @param start
735     *            start index of the subList, include start
736     * @param end
737     *            end index of the subList, exclude end
738     * @return a subList view of this list start from start (inclusive), end
739     *         with end (exclusive)
740     * @exception IndexOutOfBoundsException
741     *                when (start < 0 || end > size())
742     * @exception IllegalArgumentException
743     *                when (start > end)
744     */
745    public List<E> subList(int start, int end) {
746        if (0 <= start && end <= size()) {
747            if (start <= end) {
748                if (this instanceof RandomAccess) {
749                    return new SubAbstractListRandomAccess<E>(this, start, end);
750                }
751                return new SubAbstractList<E>(this, start, end);
752            }
753            throw new IllegalArgumentException();
754        }
755        throw new IndexOutOfBoundsException();
756    }
757}
758