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