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