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