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 055public abstract class ImmutableTable<R, C, V> extends AbstractTable<R, C, V> 056 implements Serializable { 057 058 /** 059 * Returns a {@code Collector} that accumulates elements into an {@code ImmutableTable}. Each 060 * input element is mapped to one cell in the returned table, with the rows, columns, and values 061 * generated by applying the specified functions. 062 * 063 * <p>The returned {@code Collector} will throw a {@code NullPointerException} at collection time 064 * if the row, column, or value functions return null on any input. 065 * 066 * @since 21.0 067 */ 068 public static <T extends @Nullable Object, R, C, V> 069 Collector<T, ?, ImmutableTable<R, C, V>> toImmutableTable( 070 Function<? super T, ? extends R> rowFunction, 071 Function<? super T, ? extends C> columnFunction, 072 Function<? super T, ? extends V> valueFunction) { 073 return TableCollectors.toImmutableTable(rowFunction, columnFunction, valueFunction); 074 } 075 076 /** 077 * Returns a {@code Collector} that accumulates elements into an {@code ImmutableTable}. Each 078 * input element is mapped to one cell in the returned table, with the rows, columns, and values 079 * generated by applying the specified functions. If multiple inputs are mapped to the same row 080 * and column pair, they will be combined with the specified merging function in encounter order. 081 * 082 * <p>The returned {@code Collector} will throw a {@code NullPointerException} at collection time 083 * if the row, column, value, or merging functions return null on any input. 084 * 085 * @since 21.0 086 */ 087 public static <T extends @Nullable Object, R, C, V> 088 Collector<T, ?, ImmutableTable<R, C, V>> toImmutableTable( 089 Function<? super T, ? extends R> rowFunction, 090 Function<? super T, ? extends C> columnFunction, 091 Function<? super T, ? extends V> valueFunction, 092 BinaryOperator<V> mergeFunction) { 093 return TableCollectors.toImmutableTable( 094 rowFunction, columnFunction, valueFunction, mergeFunction); 095 } 096 097 /** 098 * Returns an empty immutable table. 099 * 100 * <p><b>Performance note:</b> the instance returned is a singleton. 101 */ 102 @SuppressWarnings("unchecked") 103 public static <R, C, V> ImmutableTable<R, C, V> of() { 104 return (ImmutableTable<R, C, V>) SparseImmutableTable.EMPTY; 105 } 106 107 /** Returns an immutable table containing a single cell. */ 108 public static <R, C, V> ImmutableTable<R, C, V> of(R rowKey, C columnKey, V value) { 109 return new SingletonImmutableTable<>(rowKey, columnKey, value); 110 } 111 112 /** 113 * Returns an immutable copy of the provided table. 114 * 115 * <p>The {@link Table#cellSet()} iteration order of the provided table determines the iteration 116 * ordering of all views in the returned table. Note that some views of the original table and the 117 * copied table may have different iteration orders. For more control over the ordering, create a 118 * {@link Builder} and call {@link Builder#orderRowsBy}, {@link Builder#orderColumnsBy}, and 119 * {@link Builder#putAll} 120 * 121 * <p>Despite the method name, this method attempts to avoid actually copying the data when it is 122 * safe to do so. The exact circumstances under which a copy will or will not be performed are 123 * undocumented and subject to change. 124 */ 125 public static <R, C, V> ImmutableTable<R, C, V> copyOf( 126 Table<? extends R, ? extends C, ? extends V> table) { 127 if (table instanceof ImmutableTable) { 128 @SuppressWarnings("unchecked") 129 ImmutableTable<R, C, V> parameterizedTable = (ImmutableTable<R, C, V>) table; 130 return parameterizedTable; 131 } else { 132 return copyOf(table.cellSet()); 133 } 134 } 135 136 static <R, C, V> ImmutableTable<R, C, V> copyOf( 137 Iterable<? extends Cell<? extends R, ? extends C, ? extends V>> cells) { 138 ImmutableTable.Builder<R, C, V> builder = ImmutableTable.builder(); 139 for (Cell<? extends R, ? extends C, ? extends V> cell : cells) { 140 builder.put(cell); 141 } 142 return builder.build(); 143 } 144 145 /** 146 * Returns a new builder. The generated builder is equivalent to the builder created by the {@link 147 * Builder#Builder() ImmutableTable.Builder()} constructor. 148 */ 149 public static <R, C, V> Builder<R, C, V> builder() { 150 return new Builder<>(); 151 } 152 153 /** 154 * Verifies that {@code rowKey}, {@code columnKey} and {@code value} are non-null, and returns a 155 * new entry with those values. 156 */ 157 static <R, C, V> Cell<R, C, V> cellOf(R rowKey, C columnKey, V value) { 158 return immutableCell( 159 checkNotNull(rowKey, "rowKey"), 160 checkNotNull(columnKey, "columnKey"), 161 checkNotNull(value, "value")); 162 } 163 164 /** 165 * A builder for creating immutable table instances, especially {@code public static final} tables 166 * ("constant tables"). Example: 167 * 168 * <pre>{@code 169 * static final ImmutableTable<Integer, Character, String> SPREADSHEET = 170 * new ImmutableTable.Builder<Integer, Character, String>() 171 * .put(1, 'A', "foo") 172 * .put(1, 'B', "bar") 173 * .put(2, 'A', "baz") 174 * .buildOrThrow(); 175 * }</pre> 176 * 177 * <p>By default, the order in which cells are added to the builder determines the iteration 178 * ordering of all views in the returned table, with {@link #putAll} following the {@link 179 * Table#cellSet()} iteration order. However, if {@link #orderRowsBy} or {@link #orderColumnsBy} 180 * is called, the views are sorted by the supplied comparators. 181 * 182 * <p>For empty or single-cell immutable tables, {@link #of()} and {@link #of(Object, Object, 183 * Object)} are even more convenient. 184 * 185 * <p>Builder instances can be reused - it is safe to call {@link #buildOrThrow} multiple times to 186 * build multiple tables in series. Each table is a superset of the tables created before it. 187 * 188 * @since 11.0 189 */ 190 @DoNotMock 191 public static final class Builder<R, C, V> { 192 private final List<Cell<R, C, V>> cells = Lists.newArrayList(); 193 @CheckForNull private Comparator<? super R> rowComparator; 194 @CheckForNull private Comparator<? super C> columnComparator; 195 196 /** 197 * Creates a new builder. The returned builder is equivalent to the builder generated by {@link 198 * ImmutableTable#builder}. 199 */ 200 public Builder() {} 201 202 /** Specifies the ordering of the generated table's rows. */ 203 @CanIgnoreReturnValue 204 public Builder<R, C, V> orderRowsBy(Comparator<? super R> rowComparator) { 205 this.rowComparator = checkNotNull(rowComparator, "rowComparator"); 206 return this; 207 } 208 209 /** Specifies the ordering of the generated table's columns. */ 210 @CanIgnoreReturnValue 211 public Builder<R, C, V> orderColumnsBy(Comparator<? super C> columnComparator) { 212 this.columnComparator = checkNotNull(columnComparator, "columnComparator"); 213 return this; 214 } 215 216 /** 217 * Associates the ({@code rowKey}, {@code columnKey}) pair with {@code value} in the built 218 * table. Duplicate key pairs are not allowed and will cause {@link #build} to fail. 219 */ 220 @CanIgnoreReturnValue 221 public Builder<R, C, V> put(R rowKey, C columnKey, V value) { 222 cells.add(cellOf(rowKey, columnKey, value)); 223 return this; 224 } 225 226 /** 227 * Adds the given {@code cell} to the table, making it immutable if necessary. Duplicate key 228 * pairs are not allowed and will cause {@link #build} to fail. 229 */ 230 @CanIgnoreReturnValue 231 public Builder<R, C, V> put(Cell<? extends R, ? extends C, ? extends V> cell) { 232 if (cell instanceof Tables.ImmutableCell) { 233 checkNotNull(cell.getRowKey(), "row"); 234 checkNotNull(cell.getColumnKey(), "column"); 235 checkNotNull(cell.getValue(), "value"); 236 @SuppressWarnings("unchecked") // all supported methods are covariant 237 Cell<R, C, V> immutableCell = (Cell<R, C, V>) cell; 238 cells.add(immutableCell); 239 } else { 240 put(cell.getRowKey(), cell.getColumnKey(), cell.getValue()); 241 } 242 return this; 243 } 244 245 /** 246 * Associates all of the given table's keys and values in the built table. Duplicate row key 247 * column key pairs are not allowed, and will cause {@link #build} to fail. 248 * 249 * @throws NullPointerException if any key or value in {@code table} is null 250 */ 251 @CanIgnoreReturnValue 252 public Builder<R, C, V> putAll(Table<? extends R, ? extends C, ? extends V> table) { 253 for (Cell<? extends R, ? extends C, ? extends V> cell : table.cellSet()) { 254 put(cell); 255 } 256 return this; 257 } 258 259 @CanIgnoreReturnValue 260 Builder<R, C, V> combine(Builder<R, C, V> other) { 261 this.cells.addAll(other.cells); 262 return this; 263 } 264 265 /** 266 * Returns a newly-created immutable table. 267 * 268 * <p>Prefer the equivalent method {@link #buildOrThrow()} to make it explicit that the method 269 * will throw an exception if there are duplicate key pairs. The {@code build()} method will 270 * soon be deprecated. 271 * 272 * @throws IllegalArgumentException if duplicate key pairs were added 273 */ 274 public ImmutableTable<R, C, V> build() { 275 return buildOrThrow(); 276 } 277 278 /** 279 * Returns a newly-created immutable table, or throws an exception if duplicate key pairs were 280 * added. 281 * 282 * @throws IllegalArgumentException if duplicate key pairs were added 283 * @since 31.0 284 */ 285 public ImmutableTable<R, C, V> buildOrThrow() { 286 int size = cells.size(); 287 switch (size) { 288 case 0: 289 return of(); 290 case 1: 291 return new SingletonImmutableTable<>(getOnlyElement(cells)); 292 default: 293 return RegularImmutableTable.forCells(cells, rowComparator, columnComparator); 294 } 295 } 296 } 297 298 ImmutableTable() {} 299 300 @Override 301 public ImmutableSet<Cell<R, C, V>> cellSet() { 302 return (ImmutableSet<Cell<R, C, V>>) super.cellSet(); 303 } 304 305 @Override 306 abstract ImmutableSet<Cell<R, C, V>> createCellSet(); 307 308 @Override 309 final UnmodifiableIterator<Cell<R, C, V>> cellIterator() { 310 throw new AssertionError("should never be called"); 311 } 312 313 @Override 314 final Spliterator<Cell<R, C, V>> cellSpliterator() { 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 = 0xcafebabe; 515}