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