001    /*
002     * Copyright (C) 2008 The Guava Authors
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package com.google.common.collect;
018    
019    import static com.google.common.base.Preconditions.checkNotNull;
020    
021    import com.google.common.annotations.Beta;
022    import com.google.common.annotations.GwtCompatible;
023    import com.google.common.base.Function;
024    import com.google.common.base.Supplier;
025    
026    import java.io.Serializable;
027    import java.util.Comparator;
028    import java.util.Iterator;
029    import java.util.Map;
030    import java.util.PriorityQueue;
031    import java.util.Queue;
032    import java.util.Set;
033    import java.util.SortedMap;
034    import java.util.SortedSet;
035    import java.util.TreeMap;
036    
037    import javax.annotation.Nullable;
038    
039    /**
040     * Implementation of {@code Table} whose row keys and column keys are ordered
041     * by their natural ordering or by supplied comparators. When constructing a
042     * {@code TreeBasedTable}, you may provide comparators for the row keys and
043     * the column keys, or you may use natural ordering for both.
044     *
045     * <p>The {@link #rowKeySet} method returns a {@link SortedSet} and the {@link
046     * #rowMap} method returns a {@link SortedMap}, instead of the {@link Set} and
047     * {@link Map} specified by the {@link Table} interface.
048     *
049     * <p>The views returned by {@link #column}, {@link #columnKeySet()}, and {@link
050     * #columnMap()} have iterators that don't support {@code remove()}. Otherwise,
051     * all optional operations are supported. Null row keys, columns keys, and
052     * values are not supported.
053     *
054     * <p>Lookups by row key are often faster than lookups by column key, because
055     * the data is stored in a {@code Map<R, Map<C, V>>}. A method call like {@code
056     * column(columnKey).get(rowKey)} still runs quickly, since the row key is
057     * provided. However, {@code column(columnKey).size()} takes longer, since an
058     * iteration across all row keys occurs.
059     *
060     * <p>Note that this implementation is not synchronized. If multiple threads
061     * access this table concurrently and one of the threads modifies the table, it
062     * must be synchronized externally.
063     *
064     * @author Jared Levy
065     * @since 7
066     */
067    @GwtCompatible(serializable = true)
068    @Beta
069    public class TreeBasedTable<R, C, V> extends StandardRowSortedTable<R, C, V> {
070      private final Comparator<? super C> columnComparator;
071    
072      private static class Factory<C, V>
073          implements Supplier<TreeMap<C, V>>, Serializable {
074        final Comparator<? super C> comparator;
075        Factory(Comparator<? super C> comparator) {
076          this.comparator = comparator;
077        }
078        @Override
079        public TreeMap<C, V> get() {
080          return new TreeMap<C, V>(comparator);
081        }
082        private static final long serialVersionUID = 0;
083      }
084    
085      /**
086       * Creates an empty {@code TreeBasedTable} that uses the natural orderings
087       * of both row and column keys.
088       *
089       * <p>The method signature specifies {@code R extends Comparable} with a raw
090       * {@link Comparable}, instead of {@code R extends Comparable<? super R>},
091       * and the same for {@code C}. That's necessary to support classes defined
092       * without generics.
093       */
094      @SuppressWarnings("unchecked") // eclipse doesn't like the raw Comparable
095      public static <R extends Comparable, C extends Comparable, V>
096          TreeBasedTable<R, C, V> create() {
097        return new TreeBasedTable<R, C, V>(Ordering.natural(),
098            Ordering.natural());
099      }
100    
101      /**
102       * Creates an empty {@code TreeBasedTable} that is ordered by the specified
103       * comparators.
104       *
105       * @param rowComparator the comparator that orders the row keys
106       * @param columnComparator the comparator that orders the column keys
107       */
108      public static <R, C, V> TreeBasedTable<R, C, V> create(
109          Comparator<? super R> rowComparator,
110          Comparator<? super C> columnComparator) {
111        checkNotNull(rowComparator);
112        checkNotNull(columnComparator);
113        return new TreeBasedTable<R, C, V>(rowComparator, columnComparator);
114      }
115    
116      /**
117       * Creates a {@code TreeBasedTable} with the same mappings and sort order
118       * as the specified {@code TreeBasedTable}.
119       */
120      public static <R, C, V> TreeBasedTable<R, C, V> create(
121          TreeBasedTable<R, C, ? extends V> table) {
122        TreeBasedTable<R, C, V> result
123            = new TreeBasedTable<R, C, V>(
124                table.rowComparator(), table.columnComparator());
125        result.putAll(table);
126        return result;
127      }
128    
129      TreeBasedTable(Comparator<? super R> rowComparator,
130          Comparator<? super C> columnComparator) {
131        super(new TreeMap<R, Map<C, V>>(rowComparator),
132            new Factory<C, V>(columnComparator));
133        this.columnComparator = columnComparator;
134      }
135    
136      // TODO(jlevy): Move to StandardRowSortedTable?
137    
138      /**
139       * Returns the comparator that orders the rows. With natural ordering,
140       * {@link Ordering#natural()} is returned.
141       */
142      public Comparator<? super R> rowComparator() {
143        return rowKeySet().comparator();
144      }
145    
146      /**
147       * Returns the comparator that orders the columns. With natural ordering,
148       * {@link Ordering#natural()} is returned.
149       */
150      public Comparator<? super C> columnComparator() {
151        return columnComparator;
152      }
153    
154      // rowKeySet() and rowMap() are defined here so they appear in the Javadoc.
155    
156      @Override public SortedSet<R> rowKeySet() {
157        return super.rowKeySet();
158      }
159    
160      @Override public SortedMap<R, Map<C, V>> rowMap() {
161        return super.rowMap();
162      }
163    
164      // Overriding so NullPointerTester test passes.
165    
166      @Override public boolean contains(
167          @Nullable Object rowKey, @Nullable Object columnKey) {
168        return super.contains(rowKey, columnKey);
169      }
170    
171      @Override public boolean containsColumn(@Nullable Object columnKey) {
172        return super.containsColumn(columnKey);
173      }
174    
175      @Override public boolean containsRow(@Nullable Object rowKey) {
176        return super.containsRow(rowKey);
177      }
178    
179      @Override public boolean containsValue(@Nullable Object value) {
180        return super.containsValue(value);
181      }
182    
183      @Override public V get(@Nullable Object rowKey, @Nullable Object columnKey) {
184        return super.get(rowKey, columnKey);
185      }
186    
187      @Override public boolean equals(@Nullable Object obj) {
188        return super.equals(obj);
189      }
190    
191      @Override public V remove(
192          @Nullable Object rowKey, @Nullable Object columnKey) {
193        return super.remove(rowKey, columnKey);
194      }
195    
196      /**
197       * Overridden column iterator to return columns values in globally sorted
198       * order.
199       */
200      @Override Iterator<C> createColumnKeyIterator() {
201        return new MergingIterator<C>(
202            Iterables.transform(backingMap.values(),
203                new Function<Map<C, V>, Iterator<C>>() {
204                    @Override
205                    public Iterator<C> apply(Map<C, V> input) {
206                      return input.keySet().iterator();
207                    }
208                  }), columnComparator());
209      }
210    
211      /**
212       * An iterator that performs a lazy N-way merge, calculating the next value
213       * each time the iterator is polled. This amortizes the sorting cost over the
214       * iteration and requires less memory than sorting all elements at once.
215       * Duplicate values are omitted.
216       *
217       * <p>Retrieving a single element takes approximately O(log(M)) time, where M
218       * is the number of iterators. (Retrieving all elements takes approximately
219       * O(N*log(M)) time, where N is the total number of elements.)
220       */
221      // TODO(user): Push this into OrderedIterators or somewhere more generic.
222      private static class MergingIterator<T> extends AbstractIterator<T> {
223        private final Queue<PeekingIterator<T>> queue;
224        private final Comparator<? super T> comparator;
225    
226        // The last value we returned, used for removing duplicate values.
227        private T lastValue = null;
228    
229        public MergingIterator(
230            Iterable<? extends Iterator<T>> iterators,
231            Comparator<? super T> itemComparator) {
232    //    checkNotNull(iterators, "iterators");
233    //    checkNotNull(comparator, "comparator");
234          this.comparator = itemComparator;
235    
236          // A comparator that's used by the heap, allowing the heap
237          // to be sorted based on the top of each iterator.
238          Comparator<PeekingIterator<T>> heapComparator =
239              new Comparator<PeekingIterator<T>>() {
240                @Override
241                public int compare(PeekingIterator<T> o1, PeekingIterator<T> o2) {
242                  return comparator.compare(o1.peek(), o2.peek());
243                }
244              };
245    
246          // Construct the heap with a minimum size of 1, because
247          // Because PriorityQueue will fail if it's 0.
248          queue = new PriorityQueue<PeekingIterator<T>>(
249              Math.max(1, Iterables.size(iterators)), heapComparator);
250          for (Iterator<T> iterator : iterators) {
251            if (iterator.hasNext()) {
252              queue.add(Iterators.peekingIterator(iterator));
253            }
254          }
255        }
256    
257        @Override protected T computeNext() {
258          while (!queue.isEmpty()) {
259            PeekingIterator<T> nextIter = queue.poll();
260    
261            T next = nextIter.next();
262            boolean duplicate =
263                lastValue != null
264                && comparator.compare(next, lastValue) == 0;
265    
266            if (nextIter.hasNext()) {
267              queue.add(nextIter);
268            }
269            // Keep looping till we find a non-duplicate value.
270            if (!duplicate) {
271              lastValue = next;
272              return lastValue;
273            }
274          }
275    
276          lastValue = null; // clear reference to unused data
277          return endOfData();
278        }
279      }
280    
281      private static final long serialVersionUID = 0;
282    }