All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.common.collect.Tables Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 34.0.0.Final
Show newest version
/*
 * Copyright (C) 2008 The Guava Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.common.collect;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.NullnessCasts.uncheckedCastNullableTToT;

import com.google.common.annotations.GwtCompatible;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.base.Supplier;
import com.google.common.collect.Table.Cell;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.Spliterator;
import java.util.function.BinaryOperator;
import java.util.stream.Collector;
import javax.annotation.CheckForNull;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
 * Provides static methods that involve a {@code Table}.
 *
 * 

See the Guava User Guide article on {@code Tables}. * * @author Jared Levy * @author Louis Wasserman * @since 7.0 */ @GwtCompatible @ElementTypesAreNonnullByDefault public final class Tables { private Tables() {} /** * Returns a {@link Collector} that accumulates elements into a {@code Table} created using the * specified supplier, whose cells are generated by applying the provided mapping functions to the * input elements. Cells are inserted into the generated {@code Table} in encounter order. * *

If multiple input elements map to the same row and column, an {@code IllegalStateException} * is thrown when the collection operation is performed. * *

To collect to an {@link ImmutableTable}, use {@link ImmutableTable#toImmutableTable}. * * @since 21.0 */ public static < T extends @Nullable Object, R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object, I extends Table> Collector toTable( java.util.function.Function rowFunction, java.util.function.Function columnFunction, java.util.function.Function valueFunction, java.util.function.Supplier tableSupplier) { return TableCollectors.toTable( rowFunction, columnFunction, valueFunction, tableSupplier); } /** * Returns a {@link Collector} that accumulates elements into a {@code Table} created using the * specified supplier, whose cells are generated by applying the provided mapping functions to the * input elements. Cells are inserted into the generated {@code Table} in encounter order. * *

If multiple input elements map to the same row and column, the specified merging function is * used to combine the values. Like {@link * java.util.stream.Collectors#toMap(java.util.function.Function, java.util.function.Function, * BinaryOperator, java.util.function.Supplier)}, this Collector throws a {@code * NullPointerException} on null values returned from {@code valueFunction}, and treats nulls * returned from {@code mergeFunction} as removals of that row/column pair. * * @since 21.0 */ public static < T extends @Nullable Object, R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object, I extends Table> Collector toTable( java.util.function.Function rowFunction, java.util.function.Function columnFunction, java.util.function.Function valueFunction, BinaryOperator mergeFunction, java.util.function.Supplier tableSupplier) { return TableCollectors.toTable( rowFunction, columnFunction, valueFunction, mergeFunction, tableSupplier); } /** * Returns an immutable cell with the specified row key, column key, and value. * *

The returned cell is serializable. * * @param rowKey the row key to be associated with the returned cell * @param columnKey the column key to be associated with the returned cell * @param value the value to be associated with the returned cell */ public static Cell immutableCell( @ParametricNullness R rowKey, @ParametricNullness C columnKey, @ParametricNullness V value) { return new ImmutableCell<>(rowKey, columnKey, value); } static final class ImmutableCell< R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object> extends AbstractCell implements Serializable { @ParametricNullness private final R rowKey; @ParametricNullness private final C columnKey; @ParametricNullness private final V value; ImmutableCell( @ParametricNullness R rowKey, @ParametricNullness C columnKey, @ParametricNullness V value) { this.rowKey = rowKey; this.columnKey = columnKey; this.value = value; } @Override @ParametricNullness public R getRowKey() { return rowKey; } @Override @ParametricNullness public C getColumnKey() { return columnKey; } @Override @ParametricNullness public V getValue() { return value; } private static final long serialVersionUID = 0; } abstract static class AbstractCell< R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object> implements Cell { // needed for serialization AbstractCell() {} @Override public boolean equals(@CheckForNull Object obj) { if (obj == this) { return true; } if (obj instanceof Cell) { Cell other = (Cell) obj; return Objects.equal(getRowKey(), other.getRowKey()) && Objects.equal(getColumnKey(), other.getColumnKey()) && Objects.equal(getValue(), other.getValue()); } return false; } @Override public int hashCode() { return Objects.hashCode(getRowKey(), getColumnKey(), getValue()); } @Override public String toString() { return "(" + getRowKey() + "," + getColumnKey() + ")=" + getValue(); } } /** * Creates a transposed view of a given table that flips its row and column keys. In other words, * calling {@code get(columnKey, rowKey)} on the generated table always returns the same value as * calling {@code get(rowKey, columnKey)} on the original table. Updating the original table * changes the contents of the transposed table and vice versa. * *

The returned table supports update operations as long as the input table supports the * analogous operation with swapped rows and columns. For example, in a {@link HashBasedTable} * instance, {@code rowKeySet().iterator()} supports {@code remove()} but {@code * columnKeySet().iterator()} doesn't. With a transposed {@link HashBasedTable}, it's the other * way around. */ public static Table transpose(Table table) { return (table instanceof TransposeTable) ? ((TransposeTable) table).original : new TransposeTable(table); } private static class TransposeTable< C extends @Nullable Object, R extends @Nullable Object, V extends @Nullable Object> extends AbstractTable { final Table original; TransposeTable(Table original) { this.original = checkNotNull(original); } @Override public void clear() { original.clear(); } @Override public Map column(@ParametricNullness R columnKey) { return original.row(columnKey); } @Override public Set columnKeySet() { return original.rowKeySet(); } @Override public Map> columnMap() { return original.rowMap(); } @Override public boolean contains(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { return original.contains(columnKey, rowKey); } @Override public boolean containsColumn(@CheckForNull Object columnKey) { return original.containsRow(columnKey); } @Override public boolean containsRow(@CheckForNull Object rowKey) { return original.containsColumn(rowKey); } @Override public boolean containsValue(@CheckForNull Object value) { return original.containsValue(value); } @Override @CheckForNull public V get(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { return original.get(columnKey, rowKey); } @Override @CheckForNull public V put( @ParametricNullness C rowKey, @ParametricNullness R columnKey, @ParametricNullness V value) { return original.put(columnKey, rowKey, value); } @Override public void putAll(Table table) { original.putAll(transpose(table)); } @Override @CheckForNull public V remove(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { return original.remove(columnKey, rowKey); } @Override public Map row(@ParametricNullness C rowKey) { return original.column(rowKey); } @Override public Set rowKeySet() { return original.columnKeySet(); } @Override public Map> rowMap() { return original.columnMap(); } @Override public int size() { return original.size(); } @Override public Collection values() { return original.values(); } // Will cast TRANSPOSE_CELL to a type that always succeeds private static final Function TRANSPOSE_CELL = new Function, Cell>() { @Override public Cell apply(Cell cell) { return immutableCell(cell.getColumnKey(), cell.getRowKey(), cell.getValue()); } }; @SuppressWarnings("unchecked") @Override Iterator> cellIterator() { return Iterators.transform( original.cellSet().iterator(), (Function, Cell>) TRANSPOSE_CELL); } @SuppressWarnings("unchecked") @Override Spliterator> cellSpliterator() { return CollectSpliterators.map( original.cellSet().spliterator(), (Function, Cell>) TRANSPOSE_CELL); } } /** * Creates a table that uses the specified backing map and factory. It can generate a table based * on arbitrary {@link Map} classes. * *

The {@code factory}-generated and {@code backingMap} classes determine the table iteration * order. However, the table's {@code row()} method returns instances of a different class than * {@code factory.get()} does. * *

Call this method only when the simpler factory methods in classes like {@link * HashBasedTable} and {@link TreeBasedTable} won't suffice. * *

The views returned by the {@code Table} methods {@link Table#column}, {@link * Table#columnKeySet}, and {@link Table#columnMap} have iterators that don't support {@code * remove()}. Otherwise, all optional operations are supported. Null row keys, columns keys, and * values are not supported. * *

Lookups by row key are often faster than lookups by column key, because the data is stored * in a {@code Map>}. A method call like {@code column(columnKey).get(rowKey)} still * runs quickly, since the row key is provided. However, {@code column(columnKey).size()} takes * longer, since an iteration across all row keys occurs. * *

Note that this implementation is not synchronized. If multiple threads access this table * concurrently and one of the threads modifies the table, it must be synchronized externally. * *

The table is serializable if {@code backingMap}, {@code factory}, the maps generated by * {@code factory}, and the table contents are all serializable. * *

Note: the table assumes complete ownership over of {@code backingMap} and the maps returned * by {@code factory}. Those objects should not be manually updated and they should not use soft, * weak, or phantom references. * * @param backingMap place to store the mapping from each row key to its corresponding column key * / value map * @param factory supplier of new, empty maps that will each hold all column key / value mappings * for a given row key * @throws IllegalArgumentException if {@code backingMap} is not empty * @since 10.0 */ public static Table newCustomTable( Map> backingMap, Supplier> factory) { checkArgument(backingMap.isEmpty()); checkNotNull(factory); // TODO(jlevy): Wrap factory to validate that the supplied maps are empty? return new StandardTable<>(backingMap, factory); } /** * Returns a view of a table where each value is transformed by a function. All other properties * of the table, such as iteration order, are left intact. * *

Changes in the underlying table are reflected in this view. Conversely, this view supports * removal operations, and these are reflected in the underlying table. * *

It's acceptable for the underlying table to contain null keys, and even null values provided * that the function is capable of accepting null input. The transformed table might contain null * values, if the function sometimes gives a null result. * *

The returned table is not thread-safe or serializable, even if the underlying table is. * *

The function is applied lazily, invoked when needed. This is necessary for the returned * table to be a view, but it means that the function will be applied many times for bulk * operations like {@link Table#containsValue} and {@code Table.toString()}. For this to perform * well, {@code function} should be fast. To avoid lazy evaluation when the returned table doesn't * need to be a view, copy the returned table into a new table of your choosing. * * @since 10.0 */ public static < R extends @Nullable Object, C extends @Nullable Object, V1 extends @Nullable Object, V2 extends @Nullable Object> Table transformValues( Table fromTable, Function function) { return new TransformedTable<>(fromTable, function); } private static class TransformedTable< R extends @Nullable Object, C extends @Nullable Object, V1 extends @Nullable Object, V2 extends @Nullable Object> extends AbstractTable { final Table fromTable; final Function function; TransformedTable(Table fromTable, Function function) { this.fromTable = checkNotNull(fromTable); this.function = checkNotNull(function); } @Override public boolean contains(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { return fromTable.contains(rowKey, columnKey); } @Override @CheckForNull public V2 get(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { // The function is passed a null input only when the table contains a null // value. // The cast is safe because of the contains() check. return contains(rowKey, columnKey) ? function.apply(uncheckedCastNullableTToT(fromTable.get(rowKey, columnKey))) : null; } @Override public int size() { return fromTable.size(); } @Override public void clear() { fromTable.clear(); } @Override @CheckForNull public V2 put( @ParametricNullness R rowKey, @ParametricNullness C columnKey, @ParametricNullness V2 value) { throw new UnsupportedOperationException(); } @Override public void putAll(Table table) { throw new UnsupportedOperationException(); } @Override @CheckForNull public V2 remove(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { return contains(rowKey, columnKey) // The cast is safe because of the contains() check. ? function.apply(uncheckedCastNullableTToT(fromTable.remove(rowKey, columnKey))) : null; } @Override public Map row(@ParametricNullness R rowKey) { return Maps.transformValues(fromTable.row(rowKey), function); } @Override public Map column(@ParametricNullness C columnKey) { return Maps.transformValues(fromTable.column(columnKey), function); } Function, Cell> cellFunction() { return new Function, Cell>() { @Override public Cell apply(Cell cell) { return immutableCell( cell.getRowKey(), cell.getColumnKey(), function.apply(cell.getValue())); } }; } @Override Iterator> cellIterator() { return Iterators.transform(fromTable.cellSet().iterator(), cellFunction()); } @Override Spliterator> cellSpliterator() { return CollectSpliterators.map(fromTable.cellSet().spliterator(), cellFunction()); } @Override public Set rowKeySet() { return fromTable.rowKeySet(); } @Override public Set columnKeySet() { return fromTable.columnKeySet(); } @Override Collection createValues() { return Collections2.transform(fromTable.values(), function); } @Override public Map> rowMap() { Function, Map> rowFunction = new Function, Map>() { @Override public Map apply(Map row) { return Maps.transformValues(row, function); } }; return Maps.transformValues(fromTable.rowMap(), rowFunction); } @Override public Map> columnMap() { Function, Map> columnFunction = new Function, Map>() { @Override public Map apply(Map column) { return Maps.transformValues(column, function); } }; return Maps.transformValues(fromTable.columnMap(), columnFunction); } } /** * Returns an unmodifiable view of the specified table. This method allows modules to provide * users with "read-only" access to internal tables. Query operations on the returned table "read * through" to the specified table, and attempts to modify the returned table, whether direct or * via its collection views, result in an {@code UnsupportedOperationException}. * *

The returned table will be serializable if the specified table is serializable. * *

Consider using an {@link ImmutableTable}, which is guaranteed never to change. * * @since 11.0 */ public static Table unmodifiableTable(Table table) { return new UnmodifiableTable<>(table); } private static class UnmodifiableTable< R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object> extends ForwardingTable implements Serializable { final Table delegate; UnmodifiableTable(Table delegate) { this.delegate = checkNotNull(delegate); } @SuppressWarnings("unchecked") // safe, covariant cast @Override protected Table delegate() { return (Table) delegate; } @Override public Set> cellSet() { return Collections.unmodifiableSet(super.cellSet()); } @Override public void clear() { throw new UnsupportedOperationException(); } @Override public Map column(@ParametricNullness C columnKey) { return Collections.unmodifiableMap(super.column(columnKey)); } @Override public Set columnKeySet() { return Collections.unmodifiableSet(super.columnKeySet()); } @Override public Map> columnMap() { Function, Map> wrapper = unmodifiableWrapper(); return Collections.unmodifiableMap(Maps.transformValues(super.columnMap(), wrapper)); } @Override @CheckForNull public V put( @ParametricNullness R rowKey, @ParametricNullness C columnKey, @ParametricNullness V value) { throw new UnsupportedOperationException(); } @Override public void putAll(Table table) { throw new UnsupportedOperationException(); } @Override @CheckForNull public V remove(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { throw new UnsupportedOperationException(); } @Override public Map row(@ParametricNullness R rowKey) { return Collections.unmodifiableMap(super.row(rowKey)); } @Override public Set rowKeySet() { return Collections.unmodifiableSet(super.rowKeySet()); } @Override public Map> rowMap() { Function, Map> wrapper = unmodifiableWrapper(); return Collections.unmodifiableMap(Maps.transformValues(super.rowMap(), wrapper)); } @Override public Collection values() { return Collections.unmodifiableCollection(super.values()); } private static final long serialVersionUID = 0; } /** * Returns an unmodifiable view of the specified row-sorted table. This method allows modules to * provide users with "read-only" access to internal tables. Query operations on the returned * table "read through" to the specified table, and attempts to modify the returned table, whether * direct or via its collection views, result in an {@code UnsupportedOperationException}. * *

The returned table will be serializable if the specified table is serializable. * * @param table the row-sorted table for which an unmodifiable view is to be returned * @return an unmodifiable view of the specified table * @since 11.0 */ public static RowSortedTable unmodifiableRowSortedTable( RowSortedTable table) { /* * It's not ? extends R, because it's technically not covariant in R. Specifically, * table.rowMap().comparator() could return a comparator that only works for the ? extends R. * Collections.unmodifiableSortedMap makes the same distinction. */ return new UnmodifiableRowSortedMap<>(table); } private static final class UnmodifiableRowSortedMap< R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object> extends UnmodifiableTable implements RowSortedTable { public UnmodifiableRowSortedMap(RowSortedTable delegate) { super(delegate); } @Override protected RowSortedTable delegate() { return (RowSortedTable) super.delegate(); } @Override public SortedMap> rowMap() { Function, Map> wrapper = unmodifiableWrapper(); return Collections.unmodifiableSortedMap(Maps.transformValues(delegate().rowMap(), wrapper)); } @Override public SortedSet rowKeySet() { return Collections.unmodifiableSortedSet(delegate().rowKeySet()); } private static final long serialVersionUID = 0; } @SuppressWarnings("unchecked") private static Function, Map> unmodifiableWrapper() { return (Function) UNMODIFIABLE_WRAPPER; } private static final Function, ? extends Map> UNMODIFIABLE_WRAPPER = new Function, Map>() { @Override public Map apply(Map input) { return Collections.unmodifiableMap(input); } }; /** * Returns a synchronized (thread-safe) table backed by the specified table. In order to guarantee * serial access, it is critical that all access to the backing table is accomplished * through the returned table. * *

It is imperative that the user manually synchronize on the returned table when accessing any * of its collection views: * *

{@code
   * Table table = Tables.synchronizedTable(HashBasedTable.create());
   * ...
   * Map row = table.row(rowKey);  // Needn't be in synchronized block
   * ...
   * synchronized (table) {  // Synchronizing on table, not row!
   *   Iterator> i = row.entrySet().iterator(); // Must be in synchronized block
   *   while (i.hasNext()) {
   *     foo(i.next());
   *   }
   * }
   * }
* *

Failure to follow this advice may result in non-deterministic behavior. * *

The returned table will be serializable if the specified table is serializable. * * @param table the table to be wrapped in a synchronized view * @return a synchronized view of the specified table * @since 22.0 */ public static Table synchronizedTable(Table table) { return Synchronized.table(table, null); } static boolean equalsImpl(Table table, @CheckForNull Object obj) { if (obj == table) { return true; } else if (obj instanceof Table) { Table that = (Table) obj; return table.cellSet().equals(that.cellSet()); } else { return false; } } }