Home | History | Annotate | Download | only in collect
      1 /*
      2  * Copyright (C) 2009 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.checkElementIndex;
     21 import static com.google.common.base.Preconditions.checkNotNull;
     22 
     23 import com.google.common.annotations.Beta;
     24 import com.google.common.annotations.GwtCompatible;
     25 import com.google.common.annotations.GwtIncompatible;
     26 import com.google.common.base.Objects;
     27 
     28 import java.io.Serializable;
     29 import java.lang.reflect.Array;
     30 import java.util.Arrays;
     31 import java.util.Collection;
     32 import java.util.Iterator;
     33 import java.util.List;
     34 import java.util.Map;
     35 import java.util.Set;
     36 
     37 import javax.annotation.Nullable;
     38 
     39 /**
     40  * Fixed-size {@link Table} implementation backed by a two-dimensional array.
     41  *
     42  * <p>The allowed row and column keys must be supplied when the table is
     43  * created. The table always contains a mapping for every row key / column pair.
     44  * The value corresponding to a given row and column is null unless another
     45  * value is provided.
     46  *
     47  * <p>The table's size is constant: the product of the number of supplied row
     48  * keys and the number of supplied column keys. The {@code remove} and {@code
     49  * clear} methods are not supported by the table or its views. The {@link
     50  * #erase} and {@link #eraseAll} methods may be used instead.
     51  *
     52  * <p>The ordering of the row and column keys provided when the table is
     53  * constructed determines the iteration ordering across rows and columns in the
     54  * table's views. None of the view iterators support {@link Iterator#remove}.
     55  * If the table is modified after an iterator is created, the iterator remains
     56  * valid.
     57  *
     58  * <p>This class requires less memory than the {@link HashBasedTable} and {@link
     59  * TreeBasedTable} implementations, except when the table is sparse.
     60  *
     61  * <p>Null row keys or column keys are not permitted.
     62  *
     63  * <p>This class provides methods involving the underlying array structure,
     64  * where the array indices correspond to the position of a row or column in the
     65  * lists of allowed keys and values. See the {@link #at}, {@link #set}, {@link
     66  * #toArray}, {@link #rowKeyList}, and {@link #columnKeyList} methods for more
     67  * details.
     68  *
     69  * <p>Note that this implementation is not synchronized. If multiple threads
     70  * access the same cell of an {@code ArrayTable} concurrently and one of the
     71  * threads modifies its value, there is no guarantee that the new value will be
     72  * fully visible to the other threads. To guarantee that modifications are
     73  * visible, synchronize access to the table. Unlike other {@code Table}
     74  * implementations, synchronization is unnecessary between a thread that writes
     75  * to one cell and a thread that reads from another.
     76  *
     77  * <p>See the Guava User Guide article on <a href=
     78  * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Table">
     79  * {@code Table}</a>.
     80  *
     81  * @author Jared Levy
     82  * @since 10.0
     83  */
     84 @Beta
     85 @GwtCompatible(emulated = true)
     86 public final class ArrayTable<R, C, V> extends AbstractTable<R, C, V> implements Serializable {
     87 
     88   /**
     89    * Creates an empty {@code ArrayTable}.
     90    *
     91    * @param rowKeys row keys that may be stored in the generated table
     92    * @param columnKeys column keys that may be stored in the generated table
     93    * @throws NullPointerException if any of the provided keys is null
     94    * @throws IllegalArgumentException if {@code rowKeys} or {@code columnKeys}
     95    *     contains duplicates or is empty
     96    */
     97   public static <R, C, V> ArrayTable<R, C, V> create(
     98       Iterable<? extends R> rowKeys, Iterable<? extends C> columnKeys) {
     99     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 }
    774