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