Home | History | Annotate | Download | only in collect
      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;
     85     Factory(Comparator<? super C> comparator) {
     86       this.comparator = comparator;
     87     }
     88     @Override
     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>
    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    */
    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    */
    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 
    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    */
    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    */
    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
    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 
    184     TreeRow(R rowKey) {
    185       this(rowKey, null, null);
    186     }
    187 
    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 
    196     @Override public SortedSet<C> keySet() {
    197       return new Maps.SortedKeySet<C, V>(this);
    198     }
    199 
    200     @Override public Comparator<? super C> comparator() {
    201       return columnComparator();
    202     }
    203 
    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 
    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 
    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 
    222     @Override public SortedMap<C, V> headMap(C toKey) {
    223       checkArgument(rangeContains(checkNotNull(toKey)));
    224       return new TreeRow(rowKey, lowerBound, toKey);
    225     }
    226 
    227     @Override public SortedMap<C, V> tailMap(C fromKey) {
    228       checkArgument(rangeContains(checkNotNull(fromKey)));
    229       return new TreeRow(rowKey, fromKey, upperBound);
    230     }
    231 
    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 
    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      */
    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
    263     SortedMap<C, V> backingRowMap() {
    264       return (SortedMap<C, V>) super.backingRowMap();
    265     }
    266 
    267     @Override
    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
    283     void maintainEmptyInvariant() {
    284       if (wholeRow() != null && wholeRow.isEmpty()) {
    285         backingMap.remove(rowKey);
    286         wholeRow = null;
    287         backingRowMap = null;
    288       }
    289     }
    290 
    291     @Override public boolean containsKey(Object key) {
    292       return rangeContains(key) && super.containsKey(key);
    293     }
    294 
    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 
    303   @Override public SortedSet<R> rowKeySet() {
    304     return super.rowKeySet();
    305   }
    306 
    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
    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