001/* 002 * Copyright (C) 2008 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.checkNotNull; 021import static com.google.common.collect.NullnessCasts.uncheckedCastNullableTToT; 022 023import com.google.common.annotations.GwtCompatible; 024import com.google.common.base.Function; 025import com.google.common.base.Objects; 026import com.google.common.base.Supplier; 027import com.google.common.collect.Table.Cell; 028import java.io.Serializable; 029import java.util.Collection; 030import java.util.Collections; 031import java.util.Iterator; 032import java.util.Map; 033import java.util.Set; 034import java.util.SortedMap; 035import java.util.SortedSet; 036import java.util.Spliterator; 037import java.util.function.BinaryOperator; 038import java.util.stream.Collector; 039import javax.annotation.CheckForNull; 040import org.checkerframework.checker.nullness.qual.Nullable; 041 042/** 043 * Provides static methods that involve a {@code Table}. 044 * 045 * <p>See the Guava User Guide article on <a href= 046 * "https://github.com/google/guava/wiki/CollectionUtilitiesExplained#tables">{@code Tables}</a>. 047 * 048 * @author Jared Levy 049 * @author Louis Wasserman 050 * @since 7.0 051 */ 052@GwtCompatible 053@ElementTypesAreNonnullByDefault 054public final class Tables { 055 private Tables() {} 056 057 /** 058 * Returns a {@link Collector} that accumulates elements into a {@code Table} created using the 059 * specified supplier, whose cells are generated by applying the provided mapping functions to the 060 * input elements. Cells are inserted into the generated {@code Table} in encounter order. 061 * 062 * <p>If multiple input elements map to the same row and column, an {@code IllegalStateException} 063 * is thrown when the collection operation is performed. 064 * 065 * <p>To collect to an {@link ImmutableTable}, use {@link ImmutableTable#toImmutableTable}. 066 * 067 * @since 21.0 068 */ 069 public static < 070 T extends @Nullable Object, 071 R extends @Nullable Object, 072 C extends @Nullable Object, 073 V extends @Nullable Object, 074 I extends Table<R, C, V>> 075 Collector<T, ?, I> toTable( 076 java.util.function.Function<? super T, ? extends R> rowFunction, 077 java.util.function.Function<? super T, ? extends C> columnFunction, 078 java.util.function.Function<? super T, ? extends V> valueFunction, 079 java.util.function.Supplier<I> tableSupplier) { 080 return TableCollectors.toTable(rowFunction, columnFunction, valueFunction, tableSupplier); 081 } 082 083 /** 084 * Returns a {@link Collector} that accumulates elements into a {@code Table} created using the 085 * specified supplier, whose cells are generated by applying the provided mapping functions to the 086 * input elements. Cells are inserted into the generated {@code Table} in encounter order. 087 * 088 * <p>If multiple input elements map to the same row and column, the specified merging function is 089 * used to combine the values. Like {@link 090 * java.util.stream.Collectors#toMap(java.util.function.Function, java.util.function.Function, 091 * BinaryOperator, java.util.function.Supplier)}, this Collector throws a {@code 092 * NullPointerException} on null values returned from {@code valueFunction}, and treats nulls 093 * returned from {@code mergeFunction} as removals of that row/column pair. 094 * 095 * @since 21.0 096 */ 097 public static < 098 T extends @Nullable Object, 099 R extends @Nullable Object, 100 C extends @Nullable Object, 101 V extends @Nullable Object, 102 I extends Table<R, C, V>> 103 Collector<T, ?, I> toTable( 104 java.util.function.Function<? super T, ? extends R> rowFunction, 105 java.util.function.Function<? super T, ? extends C> columnFunction, 106 java.util.function.Function<? super T, ? extends V> valueFunction, 107 BinaryOperator<V> mergeFunction, 108 java.util.function.Supplier<I> tableSupplier) { 109 return TableCollectors.toTable( 110 rowFunction, columnFunction, valueFunction, mergeFunction, tableSupplier); 111 } 112 113 /** 114 * Returns an immutable cell with the specified row key, column key, and value. 115 * 116 * <p>The returned cell is serializable. 117 * 118 * @param rowKey the row key to be associated with the returned cell 119 * @param columnKey the column key to be associated with the returned cell 120 * @param value the value to be associated with the returned cell 121 */ 122 public static <R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object> 123 Cell<R, C, V> immutableCell( 124 @ParametricNullness R rowKey, 125 @ParametricNullness C columnKey, 126 @ParametricNullness V value) { 127 return new ImmutableCell<>(rowKey, columnKey, value); 128 } 129 130 static final class ImmutableCell< 131 R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object> 132 extends AbstractCell<R, C, V> implements Serializable { 133 @ParametricNullness private final R rowKey; 134 @ParametricNullness private final C columnKey; 135 @ParametricNullness private final V value; 136 137 ImmutableCell( 138 @ParametricNullness R rowKey, 139 @ParametricNullness C columnKey, 140 @ParametricNullness V value) { 141 this.rowKey = rowKey; 142 this.columnKey = columnKey; 143 this.value = value; 144 } 145 146 @Override 147 @ParametricNullness 148 public R getRowKey() { 149 return rowKey; 150 } 151 152 @Override 153 @ParametricNullness 154 public C getColumnKey() { 155 return columnKey; 156 } 157 158 @Override 159 @ParametricNullness 160 public V getValue() { 161 return value; 162 } 163 164 private static final long serialVersionUID = 0; 165 } 166 167 abstract static class AbstractCell< 168 R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object> 169 implements Cell<R, C, V> { 170 // needed for serialization 171 AbstractCell() {} 172 173 @Override 174 public boolean equals(@CheckForNull Object obj) { 175 if (obj == this) { 176 return true; 177 } 178 if (obj instanceof Cell) { 179 Cell<?, ?, ?> other = (Cell<?, ?, ?>) obj; 180 return Objects.equal(getRowKey(), other.getRowKey()) 181 && Objects.equal(getColumnKey(), other.getColumnKey()) 182 && Objects.equal(getValue(), other.getValue()); 183 } 184 return false; 185 } 186 187 @Override 188 public int hashCode() { 189 return Objects.hashCode(getRowKey(), getColumnKey(), getValue()); 190 } 191 192 @Override 193 public String toString() { 194 return "(" + getRowKey() + "," + getColumnKey() + ")=" + getValue(); 195 } 196 } 197 198 /** 199 * Creates a transposed view of a given table that flips its row and column keys. In other words, 200 * calling {@code get(columnKey, rowKey)} on the generated table always returns the same value as 201 * calling {@code get(rowKey, columnKey)} on the original table. Updating the original table 202 * changes the contents of the transposed table and vice versa. 203 * 204 * <p>The returned table supports update operations as long as the input table supports the 205 * analogous operation with swapped rows and columns. For example, in a {@link HashBasedTable} 206 * instance, {@code rowKeySet().iterator()} supports {@code remove()} but {@code 207 * columnKeySet().iterator()} doesn't. With a transposed {@link HashBasedTable}, it's the other 208 * way around. 209 */ 210 public static <R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object> 211 Table<C, R, V> transpose(Table<R, C, V> table) { 212 return (table instanceof TransposeTable) 213 ? ((TransposeTable<R, C, V>) table).original 214 : new TransposeTable<C, R, V>(table); 215 } 216 217 private static class TransposeTable< 218 C extends @Nullable Object, R extends @Nullable Object, V extends @Nullable Object> 219 extends AbstractTable<C, R, V> { 220 final Table<R, C, V> original; 221 222 TransposeTable(Table<R, C, V> original) { 223 this.original = checkNotNull(original); 224 } 225 226 @Override 227 public void clear() { 228 original.clear(); 229 } 230 231 @Override 232 public Map<C, V> column(@ParametricNullness R columnKey) { 233 return original.row(columnKey); 234 } 235 236 @Override 237 public Set<R> columnKeySet() { 238 return original.rowKeySet(); 239 } 240 241 @Override 242 public Map<R, Map<C, V>> columnMap() { 243 return original.rowMap(); 244 } 245 246 @Override 247 public boolean contains(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { 248 return original.contains(columnKey, rowKey); 249 } 250 251 @Override 252 public boolean containsColumn(@CheckForNull Object columnKey) { 253 return original.containsRow(columnKey); 254 } 255 256 @Override 257 public boolean containsRow(@CheckForNull Object rowKey) { 258 return original.containsColumn(rowKey); 259 } 260 261 @Override 262 public boolean containsValue(@CheckForNull Object value) { 263 return original.containsValue(value); 264 } 265 266 @Override 267 @CheckForNull 268 public V get(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { 269 return original.get(columnKey, rowKey); 270 } 271 272 @Override 273 @CheckForNull 274 public V put( 275 @ParametricNullness C rowKey, 276 @ParametricNullness R columnKey, 277 @ParametricNullness V value) { 278 return original.put(columnKey, rowKey, value); 279 } 280 281 @Override 282 public void putAll(Table<? extends C, ? extends R, ? extends V> table) { 283 original.putAll(transpose(table)); 284 } 285 286 @Override 287 @CheckForNull 288 public V remove(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { 289 return original.remove(columnKey, rowKey); 290 } 291 292 @Override 293 public Map<R, V> row(@ParametricNullness C rowKey) { 294 return original.column(rowKey); 295 } 296 297 @Override 298 public Set<C> rowKeySet() { 299 return original.columnKeySet(); 300 } 301 302 @Override 303 public Map<C, Map<R, V>> rowMap() { 304 return original.columnMap(); 305 } 306 307 @Override 308 public int size() { 309 return original.size(); 310 } 311 312 @Override 313 public Collection<V> values() { 314 return original.values(); 315 } 316 317 // Will cast TRANSPOSE_CELL to a type that always succeeds 318 private static final Function TRANSPOSE_CELL = 319 new Function<Cell<?, ?, ?>, Cell<?, ?, ?>>() { 320 @Override 321 public Cell<?, ?, ?> apply(Cell<?, ?, ?> cell) { 322 return immutableCell(cell.getColumnKey(), cell.getRowKey(), cell.getValue()); 323 } 324 }; 325 326 @SuppressWarnings("unchecked") 327 @Override 328 Iterator<Cell<C, R, V>> cellIterator() { 329 return Iterators.transform( 330 original.cellSet().iterator(), (Function<Cell<R, C, V>, Cell<C, R, V>>) TRANSPOSE_CELL); 331 } 332 333 @SuppressWarnings("unchecked") 334 @Override 335 Spliterator<Cell<C, R, V>> cellSpliterator() { 336 return CollectSpliterators.map( 337 original.cellSet().spliterator(), 338 (Function<Cell<R, C, V>, Cell<C, R, V>>) TRANSPOSE_CELL); 339 } 340 } 341 342 /** 343 * Creates a table that uses the specified backing map and factory. It can generate a table based 344 * on arbitrary {@link Map} classes. 345 * 346 * <p>The {@code factory}-generated and {@code backingMap} classes determine the table iteration 347 * order. However, the table's {@code row()} method returns instances of a different class than 348 * {@code factory.get()} does. 349 * 350 * <p>Call this method only when the simpler factory methods in classes like {@link 351 * HashBasedTable} and {@link TreeBasedTable} won't suffice. 352 * 353 * <p>The views returned by the {@code Table} methods {@link Table#column}, {@link 354 * Table#columnKeySet}, and {@link Table#columnMap} have iterators that don't support {@code 355 * remove()}. Otherwise, all optional operations are supported. Null row keys, columns keys, and 356 * values are not supported. 357 * 358 * <p>Lookups by row key are often faster than lookups by column key, because the data is stored 359 * in a {@code Map<R, Map<C, V>>}. A method call like {@code column(columnKey).get(rowKey)} still 360 * runs quickly, since the row key is provided. However, {@code column(columnKey).size()} takes 361 * longer, since an iteration across all row keys occurs. 362 * 363 * <p>Note that this implementation is not synchronized. If multiple threads access this table 364 * concurrently and one of the threads modifies the table, it must be synchronized externally. 365 * 366 * <p>The table is serializable if {@code backingMap}, {@code factory}, the maps generated by 367 * {@code factory}, and the table contents are all serializable. 368 * 369 * <p>Note: the table assumes complete ownership over of {@code backingMap} and the maps returned 370 * by {@code factory}. Those objects should not be manually updated and they should not use soft, 371 * weak, or phantom references. 372 * 373 * @param backingMap place to store the mapping from each row key to its corresponding column key 374 * / value map 375 * @param factory supplier of new, empty maps that will each hold all column key / value mappings 376 * for a given row key 377 * @throws IllegalArgumentException if {@code backingMap} is not empty 378 * @since 10.0 379 */ 380 public static <R, C, V> Table<R, C, V> newCustomTable( 381 Map<R, Map<C, V>> backingMap, Supplier<? extends Map<C, V>> factory) { 382 checkArgument(backingMap.isEmpty()); 383 checkNotNull(factory); 384 // TODO(jlevy): Wrap factory to validate that the supplied maps are empty? 385 return new StandardTable<>(backingMap, factory); 386 } 387 388 /** 389 * Returns a view of a table where each value is transformed by a function. All other properties 390 * of the table, such as iteration order, are left intact. 391 * 392 * <p>Changes in the underlying table are reflected in this view. Conversely, this view supports 393 * removal operations, and these are reflected in the underlying table. 394 * 395 * <p>It's acceptable for the underlying table to contain null keys, and even null values provided 396 * that the function is capable of accepting null input. The transformed table might contain null 397 * values, if the function sometimes gives a null result. 398 * 399 * <p>The returned table is not thread-safe or serializable, even if the underlying table is. 400 * 401 * <p>The function is applied lazily, invoked when needed. This is necessary for the returned 402 * table to be a view, but it means that the function will be applied many times for bulk 403 * operations like {@link Table#containsValue} and {@code Table.toString()}. For this to perform 404 * well, {@code function} should be fast. To avoid lazy evaluation when the returned table doesn't 405 * need to be a view, copy the returned table into a new table of your choosing. 406 * 407 * @since 10.0 408 */ 409 public static < 410 R extends @Nullable Object, 411 C extends @Nullable Object, 412 V1 extends @Nullable Object, 413 V2 extends @Nullable Object> 414 Table<R, C, V2> transformValues( 415 Table<R, C, V1> fromTable, Function<? super V1, V2> function) { 416 return new TransformedTable<>(fromTable, function); 417 } 418 419 private static class TransformedTable< 420 R extends @Nullable Object, 421 C extends @Nullable Object, 422 V1 extends @Nullable Object, 423 V2 extends @Nullable Object> 424 extends AbstractTable<R, C, V2> { 425 final Table<R, C, V1> fromTable; 426 final Function<? super V1, V2> function; 427 428 TransformedTable(Table<R, C, V1> fromTable, Function<? super V1, V2> function) { 429 this.fromTable = checkNotNull(fromTable); 430 this.function = checkNotNull(function); 431 } 432 433 @Override 434 public boolean contains(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { 435 return fromTable.contains(rowKey, columnKey); 436 } 437 438 @Override 439 @CheckForNull 440 public V2 get(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { 441 // The function is passed a null input only when the table contains a null 442 // value. 443 // The cast is safe because of the contains() check. 444 return contains(rowKey, columnKey) 445 ? function.apply(uncheckedCastNullableTToT(fromTable.get(rowKey, columnKey))) 446 : null; 447 } 448 449 @Override 450 public int size() { 451 return fromTable.size(); 452 } 453 454 @Override 455 public void clear() { 456 fromTable.clear(); 457 } 458 459 @Override 460 @CheckForNull 461 public V2 put( 462 @ParametricNullness R rowKey, 463 @ParametricNullness C columnKey, 464 @ParametricNullness V2 value) { 465 throw new UnsupportedOperationException(); 466 } 467 468 @Override 469 public void putAll(Table<? extends R, ? extends C, ? extends V2> table) { 470 throw new UnsupportedOperationException(); 471 } 472 473 @Override 474 @CheckForNull 475 public V2 remove(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { 476 return contains(rowKey, columnKey) 477 // The cast is safe because of the contains() check. 478 ? function.apply(uncheckedCastNullableTToT(fromTable.remove(rowKey, columnKey))) 479 : null; 480 } 481 482 @Override 483 public Map<C, V2> row(@ParametricNullness R rowKey) { 484 return Maps.transformValues(fromTable.row(rowKey), function); 485 } 486 487 @Override 488 public Map<R, V2> column(@ParametricNullness C columnKey) { 489 return Maps.transformValues(fromTable.column(columnKey), function); 490 } 491 492 Function<Cell<R, C, V1>, Cell<R, C, V2>> cellFunction() { 493 return new Function<Cell<R, C, V1>, Cell<R, C, V2>>() { 494 @Override 495 public Cell<R, C, V2> apply(Cell<R, C, V1> cell) { 496 return immutableCell( 497 cell.getRowKey(), cell.getColumnKey(), function.apply(cell.getValue())); 498 } 499 }; 500 } 501 502 @Override 503 Iterator<Cell<R, C, V2>> cellIterator() { 504 return Iterators.transform(fromTable.cellSet().iterator(), cellFunction()); 505 } 506 507 @Override 508 Spliterator<Cell<R, C, V2>> cellSpliterator() { 509 return CollectSpliterators.map(fromTable.cellSet().spliterator(), cellFunction()); 510 } 511 512 @Override 513 public Set<R> rowKeySet() { 514 return fromTable.rowKeySet(); 515 } 516 517 @Override 518 public Set<C> columnKeySet() { 519 return fromTable.columnKeySet(); 520 } 521 522 @Override 523 Collection<V2> createValues() { 524 return Collections2.transform(fromTable.values(), function); 525 } 526 527 @Override 528 public Map<R, Map<C, V2>> rowMap() { 529 Function<Map<C, V1>, Map<C, V2>> rowFunction = 530 new Function<Map<C, V1>, Map<C, V2>>() { 531 @Override 532 public Map<C, V2> apply(Map<C, V1> row) { 533 return Maps.transformValues(row, function); 534 } 535 }; 536 return Maps.transformValues(fromTable.rowMap(), rowFunction); 537 } 538 539 @Override 540 public Map<C, Map<R, V2>> columnMap() { 541 Function<Map<R, V1>, Map<R, V2>> columnFunction = 542 new Function<Map<R, V1>, Map<R, V2>>() { 543 @Override 544 public Map<R, V2> apply(Map<R, V1> column) { 545 return Maps.transformValues(column, function); 546 } 547 }; 548 return Maps.transformValues(fromTable.columnMap(), columnFunction); 549 } 550 } 551 552 /** 553 * Returns an unmodifiable view of the specified table. This method allows modules to provide 554 * users with "read-only" access to internal tables. Query operations on the returned table "read 555 * through" to the specified table, and attempts to modify the returned table, whether direct or 556 * via its collection views, result in an {@code UnsupportedOperationException}. 557 * 558 * <p>The returned table will be serializable if the specified table is serializable. 559 * 560 * <p>Consider using an {@link ImmutableTable}, which is guaranteed never to change. 561 * 562 * @since 11.0 563 */ 564 public static <R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object> 565 Table<R, C, V> unmodifiableTable(Table<? extends R, ? extends C, ? extends V> table) { 566 return new UnmodifiableTable<>(table); 567 } 568 569 private static class UnmodifiableTable< 570 R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object> 571 extends ForwardingTable<R, C, V> implements Serializable { 572 final Table<? extends R, ? extends C, ? extends V> delegate; 573 574 UnmodifiableTable(Table<? extends R, ? extends C, ? extends V> delegate) { 575 this.delegate = checkNotNull(delegate); 576 } 577 578 @SuppressWarnings("unchecked") // safe, covariant cast 579 @Override 580 protected Table<R, C, V> delegate() { 581 return (Table<R, C, V>) delegate; 582 } 583 584 @Override 585 public Set<Cell<R, C, V>> cellSet() { 586 return Collections.unmodifiableSet(super.cellSet()); 587 } 588 589 @Override 590 public void clear() { 591 throw new UnsupportedOperationException(); 592 } 593 594 @Override 595 public Map<R, V> column(@ParametricNullness C columnKey) { 596 return Collections.unmodifiableMap(super.column(columnKey)); 597 } 598 599 @Override 600 public Set<C> columnKeySet() { 601 return Collections.unmodifiableSet(super.columnKeySet()); 602 } 603 604 @Override 605 public Map<C, Map<R, V>> columnMap() { 606 Function<Map<R, V>, Map<R, V>> wrapper = unmodifiableWrapper(); 607 return Collections.unmodifiableMap(Maps.transformValues(super.columnMap(), wrapper)); 608 } 609 610 @Override 611 @CheckForNull 612 public V put( 613 @ParametricNullness R rowKey, 614 @ParametricNullness C columnKey, 615 @ParametricNullness V value) { 616 throw new UnsupportedOperationException(); 617 } 618 619 @Override 620 public void putAll(Table<? extends R, ? extends C, ? extends V> table) { 621 throw new UnsupportedOperationException(); 622 } 623 624 @Override 625 @CheckForNull 626 public V remove(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { 627 throw new UnsupportedOperationException(); 628 } 629 630 @Override 631 public Map<C, V> row(@ParametricNullness R rowKey) { 632 return Collections.unmodifiableMap(super.row(rowKey)); 633 } 634 635 @Override 636 public Set<R> rowKeySet() { 637 return Collections.unmodifiableSet(super.rowKeySet()); 638 } 639 640 @Override 641 public Map<R, Map<C, V>> rowMap() { 642 Function<Map<C, V>, Map<C, V>> wrapper = unmodifiableWrapper(); 643 return Collections.unmodifiableMap(Maps.transformValues(super.rowMap(), wrapper)); 644 } 645 646 @Override 647 public Collection<V> values() { 648 return Collections.unmodifiableCollection(super.values()); 649 } 650 651 private static final long serialVersionUID = 0; 652 } 653 654 /** 655 * Returns an unmodifiable view of the specified row-sorted table. This method allows modules to 656 * provide users with "read-only" access to internal tables. Query operations on the returned 657 * table "read through" to the specified table, and attempts to modify the returned table, whether 658 * direct or via its collection views, result in an {@code UnsupportedOperationException}. 659 * 660 * <p>The returned table will be serializable if the specified table is serializable. 661 * 662 * @param table the row-sorted table for which an unmodifiable view is to be returned 663 * @return an unmodifiable view of the specified table 664 * @since 11.0 665 */ 666 public static <R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object> 667 RowSortedTable<R, C, V> unmodifiableRowSortedTable( 668 RowSortedTable<R, ? extends C, ? extends V> table) { 669 /* 670 * It's not ? extends R, because it's technically not covariant in R. Specifically, 671 * table.rowMap().comparator() could return a comparator that only works for the ? extends R. 672 * Collections.unmodifiableSortedMap makes the same distinction. 673 */ 674 return new UnmodifiableRowSortedMap<>(table); 675 } 676 677 static final class UnmodifiableRowSortedMap< 678 R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object> 679 extends UnmodifiableTable<R, C, V> implements RowSortedTable<R, C, V> { 680 681 public UnmodifiableRowSortedMap(RowSortedTable<R, ? extends C, ? extends V> delegate) { 682 super(delegate); 683 } 684 685 @Override 686 protected RowSortedTable<R, C, V> delegate() { 687 return (RowSortedTable<R, C, V>) super.delegate(); 688 } 689 690 @Override 691 public SortedMap<R, Map<C, V>> rowMap() { 692 Function<Map<C, V>, Map<C, V>> wrapper = unmodifiableWrapper(); 693 return Collections.unmodifiableSortedMap(Maps.transformValues(delegate().rowMap(), wrapper)); 694 } 695 696 @Override 697 public SortedSet<R> rowKeySet() { 698 return Collections.unmodifiableSortedSet(delegate().rowKeySet()); 699 } 700 701 private static final long serialVersionUID = 0; 702 } 703 704 @SuppressWarnings("unchecked") 705 private static <K extends @Nullable Object, V extends @Nullable Object> 706 Function<Map<K, V>, Map<K, V>> unmodifiableWrapper() { 707 return (Function) UNMODIFIABLE_WRAPPER; 708 } 709 710 private static final Function<? extends Map<?, ?>, ? extends Map<?, ?>> UNMODIFIABLE_WRAPPER = 711 new Function<Map<Object, Object>, Map<Object, Object>>() { 712 @Override 713 public Map<Object, Object> apply(Map<Object, Object> input) { 714 return Collections.unmodifiableMap(input); 715 } 716 }; 717 718 /** 719 * Returns a synchronized (thread-safe) table backed by the specified table. In order to guarantee 720 * serial access, it is critical that <b>all</b> access to the backing table is accomplished 721 * through the returned table. 722 * 723 * <p>It is imperative that the user manually synchronize on the returned table when accessing any 724 * of its collection views: 725 * 726 * <pre>{@code 727 * Table<R, C, V> table = Tables.synchronizedTable(HashBasedTable.<R, C, V>create()); 728 * ... 729 * Map<C, V> row = table.row(rowKey); // Needn't be in synchronized block 730 * ... 731 * synchronized (table) { // Synchronizing on table, not row! 732 * Iterator<Entry<C, V>> i = row.entrySet().iterator(); // Must be in synchronized block 733 * while (i.hasNext()) { 734 * foo(i.next()); 735 * } 736 * } 737 * }</pre> 738 * 739 * <p>Failure to follow this advice may result in non-deterministic behavior. 740 * 741 * <p>The returned table will be serializable if the specified table is serializable. 742 * 743 * @param table the table to be wrapped in a synchronized view 744 * @return a synchronized view of the specified table 745 * @since 22.0 746 */ 747 public static <R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object> 748 Table<R, C, V> synchronizedTable(Table<R, C, V> table) { 749 return Synchronized.table(table, null); 750 } 751 752 static boolean equalsImpl(Table<?, ?, ?> table, @CheckForNull Object obj) { 753 if (obj == table) { 754 return true; 755 } else if (obj instanceof Table) { 756 Table<?, ?, ?> that = (Table<?, ?, ?>) obj; 757 return table.cellSet().equals(that.cellSet()); 758 } else { 759 return false; 760 } 761 } 762}