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