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