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