001/* 002 * Copyright (C) 2009 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.checkNotNull; 020 021import com.google.common.annotations.Beta; 022import com.google.common.annotations.GwtCompatible; 023import com.google.common.annotations.GwtIncompatible; 024import com.google.common.annotations.J2ktIncompatible; 025import com.google.common.base.MoreObjects; 026import com.google.errorprone.annotations.CanIgnoreReturnValue; 027import com.google.errorprone.annotations.DoNotCall; 028import com.google.errorprone.annotations.DoNotMock; 029import java.io.InvalidObjectException; 030import java.io.ObjectInputStream; 031import java.io.Serializable; 032import java.util.Comparator; 033import java.util.Iterator; 034import java.util.List; 035import java.util.Map; 036import java.util.function.BinaryOperator; 037import java.util.function.Function; 038import java.util.stream.Collector; 039import javax.annotation.CheckForNull; 040import org.checkerframework.checker.nullness.qual.Nullable; 041 042/** 043 * A {@link Table} whose contents will never change, with many other important properties detailed 044 * at {@link ImmutableCollection}. 045 * 046 * <p>See the Guava User Guide article on <a href= 047 * "https://github.com/google/guava/wiki/ImmutableCollectionsExplained">immutable collections</a>. 048 * 049 * @author Gregory Kick 050 * @since 11.0 051 */ 052@GwtCompatible 053@ElementTypesAreNonnullByDefault 054public abstract class ImmutableTable<R, C, V> extends AbstractTable<R, C, V> 055 implements Serializable { 056 057 /** 058 * Returns a {@code Collector} that accumulates elements into an {@code ImmutableTable}. Each 059 * input element is mapped to one cell in the returned table, with the rows, columns, and values 060 * generated by applying the specified functions. 061 * 062 * <p>The returned {@code Collector} will throw a {@code NullPointerException} at collection time 063 * if the row, column, or value functions return null on any input. 064 * 065 * @since 33.2.0 (available since 21.0 in guava-jre) 066 */ 067 @SuppressWarnings({"AndroidJdkLibsChecker", "Java7ApiChecker"}) 068 @IgnoreJRERequirement // Users will use this only if they're already using streams. 069 @Beta // TODO: b/288085449 - Remove. 070 public static <T extends @Nullable Object, R, C, V> 071 Collector<T, ?, ImmutableTable<R, C, V>> toImmutableTable( 072 Function<? super T, ? extends R> rowFunction, 073 Function<? super T, ? extends C> columnFunction, 074 Function<? super T, ? extends V> valueFunction) { 075 return TableCollectors.toImmutableTable(rowFunction, columnFunction, valueFunction); 076 } 077 078 /** 079 * Returns a {@code Collector} that accumulates elements into an {@code ImmutableTable}. Each 080 * input element is mapped to one cell in the returned table, with the rows, columns, and values 081 * generated by applying the specified functions. If multiple inputs are mapped to the same row 082 * and column pair, they will be combined with the specified merging function in encounter order. 083 * 084 * <p>The returned {@code Collector} will throw a {@code NullPointerException} at collection time 085 * if the row, column, value, or merging functions return null on any input. 086 * 087 * @since 33.2.0 (available since 21.0 in guava-jre) 088 */ 089 @SuppressWarnings({"AndroidJdkLibsChecker", "Java7ApiChecker"}) 090 @IgnoreJRERequirement // Users will use this only if they're already using streams. 091 @Beta // TODO: b/288085449 - Remove. 092 public static <T extends @Nullable Object, R, C, V> 093 Collector<T, ?, ImmutableTable<R, C, V>> toImmutableTable( 094 Function<? super T, ? extends R> rowFunction, 095 Function<? super T, ? extends C> columnFunction, 096 Function<? super T, ? extends V> valueFunction, 097 BinaryOperator<V> mergeFunction) { 098 return TableCollectors.toImmutableTable( 099 rowFunction, columnFunction, valueFunction, mergeFunction); 100 } 101 102 /** 103 * Returns an empty immutable table. 104 * 105 * <p><b>Performance note:</b> the instance returned is a singleton. 106 */ 107 @SuppressWarnings("unchecked") 108 public static <R, C, V> ImmutableTable<R, C, V> of() { 109 return (ImmutableTable<R, C, V>) SparseImmutableTable.EMPTY; 110 } 111 112 /** Returns an immutable table containing a single cell. */ 113 public static <R, C, V> ImmutableTable<R, C, V> of(R rowKey, C columnKey, V value) { 114 return new SingletonImmutableTable<>(rowKey, columnKey, value); 115 } 116 117 /** 118 * Returns an immutable copy of the provided table. 119 * 120 * <p>The {@link Table#cellSet()} iteration order of the provided table determines the iteration 121 * ordering of all views in the returned table. Note that some views of the original table and the 122 * copied table may have different iteration orders. For more control over the ordering, create a 123 * {@link Builder} and call {@link Builder#orderRowsBy}, {@link Builder#orderColumnsBy}, and 124 * {@link Builder#putAll} 125 * 126 * <p>Despite the method name, this method attempts to avoid actually copying the data when it is 127 * safe to do so. The exact circumstances under which a copy will or will not be performed are 128 * undocumented and subject to change. 129 */ 130 public static <R, C, V> ImmutableTable<R, C, V> copyOf( 131 Table<? extends R, ? extends C, ? extends V> table) { 132 if (table instanceof ImmutableTable) { 133 @SuppressWarnings("unchecked") 134 ImmutableTable<R, C, V> parameterizedTable = (ImmutableTable<R, C, V>) table; 135 return parameterizedTable; 136 } else { 137 return copyOf(table.cellSet()); 138 } 139 } 140 141 static <R, C, V> ImmutableTable<R, C, V> copyOf( 142 Iterable<? extends Cell<? extends R, ? extends C, ? extends V>> cells) { 143 ImmutableTable.Builder<R, C, V> builder = ImmutableTable.builder(); 144 for (Cell<? extends R, ? extends C, ? extends V> cell : cells) { 145 builder.put(cell); 146 } 147 return builder.build(); 148 } 149 150 /** 151 * Returns a new builder. The generated builder is equivalent to the builder created by the {@link 152 * Builder#Builder() ImmutableTable.Builder()} constructor. 153 */ 154 public static <R, C, V> Builder<R, C, V> builder() { 155 return new Builder<>(); 156 } 157 158 /** 159 * Verifies that {@code rowKey}, {@code columnKey} and {@code value} are non-null, and returns a 160 * new entry with those values. 161 */ 162 static <R, C, V> Cell<R, C, V> cellOf(R rowKey, C columnKey, V value) { 163 return Tables.immutableCell( 164 checkNotNull(rowKey, "rowKey"), 165 checkNotNull(columnKey, "columnKey"), 166 checkNotNull(value, "value")); 167 } 168 169 /** 170 * A builder for creating immutable table instances, especially {@code public static final} tables 171 * ("constant tables"). Example: 172 * 173 * <pre>{@code 174 * static final ImmutableTable<Integer, Character, String> SPREADSHEET = 175 * new ImmutableTable.Builder<Integer, Character, String>() 176 * .put(1, 'A', "foo") 177 * .put(1, 'B', "bar") 178 * .put(2, 'A', "baz") 179 * .buildOrThrow(); 180 * }</pre> 181 * 182 * <p>By default, the order in which cells are added to the builder determines the iteration 183 * ordering of all views in the returned table, with {@link #putAll} following the {@link 184 * Table#cellSet()} iteration order. However, if {@link #orderRowsBy} or {@link #orderColumnsBy} 185 * is called, the views are sorted by the supplied comparators. 186 * 187 * <p>For empty or single-cell immutable tables, {@link #of()} and {@link #of(Object, Object, 188 * Object)} are even more convenient. 189 * 190 * <p>Builder instances can be reused - it is safe to call {@link #buildOrThrow} multiple times to 191 * build multiple tables in series. Each table is a superset of the tables created before it. 192 * 193 * @since 11.0 194 */ 195 @DoNotMock 196 public static final class Builder<R, C, V> { 197 private final List<Cell<R, C, V>> cells = Lists.newArrayList(); 198 @CheckForNull private Comparator<? super R> rowComparator; 199 @CheckForNull private Comparator<? super C> columnComparator; 200 201 /** 202 * Creates a new builder. The returned builder is equivalent to the builder generated by {@link 203 * ImmutableTable#builder}. 204 */ 205 public Builder() {} 206 207 /** Specifies the ordering of the generated table's rows. */ 208 @CanIgnoreReturnValue 209 public Builder<R, C, V> orderRowsBy(Comparator<? super R> rowComparator) { 210 this.rowComparator = checkNotNull(rowComparator, "rowComparator"); 211 return this; 212 } 213 214 /** Specifies the ordering of the generated table's columns. */ 215 @CanIgnoreReturnValue 216 public Builder<R, C, V> orderColumnsBy(Comparator<? super C> columnComparator) { 217 this.columnComparator = checkNotNull(columnComparator, "columnComparator"); 218 return this; 219 } 220 221 /** 222 * Associates the ({@code rowKey}, {@code columnKey}) pair with {@code value} in the built 223 * table. Duplicate key pairs are not allowed and will cause {@link #build} to fail. 224 */ 225 @CanIgnoreReturnValue 226 public Builder<R, C, V> put(R rowKey, C columnKey, V value) { 227 cells.add(cellOf(rowKey, columnKey, value)); 228 return this; 229 } 230 231 /** 232 * Adds the given {@code cell} to the table, making it immutable if necessary. Duplicate key 233 * pairs are not allowed and will cause {@link #build} to fail. 234 */ 235 @CanIgnoreReturnValue 236 public Builder<R, C, V> put(Cell<? extends R, ? extends C, ? extends V> cell) { 237 if (cell instanceof Tables.ImmutableCell) { 238 checkNotNull(cell.getRowKey(), "row"); 239 checkNotNull(cell.getColumnKey(), "column"); 240 checkNotNull(cell.getValue(), "value"); 241 @SuppressWarnings("unchecked") // all supported methods are covariant 242 Cell<R, C, V> immutableCell = (Cell<R, C, V>) cell; 243 cells.add(immutableCell); 244 } else { 245 put(cell.getRowKey(), cell.getColumnKey(), cell.getValue()); 246 } 247 return this; 248 } 249 250 /** 251 * Associates all of the given table's keys and values in the built table. Duplicate row key 252 * column key pairs are not allowed, and will cause {@link #build} to fail. 253 * 254 * @throws NullPointerException if any key or value in {@code table} is null 255 */ 256 @CanIgnoreReturnValue 257 public Builder<R, C, V> putAll(Table<? extends R, ? extends C, ? extends V> table) { 258 for (Cell<? extends R, ? extends C, ? extends V> cell : table.cellSet()) { 259 put(cell); 260 } 261 return this; 262 } 263 264 @CanIgnoreReturnValue 265 Builder<R, C, V> combine(Builder<R, C, V> other) { 266 this.cells.addAll(other.cells); 267 return this; 268 } 269 270 /** 271 * Returns a newly-created immutable table. 272 * 273 * <p>Prefer the equivalent method {@link #buildOrThrow()} to make it explicit that the method 274 * will throw an exception if there are duplicate key pairs. The {@code build()} method will 275 * soon be deprecated. 276 * 277 * @throws IllegalArgumentException if duplicate key pairs were added 278 */ 279 public ImmutableTable<R, C, V> build() { 280 return buildOrThrow(); 281 } 282 283 /** 284 * Returns a newly-created immutable table, or throws an exception if duplicate key pairs were 285 * added. 286 * 287 * @throws IllegalArgumentException if duplicate key pairs were added 288 * @since 31.0 289 */ 290 public ImmutableTable<R, C, V> buildOrThrow() { 291 int size = cells.size(); 292 switch (size) { 293 case 0: 294 return of(); 295 case 1: 296 return new SingletonImmutableTable<>(Iterables.getOnlyElement(cells)); 297 default: 298 return RegularImmutableTable.forCells(cells, rowComparator, columnComparator); 299 } 300 } 301 } 302 303 ImmutableTable() {} 304 305 @Override 306 public ImmutableSet<Cell<R, C, V>> cellSet() { 307 return (ImmutableSet<Cell<R, C, V>>) super.cellSet(); 308 } 309 310 @Override 311 abstract ImmutableSet<Cell<R, C, V>> createCellSet(); 312 313 @Override 314 final UnmodifiableIterator<Cell<R, C, V>> cellIterator() { 315 throw new AssertionError("should never be called"); 316 } 317 318 @Override 319 public ImmutableCollection<V> values() { 320 return (ImmutableCollection<V>) super.values(); 321 } 322 323 @Override 324 abstract ImmutableCollection<V> createValues(); 325 326 @Override 327 final Iterator<V> valuesIterator() { 328 throw new AssertionError("should never be called"); 329 } 330 331 /** 332 * {@inheritDoc} 333 * 334 * @throws NullPointerException if {@code columnKey} is {@code null} 335 */ 336 @Override 337 public ImmutableMap<R, V> column(C columnKey) { 338 checkNotNull(columnKey, "columnKey"); 339 return MoreObjects.firstNonNull( 340 (ImmutableMap<R, V>) columnMap().get(columnKey), ImmutableMap.<R, V>of()); 341 } 342 343 @Override 344 public ImmutableSet<C> columnKeySet() { 345 return columnMap().keySet(); 346 } 347 348 /** 349 * {@inheritDoc} 350 * 351 * <p>The value {@code Map<R, V>} instances in the returned map are {@link ImmutableMap} instances 352 * as well. 353 */ 354 @Override 355 public abstract ImmutableMap<C, Map<R, V>> columnMap(); 356 357 /** 358 * {@inheritDoc} 359 * 360 * @throws NullPointerException if {@code rowKey} is {@code null} 361 */ 362 @Override 363 public ImmutableMap<C, V> row(R rowKey) { 364 checkNotNull(rowKey, "rowKey"); 365 return MoreObjects.firstNonNull( 366 (ImmutableMap<C, V>) rowMap().get(rowKey), ImmutableMap.<C, V>of()); 367 } 368 369 @Override 370 public ImmutableSet<R> rowKeySet() { 371 return rowMap().keySet(); 372 } 373 374 /** 375 * {@inheritDoc} 376 * 377 * <p>The value {@code Map<C, V>} instances in the returned map are {@link ImmutableMap} instances 378 * as well. 379 */ 380 @Override 381 public abstract ImmutableMap<R, Map<C, V>> rowMap(); 382 383 @Override 384 public boolean contains(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { 385 return get(rowKey, columnKey) != null; 386 } 387 388 @Override 389 public boolean containsValue(@CheckForNull Object value) { 390 return values().contains(value); 391 } 392 393 /** 394 * Guaranteed to throw an exception and leave the table unmodified. 395 * 396 * @throws UnsupportedOperationException always 397 * @deprecated Unsupported operation. 398 */ 399 @Deprecated 400 @Override 401 @DoNotCall("Always throws UnsupportedOperationException") 402 public final void clear() { 403 throw new UnsupportedOperationException(); 404 } 405 406 /** 407 * Guaranteed to throw an exception and leave the table unmodified. 408 * 409 * @throws UnsupportedOperationException always 410 * @deprecated Unsupported operation. 411 */ 412 @CanIgnoreReturnValue 413 @Deprecated 414 @Override 415 @DoNotCall("Always throws UnsupportedOperationException") 416 @CheckForNull 417 public final V put(R rowKey, C columnKey, V value) { 418 throw new UnsupportedOperationException(); 419 } 420 421 /** 422 * Guaranteed to throw an exception and leave the table unmodified. 423 * 424 * @throws UnsupportedOperationException always 425 * @deprecated Unsupported operation. 426 */ 427 @Deprecated 428 @Override 429 @DoNotCall("Always throws UnsupportedOperationException") 430 public final void putAll(Table<? extends R, ? extends C, ? extends V> table) { 431 throw new UnsupportedOperationException(); 432 } 433 434 /** 435 * Guaranteed to throw an exception and leave the table unmodified. 436 * 437 * @throws UnsupportedOperationException always 438 * @deprecated Unsupported operation. 439 */ 440 @CanIgnoreReturnValue 441 @Deprecated 442 @Override 443 @DoNotCall("Always throws UnsupportedOperationException") 444 @CheckForNull 445 public final V remove(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { 446 throw new UnsupportedOperationException(); 447 } 448 449 /** 450 * Serialized type for all ImmutableTable instances. It captures the logical contents and 451 * preserves iteration order of all views. 452 */ 453 static final class SerializedForm implements Serializable { 454 private final Object[] rowKeys; 455 private final Object[] columnKeys; 456 457 private final Object[] cellValues; 458 private final int[] cellRowIndices; 459 private final int[] cellColumnIndices; 460 461 private SerializedForm( 462 Object[] rowKeys, 463 Object[] columnKeys, 464 Object[] cellValues, 465 int[] cellRowIndices, 466 int[] cellColumnIndices) { 467 this.rowKeys = rowKeys; 468 this.columnKeys = columnKeys; 469 this.cellValues = cellValues; 470 this.cellRowIndices = cellRowIndices; 471 this.cellColumnIndices = cellColumnIndices; 472 } 473 474 static SerializedForm create( 475 ImmutableTable<?, ?, ?> table, int[] cellRowIndices, int[] cellColumnIndices) { 476 return new SerializedForm( 477 table.rowKeySet().toArray(), 478 table.columnKeySet().toArray(), 479 table.values().toArray(), 480 cellRowIndices, 481 cellColumnIndices); 482 } 483 484 Object readResolve() { 485 if (cellValues.length == 0) { 486 return of(); 487 } 488 if (cellValues.length == 1) { 489 return of(rowKeys[0], columnKeys[0], cellValues[0]); 490 } 491 ImmutableList.Builder<Cell<Object, Object, Object>> cellListBuilder = 492 new ImmutableList.Builder<>(cellValues.length); 493 for (int i = 0; i < cellValues.length; i++) { 494 cellListBuilder.add( 495 cellOf(rowKeys[cellRowIndices[i]], columnKeys[cellColumnIndices[i]], cellValues[i])); 496 } 497 return RegularImmutableTable.forOrderedComponents( 498 cellListBuilder.build(), ImmutableSet.copyOf(rowKeys), ImmutableSet.copyOf(columnKeys)); 499 } 500 501 private static final long serialVersionUID = 0; 502 } 503 504 @J2ktIncompatible // serialization 505 @GwtIncompatible // serialization 506 abstract Object writeReplace(); 507 508 @GwtIncompatible // serialization 509 @J2ktIncompatible 510 private void readObject(ObjectInputStream stream) throws InvalidObjectException { 511 throw new InvalidObjectException("Use SerializedForm"); 512 } 513 514 private static final long serialVersionUID = 0xdecaf; 515}