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