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