View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase.util;
20  
21  import java.util.ArrayList;
22  import java.util.Collection;
23  import java.util.Collections;
24  import java.util.Comparator;
25  import java.util.Iterator;
26  import java.util.List;
27  import java.util.ListIterator;
28  import java.util.RandomAccess;
29  
30  /**
31   * Simple sorted list implementation that uses {@link java.util.ArrayList} as
32   * the underlying collection so we can support RandomAccess. All mutations
33   * create a new copy of the <code>ArrayList</code> instance, so can be
34   * expensive. This class is only intended for use on small, very rarely
35   * written collections that expect highly concurrent reads.
36   * <p>
37   * Read operations are performed on a reference to the internal list at the
38   * time of invocation, so will not see any mutations to the collection during
39   * their operation. Iterating over list elements manually using the
40   * RandomAccess pattern involves multiple operations. For this to be safe get
41   * a reference to the internal list first using get(). 
42   * <p>
43   * If constructed with a {@link java.util.Comparator}, the list will be sorted
44   * using the comparator. Adding or changing an element using an index will
45   * trigger a resort.
46   * <p>
47   * Iterators are read-only. They cannot be used to remove elements.
48   */
49  @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="UG_SYNC_SET_UNSYNC_GET",
50    justification="TODO: synchronization in here needs review!!!")
51  public class SortedList<E> implements List<E>, RandomAccess {
52    private volatile List<E> list;
53    private final Comparator<? super E> comparator;
54  
55    /**
56     * Constructs an empty list with the default initial capacity that will be
57     * sorted using the given comparator.
58     *
59     * @param comparator the comparator
60     */
61    public SortedList(Comparator<? super E> comparator) {
62      this.list = Collections.emptyList();
63      this.comparator = comparator;
64    }
65  
66    /**
67     * Constructs a list containing the elements of the given collection, in the
68     * order returned by the collection's iterator, that will be sorted with the
69     * given comparator.
70     *
71     * @param c the collection
72     * @param comparator the comparator
73     */
74    public SortedList(Collection<? extends E> c, Comparator<? super E> comparator) {
75      this.list = Collections.unmodifiableList(new ArrayList<E>(c));
76      this.comparator = comparator;
77    }
78  
79    /**
80     * Returns a reference to the unmodifiable list currently backing the SortedList.
81     * Changes to the SortedList will not be reflected in this list. Use this
82     * method to get a reference for iterating over using the RandomAccess
83     * pattern.
84     */
85    public List<E> get() { // FindBugs: UG_SYNC_SET_UNSYNC_GET complaint. Fix!!
86      return list;
87    }
88  
89    @Override
90    public int size() {
91      return list.size();
92    }
93  
94    @Override
95    public boolean isEmpty() {
96      return list.isEmpty();
97    }
98  
99    @Override
100   public boolean contains(Object o) {
101     return list.contains(o);
102   }
103 
104   @Override
105   public Iterator<E> iterator() {
106     return list.iterator();
107   }
108 
109   @Override
110   public Object[] toArray() {
111     return list.toArray();
112   }
113 
114   @Override
115   public <T> T[] toArray(T[] a) {
116     return list.toArray(a);
117   }
118 
119   @Override
120   public synchronized boolean add(E e) {
121     ArrayList<E> newList = new ArrayList<E>(list);
122     boolean changed = newList.add(e);
123     if (changed) {
124       Collections.sort(newList, comparator);
125     }
126     list = Collections.unmodifiableList(newList);
127     return changed;
128   }
129 
130   @Override
131   public synchronized boolean remove(Object o) {
132     ArrayList<E> newList = new ArrayList<E>(list);
133     // Removals in ArrayList won't break sorting
134     boolean changed = newList.remove(o);
135     list = Collections.unmodifiableList(newList);
136     return changed;
137   }
138 
139   @Override
140   public boolean containsAll(Collection<?> c) {
141     return list.containsAll(c);
142   }
143 
144   @Override
145   public synchronized boolean addAll(Collection<? extends E> c) {
146     ArrayList<E> newList = new ArrayList<E>(list);
147     boolean changed = newList.addAll(c);
148     if (changed) {
149       Collections.sort(newList, comparator);
150     }
151     list = Collections.unmodifiableList(newList);
152     return changed;
153   }
154 
155   @Override
156   public synchronized boolean addAll(int index, Collection<? extends E> c) {
157     ArrayList<E> newList = new ArrayList<E>(list);
158     boolean changed = newList.addAll(index, c);
159     if (changed) {
160       Collections.sort(newList, comparator);
161     }
162     list = Collections.unmodifiableList(newList);
163     return changed;
164   }
165 
166   @Override
167   public synchronized boolean removeAll(Collection<?> c) {
168     ArrayList<E> newList = new ArrayList<E>(list);
169     // Removals in ArrayList won't break sorting
170     boolean changed = newList.removeAll(c);
171     list = Collections.unmodifiableList(newList);
172     return changed;
173   }
174 
175   @Override
176   public synchronized boolean retainAll(Collection<?> c) {
177     ArrayList<E> newList = new ArrayList<E>(list);
178     // Removals in ArrayList won't break sorting
179     boolean changed = newList.retainAll(c);
180     list = Collections.unmodifiableList(newList);
181     return changed;
182   }
183 
184   @Override
185   public synchronized void clear() {
186     list = Collections.emptyList();
187   }
188 
189   @Override
190   public synchronized E get(int index) {
191     return list.get(index);
192   }
193 
194   @Override
195   public synchronized E set(int index, E element) {
196     ArrayList<E> newList = new ArrayList<E>(list);
197     E result = newList.set(index, element);
198     Collections.sort(list, comparator);
199     list = Collections.unmodifiableList(newList);
200     return result;
201   }
202 
203   @Override
204   public synchronized void add(int index, E element) {
205     ArrayList<E> newList = new ArrayList<E>(list);
206     newList.add(index, element);
207     Collections.sort(list, comparator);
208     list = Collections.unmodifiableList(newList);
209   }
210 
211   @Override
212   public synchronized E remove(int index) {
213     ArrayList<E> newList = new ArrayList<E>(list);
214     // Removals in ArrayList won't break sorting
215     E result = newList.remove(index);
216     list = Collections.unmodifiableList(newList);
217     return result;
218   }
219 
220   @Override
221   public int indexOf(Object o) {
222     return list.indexOf(o);
223   }
224 
225   @Override
226   public int lastIndexOf(Object o) {
227     return list.lastIndexOf(o);
228   }
229 
230   @Override
231   public ListIterator<E> listIterator() {
232     return list.listIterator();
233   }
234 
235   @Override
236   public ListIterator<E> listIterator(int index) {
237     return list.listIterator(index);
238   }
239 
240   @Override
241   public List<E> subList(int fromIndex, int toIndex) {
242     return list.subList(fromIndex, toIndex);
243   }
244 }