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