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