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