• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 The Guava Authors
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 static com.google.common.base.Preconditions.checkArgument;
20 import static com.google.common.base.Preconditions.checkNotNull;
21 
22 import com.google.common.annotations.Beta;
23 import com.google.common.annotations.GwtCompatible;
24 import com.google.common.base.Function;
25 import com.google.common.base.Supplier;
26 
27 import java.io.Serializable;
28 import java.util.Comparator;
29 import java.util.Iterator;
30 import java.util.Map;
31 import java.util.NoSuchElementException;
32 import java.util.Set;
33 import java.util.SortedMap;
34 import java.util.SortedSet;
35 import java.util.TreeMap;
36 
37 import javax.annotation.Nullable;
38 
39 /**
40  * Implementation of {@code Table} whose row keys and column keys are ordered
41  * by their natural ordering or by supplied comparators. When constructing a
42  * {@code TreeBasedTable}, you may provide comparators for the row keys and
43  * the column keys, or you may use natural ordering for both.
44  *
45  * <p>The {@link #rowKeySet} method returns a {@link SortedSet} and the {@link
46  * #rowMap} method returns a {@link SortedMap}, instead of the {@link Set} and
47  * {@link Map} specified by the {@link Table} interface.
48  *
49  * <p>The views returned by {@link #column}, {@link #columnKeySet()}, and {@link
50  * #columnMap()} have iterators that don't support {@code remove()}. Otherwise,
51  * all optional operations are supported. Null row keys, columns keys, and
52  * values are not supported.
53  *
54  * <p>Lookups by row key are often faster than lookups by column key, because
55  * the data is stored in a {@code Map<R, Map<C, V>>}. A method call like {@code
56  * column(columnKey).get(rowKey)} still runs quickly, since the row key is
57  * provided. However, {@code column(columnKey).size()} takes longer, since an
58  * iteration across all row keys occurs.
59  *
60  * <p>Because a {@code TreeBasedTable} has unique sorted values for a given
61  * row, both {@code row(rowKey)} and {@code rowMap().get(rowKey)} are {@link
62  * SortedMap} instances, instead of the {@link Map} specified in the {@link
63  * Table} interface.
64  *
65  * <p>Note that this implementation is not synchronized. If multiple threads
66  * access this table concurrently and one of the threads modifies the table, it
67  * must be synchronized externally.
68  *
69  * <p>See the Guava User Guide article on <a href=
70  * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Table">
71  * {@code Table}</a>.
72  *
73  * @author Jared Levy
74  * @author Louis Wasserman
75  * @since 7.0
76  */
77 @GwtCompatible(serializable = true)
78 @Beta
79 public class TreeBasedTable<R, C, V> extends StandardRowSortedTable<R, C, V> {
80   private final Comparator<? super C> columnComparator;
81 
82   private static class Factory<C, V>
83       implements Supplier<TreeMap<C, V>>, Serializable {
84     final Comparator<? super C> comparator;
Factory(Comparator<? super C> comparator)85     Factory(Comparator<? super C> comparator) {
86       this.comparator = comparator;
87     }
88     @Override
get()89     public TreeMap<C, V> get() {
90       return new TreeMap<C, V>(comparator);
91     }
92     private static final long serialVersionUID = 0;
93   }
94 
95   /**
96    * Creates an empty {@code TreeBasedTable} that uses the natural orderings
97    * of both row and column keys.
98    *
99    * <p>The method signature specifies {@code R extends Comparable} with a raw
100    * {@link Comparable}, instead of {@code R extends Comparable<? super R>},
101    * and the same for {@code C}. That's necessary to support classes defined
102    * without generics.
103    */
104   public static <R extends Comparable, C extends Comparable, V>
create()105       TreeBasedTable<R, C, V> create() {
106     return new TreeBasedTable<R, C, V>(Ordering.natural(),
107         Ordering.natural());
108   }
109 
110   /**
111    * Creates an empty {@code TreeBasedTable} that is ordered by the specified
112    * comparators.
113    *
114    * @param rowComparator the comparator that orders the row keys
115    * @param columnComparator the comparator that orders the column keys
116    */
create( Comparator<? super R> rowComparator, Comparator<? super C> columnComparator)117   public static <R, C, V> TreeBasedTable<R, C, V> create(
118       Comparator<? super R> rowComparator,
119       Comparator<? super C> columnComparator) {
120     checkNotNull(rowComparator);
121     checkNotNull(columnComparator);
122     return new TreeBasedTable<R, C, V>(rowComparator, columnComparator);
123   }
124 
125   /**
126    * Creates a {@code TreeBasedTable} with the same mappings and sort order
127    * as the specified {@code TreeBasedTable}.
128    */
create( TreeBasedTable<R, C, ? extends V> table)129   public static <R, C, V> TreeBasedTable<R, C, V> create(
130       TreeBasedTable<R, C, ? extends V> table) {
131     TreeBasedTable<R, C, V> result
132         = new TreeBasedTable<R, C, V>(
133             table.rowComparator(), table.columnComparator());
134     result.putAll(table);
135     return result;
136   }
137 
TreeBasedTable(Comparator<? super R> rowComparator, Comparator<? super C> columnComparator)138   TreeBasedTable(Comparator<? super R> rowComparator,
139       Comparator<? super C> columnComparator) {
140     super(new TreeMap<R, Map<C, V>>(rowComparator),
141         new Factory<C, V>(columnComparator));
142     this.columnComparator = columnComparator;
143   }
144 
145   // TODO(jlevy): Move to StandardRowSortedTable?
146 
147   /**
148    * Returns the comparator that orders the rows. With natural ordering,
149    * {@link Ordering#natural()} is returned.
150    */
rowComparator()151   public Comparator<? super R> rowComparator() {
152     return rowKeySet().comparator();
153   }
154 
155   /**
156    * Returns the comparator that orders the columns. With natural ordering,
157    * {@link Ordering#natural()} is returned.
158    */
columnComparator()159   public Comparator<? super C> columnComparator() {
160     return columnComparator;
161   }
162 
163   // TODO(user): make column return a SortedMap
164 
165   /**
166    * {@inheritDoc}
167    *
168    * <p>Because a {@code TreeBasedTable} has unique sorted values for a given
169    * row, this method returns a {@link SortedMap}, instead of the {@link Map}
170    * specified in the {@link Table} interface.
171    * @since 10.0
172    *     (<a href="http://code.google.com/p/guava-libraries/wiki/Compatibility"
173    *     >mostly source-compatible</a> since 7.0)
174    */
175   @Override
row(R rowKey)176   public SortedMap<C, V> row(R rowKey) {
177     return new TreeRow(rowKey);
178   }
179 
180   private class TreeRow extends Row implements SortedMap<C, V> {
181     @Nullable final C lowerBound;
182     @Nullable final C upperBound;
183 
TreeRow(R rowKey)184     TreeRow(R rowKey) {
185       this(rowKey, null, null);
186     }
187 
TreeRow(R rowKey, @Nullable C lowerBound, @Nullable C upperBound)188     TreeRow(R rowKey, @Nullable C lowerBound, @Nullable C upperBound) {
189       super(rowKey);
190       this.lowerBound = lowerBound;
191       this.upperBound = upperBound;
192       checkArgument(lowerBound == null || upperBound == null
193           || compare(lowerBound, upperBound) <= 0);
194     }
195 
keySet()196     @Override public SortedSet<C> keySet() {
197       return new Maps.SortedKeySet<C, V>(this);
198     }
199 
comparator()200     @Override public Comparator<? super C> comparator() {
201       return columnComparator();
202     }
203 
compare(Object a, Object b)204     int compare(Object a, Object b) {
205       // pretend we can compare anything
206       @SuppressWarnings({"rawtypes", "unchecked"})
207       Comparator<Object> cmp = (Comparator) comparator();
208       return cmp.compare(a, b);
209     }
210 
rangeContains(@ullable Object o)211     boolean rangeContains(@Nullable Object o) {
212       return o != null && (lowerBound == null || compare(lowerBound, o) <= 0)
213           && (upperBound == null || compare(upperBound, o) > 0);
214     }
215 
subMap(C fromKey, C toKey)216     @Override public SortedMap<C, V> subMap(C fromKey, C toKey) {
217       checkArgument(rangeContains(checkNotNull(fromKey))
218           && rangeContains(checkNotNull(toKey)));
219       return new TreeRow(rowKey, fromKey, toKey);
220     }
221 
headMap(C toKey)222     @Override public SortedMap<C, V> headMap(C toKey) {
223       checkArgument(rangeContains(checkNotNull(toKey)));
224       return new TreeRow(rowKey, lowerBound, toKey);
225     }
226 
tailMap(C fromKey)227     @Override public SortedMap<C, V> tailMap(C fromKey) {
228       checkArgument(rangeContains(checkNotNull(fromKey)));
229       return new TreeRow(rowKey, fromKey, upperBound);
230     }
231 
firstKey()232     @Override public C firstKey() {
233       SortedMap<C, V> backing = backingRowMap();
234       if (backing == null) {
235         throw new NoSuchElementException();
236       }
237       return backingRowMap().firstKey();
238     }
239 
lastKey()240     @Override public C lastKey() {
241       SortedMap<C, V> backing = backingRowMap();
242       if (backing == null) {
243         throw new NoSuchElementException();
244       }
245       return backingRowMap().lastKey();
246     }
247 
248     transient SortedMap<C, V> wholeRow;
249 
250     /*
251      * If the row was previously empty, we check if there's a new row here every
252      * time we're queried.
253      */
wholeRow()254     SortedMap<C, V> wholeRow() {
255       if (wholeRow == null
256           || (wholeRow.isEmpty() && backingMap.containsKey(rowKey))) {
257         wholeRow = (SortedMap<C, V>) backingMap.get(rowKey);
258       }
259       return wholeRow;
260     }
261 
262     @Override
backingRowMap()263     SortedMap<C, V> backingRowMap() {
264       return (SortedMap<C, V>) super.backingRowMap();
265     }
266 
267     @Override
computeBackingRowMap()268     SortedMap<C, V> computeBackingRowMap() {
269       SortedMap<C, V> map = wholeRow();
270       if (map != null) {
271         if (lowerBound != null) {
272           map = map.tailMap(lowerBound);
273         }
274         if (upperBound != null) {
275           map = map.headMap(upperBound);
276         }
277         return map;
278       }
279       return null;
280     }
281 
282     @Override
maintainEmptyInvariant()283     void maintainEmptyInvariant() {
284       if (wholeRow() != null && wholeRow.isEmpty()) {
285         backingMap.remove(rowKey);
286         wholeRow = null;
287         backingRowMap = null;
288       }
289     }
290 
containsKey(Object key)291     @Override public boolean containsKey(Object key) {
292       return rangeContains(key) && super.containsKey(key);
293     }
294 
put(C key, V value)295     @Override public V put(C key, V value) {
296       checkArgument(rangeContains(checkNotNull(key)));
297       return super.put(key, value);
298     }
299   }
300 
301   // rowKeySet() and rowMap() are defined here so they appear in the Javadoc.
302 
rowKeySet()303   @Override public SortedSet<R> rowKeySet() {
304     return super.rowKeySet();
305   }
306 
rowMap()307   @Override public SortedMap<R, Map<C, V>> rowMap() {
308     return super.rowMap();
309   }
310 
311   /**
312    * Overridden column iterator to return columns values in globally sorted
313    * order.
314    */
315   @Override
createColumnKeyIterator()316   Iterator<C> createColumnKeyIterator() {
317     final Comparator<? super C> comparator = columnComparator();
318 
319     final Iterator<C> merged =
320         Iterators.mergeSorted(Iterables.transform(backingMap.values(),
321             new Function<Map<C, V>, Iterator<C>>() {
322               @Override
323               public Iterator<C> apply(Map<C, V> input) {
324                 return input.keySet().iterator();
325               }
326             }), comparator);
327 
328     return new AbstractIterator<C>() {
329       C lastValue;
330 
331       @Override
332       protected C computeNext() {
333         while (merged.hasNext()) {
334           C next = merged.next();
335           boolean duplicate =
336               lastValue != null && comparator.compare(next, lastValue) == 0;
337 
338           // Keep looping till we find a non-duplicate value.
339           if (!duplicate) {
340             lastValue = next;
341             return lastValue;
342           }
343         }
344 
345         lastValue = null; // clear reference to unused data
346         return endOfData();
347       }
348     };
349   }
350 
351   private static final long serialVersionUID = 0;
352 }
353