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