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