001/*
002 * Copyright (C) 2009 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
017package com.google.common.collect;
018
019import static com.google.common.base.Preconditions.checkArgument;
020import static com.google.common.base.Preconditions.checkElementIndex;
021import static com.google.common.base.Preconditions.checkNotNull;
022
023import com.google.common.annotations.Beta;
024import com.google.common.annotations.GwtCompatible;
025import com.google.common.annotations.GwtIncompatible;
026import com.google.common.base.Objects;
027
028import java.io.Serializable;
029import java.lang.reflect.Array;
030import java.util.Arrays;
031import java.util.Collection;
032import java.util.Iterator;
033import java.util.List;
034import java.util.Map;
035import java.util.Set;
036
037import javax.annotation.Nullable;
038
039/**
040 * Fixed-size {@link Table} implementation backed by a two-dimensional array.
041 *
042 * <p>The allowed row and column keys must be supplied when the table is
043 * created. The table always contains a mapping for every row key / column pair.
044 * The value corresponding to a given row and column is null unless another
045 * value is provided.
046 *
047 * <p>The table's size is constant: the product of the number of supplied row
048 * keys and the number of supplied column keys. The {@code remove} and {@code
049 * clear} methods are not supported by the table or its views. The {@link
050 * #erase} and {@link #eraseAll} methods may be used instead.
051 *
052 * <p>The ordering of the row and column keys provided when the table is
053 * constructed determines the iteration ordering across rows and columns in the
054 * table's views. None of the view iterators support {@link Iterator#remove}.
055 * If the table is modified after an iterator is created, the iterator remains
056 * valid.
057 *
058 * <p>This class requires less memory than the {@link HashBasedTable} and {@link
059 * TreeBasedTable} implementations, except when the table is sparse.
060 *
061 * <p>Null row keys or column keys are not permitted.
062 *
063 * <p>This class provides methods involving the underlying array structure,
064 * where the array indices correspond to the position of a row or column in the
065 * lists of allowed keys and values. See the {@link #at}, {@link #set}, {@link
066 * #toArray}, {@link #rowKeyList}, and {@link #columnKeyList} methods for more
067 * details.
068 *
069 * <p>Note that this implementation is not synchronized. If multiple threads
070 * access the same cell of an {@code ArrayTable} concurrently and one of the
071 * threads modifies its value, there is no guarantee that the new value will be
072 * fully visible to the other threads. To guarantee that modifications are
073 * visible, synchronize access to the table. Unlike other {@code Table}
074 * implementations, synchronization is unnecessary between a thread that writes
075 * to one cell and a thread that reads from another.
076 *
077 * <p>See the Guava User Guide article on <a href=
078 * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Table">
079 * {@code Table}</a>.
080 *
081 * @author Jared Levy
082 * @since 10.0
083 */
084@Beta
085@GwtCompatible(emulated = true)
086public final class ArrayTable<R, C, V> extends AbstractTable<R, C, V> implements Serializable {
087
088  /**
089   * Creates an empty {@code ArrayTable}.
090   *
091   * @param rowKeys row keys that may be stored in the generated table
092   * @param columnKeys column keys that may be stored in the generated table
093   * @throws NullPointerException if any of the provided keys is null
094   * @throws IllegalArgumentException if {@code rowKeys} or {@code columnKeys}
095   *     contains duplicates or is empty
096   */
097  public static <R, C, V> ArrayTable<R, C, V> create(
098      Iterable<? extends R> rowKeys, Iterable<? extends C> columnKeys) {
099    return new ArrayTable<R, C, V>(rowKeys, columnKeys);
100  }
101
102  /*
103   * TODO(jlevy): Add factory methods taking an Enum class, instead of an
104   * iterable, to specify the allowed row keys and/or column keys. Note that
105   * custom serialization logic is needed to support different enum sizes during
106   * serialization and deserialization.
107   */
108
109  /**
110   * Creates an {@code ArrayTable} with the mappings in the provided table.
111   *
112   * <p>If {@code table} includes a mapping with row key {@code r} and a
113   * separate mapping with column key {@code c}, the returned table contains a
114   * mapping with row key {@code r} and column key {@code c}. If that row key /
115   * column key pair in not in {@code table}, the pair maps to {@code null} in
116   * the generated table.
117   *
118   * <p>The returned table allows subsequent {@code put} calls with the row keys
119   * in {@code table.rowKeySet()} and the column keys in {@code
120   * table.columnKeySet()}. Calling {@link #put} with other keys leads to an
121   * {@code IllegalArgumentException}.
122   *
123   * <p>The ordering of {@code table.rowKeySet()} and {@code
124   * table.columnKeySet()} determines the row and column iteration ordering of
125   * the returned table.
126   *
127   * @throws NullPointerException if {@code table} has a null key
128   * @throws IllegalArgumentException if the provided table is empty
129   */
130  public static <R, C, V> ArrayTable<R, C, V> create(Table<R, C, V> table) {
131    return (table instanceof ArrayTable<?, ?, ?>)
132      ? new ArrayTable<R, C, V>((ArrayTable<R, C, V>) table)
133      : new ArrayTable<R, C, V>(table);
134  }
135
136  private final ImmutableList<R> rowList;
137  private final ImmutableList<C> columnList;
138
139  // TODO(jlevy): Add getters returning rowKeyToIndex and columnKeyToIndex?
140  private final ImmutableMap<R, Integer> rowKeyToIndex;
141  private final ImmutableMap<C, Integer> columnKeyToIndex;
142  private final V[][] array;
143
144  private ArrayTable(Iterable<? extends R> rowKeys,
145      Iterable<? extends C> columnKeys) {
146    this.rowList = ImmutableList.copyOf(rowKeys);
147    this.columnList = ImmutableList.copyOf(columnKeys);
148    checkArgument(!rowList.isEmpty());
149    checkArgument(!columnList.isEmpty());
150
151    /*
152     * TODO(jlevy): Support empty rowKeys or columnKeys? If we do, when
153     * columnKeys is empty but rowKeys isn't, the table is empty but
154     * containsRow() can return true and rowKeySet() isn't empty.
155     */
156    rowKeyToIndex = index(rowList);
157    columnKeyToIndex = index(columnList);
158
159    @SuppressWarnings("unchecked")
160    V[][] tmpArray
161        = (V[][]) new Object[rowList.size()][columnList.size()];
162    array = tmpArray;
163    // Necessary because in GWT the arrays are initialized with "undefined" instead of null.
164    eraseAll();
165  }
166
167  private static <E> ImmutableMap<E, Integer> index(List<E> list) {
168    ImmutableMap.Builder<E, Integer> columnBuilder = ImmutableMap.builder();
169    for (int i = 0; i < list.size(); i++) {
170      columnBuilder.put(list.get(i), i);
171    }
172    return columnBuilder.build();
173  }
174
175  private ArrayTable(Table<R, C, V> table) {
176    this(table.rowKeySet(), table.columnKeySet());
177    putAll(table);
178  }
179
180  private ArrayTable(ArrayTable<R, C, V> table) {
181    rowList = table.rowList;
182    columnList = table.columnList;
183    rowKeyToIndex = table.rowKeyToIndex;
184    columnKeyToIndex = table.columnKeyToIndex;
185    @SuppressWarnings("unchecked")
186    V[][] copy = (V[][]) new Object[rowList.size()][columnList.size()];
187    array = copy;
188    // Necessary because in GWT the arrays are initialized with "undefined" instead of null.
189    eraseAll();
190    for (int i = 0; i < rowList.size(); i++) {
191      System.arraycopy(table.array[i], 0, copy[i], 0, table.array[i].length);
192    }
193  }
194
195  private abstract static class ArrayMap<K, V> extends Maps.ImprovedAbstractMap<K, V> {
196    private final ImmutableMap<K, Integer> keyIndex;
197
198    private ArrayMap(ImmutableMap<K, Integer> keyIndex) {
199      this.keyIndex = keyIndex;
200    }
201
202    @Override
203    public Set<K> keySet() {
204      return keyIndex.keySet();
205    }
206
207    K getKey(int index) {
208      return keyIndex.keySet().asList().get(index);
209    }
210
211    abstract String getKeyRole();
212
213    @Nullable abstract V getValue(int index);
214
215    @Nullable abstract V setValue(int index, V newValue);
216
217    @Override
218    public int size() {
219      return keyIndex.size();
220    }
221
222    @Override
223    public boolean isEmpty() {
224      return keyIndex.isEmpty();
225    }
226
227    @Override
228    protected Set<Entry<K, V>> createEntrySet() {
229      return new Maps.EntrySet<K, V>() {
230        @Override
231        Map<K, V> map() {
232          return ArrayMap.this;
233        }
234
235        @Override
236        public Iterator<Entry<K, V>> iterator() {
237          return new AbstractIndexedListIterator<Entry<K, V>>(size()) {
238            @Override
239            protected Entry<K, V> get(final int index) {
240              return new AbstractMapEntry<K, V>() {
241                @Override
242                public K getKey() {
243                  return ArrayMap.this.getKey(index);
244                }
245
246                @Override
247                public V getValue() {
248                  return ArrayMap.this.getValue(index);
249                }
250
251                @Override
252                public V setValue(V value) {
253                  return ArrayMap.this.setValue(index, value);
254                }
255              };
256            }
257          };
258        }
259      };
260    }
261
262    // TODO(user): consider an optimized values() implementation
263
264    @Override
265    public boolean containsKey(@Nullable Object key) {
266      return keyIndex.containsKey(key);
267    }
268
269    @Override
270    public V get(@Nullable Object key) {
271      Integer index = keyIndex.get(key);
272      if (index == null) {
273        return null;
274      } else {
275        return getValue(index);
276      }
277    }
278
279    @Override
280    public V put(K key, V value) {
281      Integer index = keyIndex.get(key);
282      if (index == null) {
283        throw new IllegalArgumentException(
284            getKeyRole() + " " + key + " not in " + keyIndex.keySet());
285      }
286      return setValue(index, value);
287    }
288
289    @Override
290    public V remove(Object key) {
291      throw new UnsupportedOperationException();
292    }
293
294    @Override
295    public void clear() {
296      throw new UnsupportedOperationException();
297    }
298  }
299
300  /**
301   * Returns, as an immutable list, the row keys provided when the table was
302   * constructed, including those that are mapped to null values only.
303   */
304  public ImmutableList<R> rowKeyList() {
305    return rowList;
306  }
307
308  /**
309   * Returns, as an immutable list, the column keys provided when the table was
310   * constructed, including those that are mapped to null values only.
311   */
312  public ImmutableList<C> columnKeyList() {
313    return columnList;
314  }
315
316  /**
317   * Returns the value corresponding to the specified row and column indices.
318   * The same value is returned by {@code
319   * get(rowKeyList().get(rowIndex), columnKeyList().get(columnIndex))}, but
320   * this method runs more quickly.
321   *
322   * @param rowIndex position of the row key in {@link #rowKeyList()}
323   * @param columnIndex position of the row key in {@link #columnKeyList()}
324   * @return the value with the specified row and column
325   * @throws IndexOutOfBoundsException if either index is negative, {@code
326   *     rowIndex} is greater then or equal to the number of allowed row keys,
327   *     or {@code columnIndex} is greater then or equal to the number of
328   *     allowed column keys
329   */
330  public V at(int rowIndex, int columnIndex) {
331    // In GWT array access never throws IndexOutOfBoundsException.
332    checkElementIndex(rowIndex, rowList.size());
333    checkElementIndex(columnIndex, columnList.size());
334    return array[rowIndex][columnIndex];
335  }
336
337  /**
338   * Associates {@code value} with the specified row and column indices. The
339   * logic {@code
340   * put(rowKeyList().get(rowIndex), columnKeyList().get(columnIndex), value)}
341   * has the same behavior, but this method runs more quickly.
342   *
343   * @param rowIndex position of the row key in {@link #rowKeyList()}
344   * @param columnIndex position of the row key in {@link #columnKeyList()}
345   * @param value value to store in the table
346   * @return the previous value with the specified row and column
347   * @throws IndexOutOfBoundsException if either index is negative, {@code
348   *     rowIndex} is greater then or equal to the number of allowed row keys,
349   *     or {@code columnIndex} is greater then or equal to the number of
350   *     allowed column keys
351   */
352  public V set(int rowIndex, int columnIndex, @Nullable V value) {
353    // In GWT array access never throws IndexOutOfBoundsException.
354    checkElementIndex(rowIndex, rowList.size());
355    checkElementIndex(columnIndex, columnList.size());
356    V oldValue = array[rowIndex][columnIndex];
357    array[rowIndex][columnIndex] = value;
358    return oldValue;
359  }
360
361  /**
362   * Returns a two-dimensional array with the table contents. The row and column
363   * indices correspond to the positions of the row and column in the iterables
364   * provided during table construction. If the table lacks a mapping for a
365   * given row and column, the corresponding array element is null.
366   *
367   * <p>Subsequent table changes will not modify the array, and vice versa.
368   *
369   * @param valueClass class of values stored in the returned array
370   */
371  @GwtIncompatible("reflection")
372  public V[][] toArray(Class<V> valueClass) {
373    // Can change to use varargs in JDK 1.6 if we want
374    @SuppressWarnings("unchecked") // TODO: safe?
375    V[][] copy = (V[][]) Array.newInstance(
376        valueClass, new int[] { rowList.size(), columnList.size() });
377    for (int i = 0; i < rowList.size(); i++) {
378      System.arraycopy(array[i], 0, copy[i], 0, array[i].length);
379    }
380    return copy;
381  }
382
383  /**
384   * Not supported. Use {@link #eraseAll} instead.
385   *
386   * @throws UnsupportedOperationException always
387   * @deprecated Use {@link #eraseAll}
388   */
389  @Override
390  @Deprecated public void clear() {
391    throw new UnsupportedOperationException();
392  }
393
394  /**
395   * Associates the value {@code null} with every pair of allowed row and column
396   * keys.
397   */
398  public void eraseAll() {
399    for (V[] row : array) {
400      Arrays.fill(row, null);
401    }
402  }
403
404  /**
405   * Returns {@code true} if the provided keys are among the keys provided when
406   * the table was constructed.
407   */
408  @Override
409  public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) {
410    return containsRow(rowKey) && containsColumn(columnKey);
411  }
412
413  /**
414   * Returns {@code true} if the provided column key is among the column keys
415   * provided when the table was constructed.
416   */
417  @Override
418  public boolean containsColumn(@Nullable Object columnKey) {
419    return columnKeyToIndex.containsKey(columnKey);
420  }
421
422  /**
423   * Returns {@code true} if the provided row key is among the row keys
424   * provided when the table was constructed.
425   */
426  @Override
427  public boolean containsRow(@Nullable Object rowKey) {
428    return rowKeyToIndex.containsKey(rowKey);
429  }
430
431  @Override
432  public boolean containsValue(@Nullable Object value) {
433    for (V[] row : array) {
434      for (V element : row) {
435        if (Objects.equal(value, element)) {
436          return true;
437        }
438      }
439    }
440    return false;
441  }
442
443  @Override
444  public V get(@Nullable Object rowKey, @Nullable Object columnKey) {
445    Integer rowIndex = rowKeyToIndex.get(rowKey);
446    Integer columnIndex = columnKeyToIndex.get(columnKey);
447    return (rowIndex == null || columnIndex == null)
448        ? null : at(rowIndex, columnIndex);
449  }
450
451  /**
452   * Always returns {@code false}.
453   */
454  @Override
455  public boolean isEmpty() {
456    return false;
457  }
458
459  /**
460   * {@inheritDoc}
461   *
462   * @throws IllegalArgumentException if {@code rowKey} is not in {@link
463   *     #rowKeySet()} or {@code columnKey} is not in {@link #columnKeySet()}.
464   */
465  @Override
466  public V put(R rowKey, C columnKey, @Nullable V value) {
467    checkNotNull(rowKey);
468    checkNotNull(columnKey);
469    Integer rowIndex = rowKeyToIndex.get(rowKey);
470    checkArgument(rowIndex != null, "Row %s not in %s", rowKey, rowList);
471    Integer columnIndex = columnKeyToIndex.get(columnKey);
472    checkArgument(columnIndex != null,
473        "Column %s not in %s", columnKey, columnList);
474    return set(rowIndex, columnIndex, value);
475  }
476
477  /*
478   * TODO(jlevy): Consider creating a merge() method, similar to putAll() but
479   * copying non-null values only.
480   */
481
482  /**
483   * {@inheritDoc}
484   *
485   * <p>If {@code table} is an {@code ArrayTable}, its null values will be
486   * stored in this table, possibly replacing values that were previously
487   * non-null.
488   *
489   * @throws NullPointerException if {@code table} has a null key
490   * @throws IllegalArgumentException if any of the provided table's row keys or
491   *     column keys is not in {@link #rowKeySet()} or {@link #columnKeySet()}
492   */
493  @Override
494  public void putAll(Table<? extends R, ? extends C, ? extends V> table) {
495    super.putAll(table);
496  }
497
498  /**
499   * Not supported. Use {@link #erase} instead.
500   *
501   * @throws UnsupportedOperationException always
502   * @deprecated Use {@link #erase}
503   */
504  @Override
505  @Deprecated public V remove(Object rowKey, Object columnKey) {
506    throw new UnsupportedOperationException();
507  }
508
509  /**
510   * Associates the value {@code null} with the specified keys, assuming both
511   * keys are valid. If either key is null or isn't among the keys provided
512   * during construction, this method has no effect.
513   *
514   * <p>This method is equivalent to {@code put(rowKey, columnKey, null)} when
515   * both provided keys are valid.
516   *
517   * @param rowKey row key of mapping to be erased
518   * @param columnKey column key of mapping to be erased
519   * @return the value previously associated with the keys, or {@code null} if
520   *     no mapping existed for the keys
521   */
522  public V erase(@Nullable Object rowKey, @Nullable Object columnKey) {
523    Integer rowIndex = rowKeyToIndex.get(rowKey);
524    Integer columnIndex = columnKeyToIndex.get(columnKey);
525    if (rowIndex == null || columnIndex == null) {
526      return null;
527    }
528    return set(rowIndex, columnIndex, null);
529  }
530
531  // TODO(jlevy): Add eraseRow and eraseColumn methods?
532
533  @Override
534  public int size() {
535    return rowList.size() * columnList.size();
536  }
537
538  /**
539   * Returns an unmodifiable set of all row key / column key / value
540   * triplets. Changes to the table will update the returned set.
541   *
542   * <p>The returned set's iterator traverses the mappings with the first row
543   * key, the mappings with the second row key, and so on.
544   *
545   * <p>The value in the returned cells may change if the table subsequently
546   * changes.
547   *
548   * @return set of table cells consisting of row key / column key / value
549   *     triplets
550   */
551  @Override
552  public Set<Cell<R, C, V>> cellSet() {
553    return super.cellSet();
554  }
555
556  @Override
557  Iterator<Cell<R, C, V>> cellIterator() {
558    return new AbstractIndexedListIterator<Cell<R, C, V>>(size()) {
559      @Override protected Cell<R, C, V> get(final int index) {
560        return new Tables.AbstractCell<R, C, V>() {
561          final int rowIndex = index / columnList.size();
562          final int columnIndex = index % columnList.size();
563          @Override
564          public R getRowKey() {
565            return rowList.get(rowIndex);
566          }
567          @Override
568          public C getColumnKey() {
569            return columnList.get(columnIndex);
570          }
571          @Override
572          public V getValue() {
573            return at(rowIndex, columnIndex);
574          }
575        };
576      }
577    };
578  }
579
580  /**
581   * Returns a view of all mappings that have the given column key. If the
582   * column key isn't in {@link #columnKeySet()}, an empty immutable map is
583   * returned.
584   *
585   * <p>Otherwise, for each row key in {@link #rowKeySet()}, the returned map
586   * associates the row key with the corresponding value in the table. Changes
587   * to the returned map will update the underlying table, and vice versa.
588   *
589   * @param columnKey key of column to search for in the table
590   * @return the corresponding map from row keys to values
591   */
592  @Override
593  public Map<R, V> column(C columnKey) {
594    checkNotNull(columnKey);
595    Integer columnIndex = columnKeyToIndex.get(columnKey);
596    return (columnIndex == null)
597        ? ImmutableMap.<R, V>of() : new Column(columnIndex);
598  }
599
600  private class Column extends ArrayMap<R, V> {
601    final int columnIndex;
602
603    Column(int columnIndex) {
604      super(rowKeyToIndex);
605      this.columnIndex = columnIndex;
606    }
607
608    @Override
609    String getKeyRole() {
610      return "Row";
611    }
612
613    @Override
614    V getValue(int index) {
615      return at(index, columnIndex);
616    }
617
618    @Override
619    V setValue(int index, V newValue) {
620      return set(index, columnIndex, newValue);
621    }
622  }
623
624  /**
625   * Returns an immutable set of the valid column keys, including those that
626   * are associated with null values only.
627   *
628   * @return immutable set of column keys
629   */
630  @Override
631  public ImmutableSet<C> columnKeySet() {
632    return columnKeyToIndex.keySet();
633  }
634
635  private transient ColumnMap columnMap;
636
637  @Override
638  public Map<C, Map<R, V>> columnMap() {
639    ColumnMap map = columnMap;
640    return (map == null) ? columnMap = new ColumnMap() : map;
641  }
642
643  private class ColumnMap extends ArrayMap<C, Map<R, V>> {
644    private ColumnMap() {
645      super(columnKeyToIndex);
646    }
647
648    @Override
649    String getKeyRole() {
650      return "Column";
651    }
652
653    @Override
654    Map<R, V> getValue(int index) {
655      return new Column(index);
656    }
657
658    @Override
659    Map<R, V> setValue(int index, Map<R, V> newValue) {
660      throw new UnsupportedOperationException();
661    }
662
663    @Override
664    public Map<R, V> put(C key, Map<R, V> value) {
665      throw new UnsupportedOperationException();
666    }
667  }
668
669  /**
670   * Returns a view of all mappings that have the given row key. If the
671   * row key isn't in {@link #rowKeySet()}, an empty immutable map is
672   * returned.
673   *
674   * <p>Otherwise, for each column key in {@link #columnKeySet()}, the returned
675   * map associates the column key with the corresponding value in the
676   * table. Changes to the returned map will update the underlying table, and
677   * vice versa.
678   *
679   * @param rowKey key of row to search for in the table
680   * @return the corresponding map from column keys to values
681   */
682  @Override
683  public Map<C, V> row(R rowKey) {
684    checkNotNull(rowKey);
685    Integer rowIndex = rowKeyToIndex.get(rowKey);
686    return (rowIndex == null) ? ImmutableMap.<C, V>of() : new Row(rowIndex);
687  }
688
689  private class Row extends ArrayMap<C, V> {
690    final int rowIndex;
691
692    Row(int rowIndex) {
693      super(columnKeyToIndex);
694      this.rowIndex = rowIndex;
695    }
696
697    @Override
698    String getKeyRole() {
699      return "Column";
700    }
701
702    @Override
703    V getValue(int index) {
704      return at(rowIndex, index);
705    }
706
707    @Override
708    V setValue(int index, V newValue) {
709      return set(rowIndex, index, newValue);
710    }
711  }
712
713  /**
714   * Returns an immutable set of the valid row keys, including those that are
715   * associated with null values only.
716   *
717   * @return immutable set of row keys
718   */
719  @Override
720  public ImmutableSet<R> rowKeySet() {
721    return rowKeyToIndex.keySet();
722  }
723
724  private transient RowMap rowMap;
725
726  @Override
727  public Map<R, Map<C, V>> rowMap() {
728    RowMap map = rowMap;
729    return (map == null) ? rowMap = new RowMap() : map;
730  }
731
732  private class RowMap extends ArrayMap<R, Map<C, V>> {
733    private RowMap() {
734      super(rowKeyToIndex);
735    }
736
737    @Override
738    String getKeyRole() {
739      return "Row";
740    }
741
742    @Override
743    Map<C, V> getValue(int index) {
744      return new Row(index);
745    }
746
747    @Override
748    Map<C, V> setValue(int index, Map<C, V> newValue) {
749      throw new UnsupportedOperationException();
750    }
751
752    @Override
753    public Map<C, V> put(R key, Map<C, V> value) {
754      throw new UnsupportedOperationException();
755    }
756  }
757
758  /**
759   * Returns an unmodifiable collection of all values, which may contain
760   * duplicates. Changes to the table will update the returned collection.
761   *
762   * <p>The returned collection's iterator traverses the values of the first row
763   * key, the values of the second row key, and so on.
764   *
765   * @return collection of values
766   */
767  @Override
768  public Collection<V> values() {
769    return super.values();
770  }
771
772  private static final long serialVersionUID = 0;
773}