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