![JAR search and dependency download from the Maven repository](/logo.png)
com.google.common.collect.RegularImmutableTable Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of guava Show documentation
Show all versions of guava Show documentation
Guava is a suite of core and expanded libraries that include
utility classes, google's collections, io classes, and much
much more.
Guava has two code dependencies - javax.annotation
per the JSR-305 spec and javax.inject per the JSR-330 spec.
This is a fork of Guava 14.0.1 for Hudson.
The newest version!
/*
* Copyright (C) 2009 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 com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Objects;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
/**
* An implementation of {@link ImmutableTable} holding an arbitrary number of
* cells.
*
* @author Gregory Kick
*/
@GwtCompatible
abstract class RegularImmutableTable extends ImmutableTable {
private RegularImmutableTable() {}
private transient ImmutableCollection values;
@Override public final ImmutableCollection values() {
ImmutableCollection result = values;
return (result == null) ? values = createValues() : result;
}
abstract ImmutableCollection createValues();
@Override public abstract int size();
@Override public final boolean containsValue(@Nullable Object value) {
return values().contains(value);
}
private transient ImmutableSet> cellSet;
@Override
public final ImmutableSet> cellSet() {
ImmutableSet> result = cellSet;
return (result == null) ? cellSet = createCellSet() : result;
}
abstract ImmutableSet> createCellSet();
abstract class CellSet extends ImmutableSet> {
@Override
public int size() {
return RegularImmutableTable.this.size();
}
@Override
public boolean contains(@Nullable Object object) {
if (object instanceof Cell) {
Cell, ?, ?> cell = (Cell, ?, ?>) object;
Object value = get(cell.getRowKey(), cell.getColumnKey());
return value != null && value.equals(cell.getValue());
}
return false;
}
@Override
boolean isPartialView() {
return false;
}
}
@Override public final boolean isEmpty() {
return false;
}
static final RegularImmutableTable forCells(
List> cells,
@Nullable final Comparator super R> rowComparator,
@Nullable final Comparator super C> columnComparator) {
checkNotNull(cells);
if (rowComparator != null || columnComparator != null) {
/*
* This sorting logic leads to a cellSet() ordering that may not be
* expected and that isn't documented in the Javadoc. If a row Comparator
* is provided, cellSet() iterates across the columns in the first row,
* the columns in the second row, etc. If a column Comparator is provided
* but a row Comparator isn't, cellSet() iterates across the rows in the
* first column, the rows in the second column, etc.
*/
Comparator> comparator = new Comparator>() {
@Override public int compare(Cell cell1, Cell cell2) {
int rowCompare = (rowComparator == null) ? 0
: rowComparator.compare(cell1.getRowKey(), cell2.getRowKey());
if (rowCompare != 0) {
return rowCompare;
}
return (columnComparator == null) ? 0
: columnComparator.compare(
cell1.getColumnKey(), cell2.getColumnKey());
}
};
Collections.sort(cells, comparator);
}
return forCellsInternal(cells, rowComparator, columnComparator);
}
static final RegularImmutableTable forCells(
Iterable> cells) {
return forCellsInternal(cells, null, null);
}
/**
* A factory that chooses the most space-efficient representation of the
* table.
*/
private static final RegularImmutableTable
forCellsInternal(Iterable> cells,
@Nullable Comparator super R> rowComparator,
@Nullable Comparator super C> columnComparator) {
ImmutableSet.Builder rowSpaceBuilder = ImmutableSet.builder();
ImmutableSet.Builder columnSpaceBuilder = ImmutableSet.builder();
ImmutableList> cellList = ImmutableList.copyOf(cells);
for (Cell cell : cellList) {
rowSpaceBuilder.add(cell.getRowKey());
columnSpaceBuilder.add(cell.getColumnKey());
}
ImmutableSet rowSpace = rowSpaceBuilder.build();
if (rowComparator != null) {
List rowList = Lists.newArrayList(rowSpace);
Collections.sort(rowList, rowComparator);
rowSpace = ImmutableSet.copyOf(rowList);
}
ImmutableSet columnSpace = columnSpaceBuilder.build();
if (columnComparator != null) {
List columnList = Lists.newArrayList(columnSpace);
Collections.sort(columnList, columnComparator);
columnSpace = ImmutableSet.copyOf(columnList);
}
// use a dense table if more than half of the cells have values
// TODO(gak): tune this condition based on empirical evidence
return (cellList.size() > ((rowSpace.size() * columnSpace.size()) / 2)) ?
new DenseImmutableTable(cellList, rowSpace, columnSpace) :
new SparseImmutableTable(cellList, rowSpace, columnSpace);
}
/**
* A {@code RegularImmutableTable} optimized for sparse data.
*/
@Immutable
@VisibleForTesting
static final class SparseImmutableTable
extends RegularImmutableTable {
private final ImmutableMap> rowMap;
private final ImmutableMap> columnMap;
private final int[] iterationOrderRow;
private final int[] iterationOrderColumn;
SparseImmutableTable(ImmutableList> cellList,
ImmutableSet rowSpace, ImmutableSet columnSpace) {
Map rowIndex = Maps.newHashMap();
Map> rows = Maps.newLinkedHashMap();
for (R row : rowSpace) {
rowIndex.put(row, rows.size());
rows.put(row, new LinkedHashMap());
}
Map> columns = Maps.newLinkedHashMap();
for (C col : columnSpace) {
columns.put(col, new LinkedHashMap());
}
int[] iterationOrderRow = new int[cellList.size()];
int[] iterationOrderColumn = new int[cellList.size()];
for (int i = 0; i < cellList.size(); i++) {
Cell cell = cellList.get(i);
R rowKey = cell.getRowKey();
C columnKey = cell.getColumnKey();
V value = cell.getValue();
iterationOrderRow[i] = rowIndex.get(rowKey);
Map thisRow = rows.get(rowKey);
iterationOrderColumn[i] = thisRow.size();
V oldValue = thisRow.put(columnKey, value);
if (oldValue != null) {
throw new IllegalArgumentException("Duplicate value for row=" + rowKey + ", column="
+ columnKey + ": " + value + ", " + oldValue);
}
columns.get(columnKey).put(rowKey, value);
}
this.iterationOrderRow = iterationOrderRow;
this.iterationOrderColumn = iterationOrderColumn;
ImmutableMap.Builder> rowBuilder = ImmutableMap.builder();
for (Map.Entry> row : rows.entrySet()) {
rowBuilder.put(row.getKey(), ImmutableMap.copyOf(row.getValue()));
}
this.rowMap = rowBuilder.build();
ImmutableMap.Builder> columnBuilder = ImmutableMap.builder();
for (Map.Entry> col : columns.entrySet()) {
columnBuilder.put(col.getKey(), ImmutableMap.copyOf(col.getValue()));
}
this.columnMap = columnBuilder.build();
}
@Override public ImmutableMap column(C columnKey) {
checkNotNull(columnKey);
// value maps are guaranteed to be immutable maps
return Objects.firstNonNull((ImmutableMap) columnMap.get(columnKey),
ImmutableMap.of());
}
@Override public ImmutableSet columnKeySet() {
return columnMap.keySet();
}
@Override public ImmutableMap> columnMap() {
return columnMap;
}
@Override public ImmutableMap row(R rowKey) {
checkNotNull(rowKey);
// value maps are guaranteed to be immutable maps
return Objects.firstNonNull((ImmutableMap) rowMap.get(rowKey),
ImmutableMap.of());
}
@Override public ImmutableSet rowKeySet() {
return rowMap.keySet();
}
@Override public ImmutableMap> rowMap() {
return rowMap;
}
@Override public boolean contains(@Nullable Object rowKey,
@Nullable Object columnKey) {
Map row = rowMap.get(rowKey);
return (row != null) && row.containsKey(columnKey);
}
@Override public boolean containsColumn(@Nullable Object columnKey) {
return columnMap.containsKey(columnKey);
}
@Override public boolean containsRow(@Nullable Object rowKey) {
return rowMap.containsKey(rowKey);
}
@Override public V get(@Nullable Object rowKey,
@Nullable Object columnKey) {
Map row = rowMap.get(rowKey);
return (row == null) ? null : row.get(columnKey);
}
@Override
ImmutableCollection createValues() {
return new ImmutableList() {
@Override
public int size() {
return iterationOrderRow.length;
}
@Override
public V get(int index) {
int rowIndex = iterationOrderRow[index];
ImmutableMap row = (ImmutableMap) rowMap.values().asList().get(rowIndex);
int columnIndex = iterationOrderColumn[index];
return row.values().asList().get(columnIndex);
}
@Override
boolean isPartialView() {
return true;
}
};
}
@Override
public int size() {
return iterationOrderRow.length;
}
@Override
ImmutableSet> createCellSet() {
return new SparseCellSet();
}
class SparseCellSet extends CellSet {
@Override
public UnmodifiableIterator> iterator() {
return asList().iterator();
}
@Override
ImmutableList> createAsList() {
return new ImmutableAsList>() {
@Override
public Cell get(int index) {
int rowIndex = iterationOrderRow[index];
Map.Entry> rowEntry = rowMap.entrySet().asList().get(rowIndex);
ImmutableMap row = (ImmutableMap) rowEntry.getValue();
int columnIndex = iterationOrderColumn[index];
Map.Entry colEntry = row.entrySet().asList().get(columnIndex);
return Tables.immutableCell(rowEntry.getKey(), colEntry.getKey(), colEntry.getValue());
}
@Override
ImmutableCollection> delegateCollection() {
return SparseCellSet.this;
}
};
}
}
}
/**
* An immutable map implementation backed by an indexed nullable array, used in
* DenseImmutableTable.
*/
private abstract static class ImmutableArrayMap extends ImmutableMap {
private final int size;
ImmutableArrayMap(int size) {
this.size = size;
}
abstract ImmutableMap keyToIndex();
// True if getValue never returns null.
private boolean isFull() {
return size == keyToIndex().size();
}
K getKey(int index) {
return keyToIndex().keySet().asList().get(index);
}
@Nullable abstract V getValue(int keyIndex);
@Override
ImmutableSet createKeySet() {
return isFull() ? keyToIndex().keySet() : super.createKeySet();
}
@Override
public int size() {
return size;
}
@Override
public V get(@Nullable Object key) {
Integer keyIndex = keyToIndex().get(key);
return (keyIndex == null) ? null : getValue(keyIndex);
}
@Override
ImmutableSet> createEntrySet() {
if (isFull()) {
return new ImmutableMapEntrySet() {
@Override ImmutableMap map() {
return ImmutableArrayMap.this;
}
@Override
public UnmodifiableIterator> iterator() {
return new AbstractIndexedListIterator>(size()) {
@Override
protected Entry get(int index) {
return Maps.immutableEntry(getKey(index), getValue(index));
}
};
}
};
} else {
return new ImmutableMapEntrySet() {
@Override ImmutableMap map() {
return ImmutableArrayMap.this;
}
@Override
public UnmodifiableIterator> iterator() {
return new AbstractIterator>() {
private int index = -1;
private final int maxIndex = keyToIndex().size();
@Override
protected Entry computeNext() {
for (index++; index < maxIndex; index++) {
V value = getValue(index);
if (value != null) {
return Maps.immutableEntry(getKey(index), value);
}
}
return endOfData();
}
};
}
};
}
}
}
/**
* A {@code RegularImmutableTable} optimized for dense data.
*/
@Immutable @VisibleForTesting
static final class DenseImmutableTable
extends RegularImmutableTable {
private final ImmutableMap rowKeyToIndex;
private final ImmutableMap columnKeyToIndex;
private final ImmutableMap> rowMap;
private final ImmutableMap> columnMap;
private final int[] rowCounts;
private final int[] columnCounts;
private final V[][] values;
private final int[] iterationOrderRow;
private final int[] iterationOrderColumn;
private static ImmutableMap makeIndex(
ImmutableSet set) {
ImmutableMap.Builder indexBuilder =
ImmutableMap.builder();
int i = 0;
for (E key : set) {
indexBuilder.put(key, i);
i++;
}
return indexBuilder.build();
}
DenseImmutableTable(ImmutableList> cellList,
ImmutableSet rowSpace, ImmutableSet columnSpace) {
@SuppressWarnings("unchecked")
V[][] array = (V[][]) new Object[rowSpace.size()][columnSpace.size()];
this.values = array;
this.rowKeyToIndex = makeIndex(rowSpace);
this.columnKeyToIndex = makeIndex(columnSpace);
rowCounts = new int[rowKeyToIndex.size()];
columnCounts = new int[columnKeyToIndex.size()];
int[] iterationOrderRow = new int[cellList.size()];
int[] iterationOrderColumn = new int[cellList.size()];
for (int i = 0; i < cellList.size(); i++) {
Cell cell = cellList.get(i);
R rowKey = cell.getRowKey();
C columnKey = cell.getColumnKey();
int rowIndex = rowKeyToIndex.get(rowKey);
int columnIndex = columnKeyToIndex.get(columnKey);
V existingValue = values[rowIndex][columnIndex];
checkArgument(existingValue == null, "duplicate key: (%s, %s)", rowKey,
columnKey);
values[rowIndex][columnIndex] = cell.getValue();
rowCounts[rowIndex]++;
columnCounts[columnIndex]++;
iterationOrderRow[i] = rowIndex;
iterationOrderColumn[i] = columnIndex;
}
this.iterationOrderRow = iterationOrderRow;
this.iterationOrderColumn = iterationOrderColumn;
this.rowMap = new RowMap();
this.columnMap = new ColumnMap();
}
private final class Row extends ImmutableArrayMap {
private final int rowIndex;
Row(int rowIndex) {
super(rowCounts[rowIndex]);
this.rowIndex = rowIndex;
}
@Override
ImmutableMap keyToIndex() {
return columnKeyToIndex;
}
@Override
V getValue(int keyIndex) {
return values[rowIndex][keyIndex];
}
@Override
boolean isPartialView() {
return true;
}
}
private final class Column extends ImmutableArrayMap {
private final int columnIndex;
Column(int columnIndex) {
super(columnCounts[columnIndex]);
this.columnIndex = columnIndex;
}
@Override
ImmutableMap keyToIndex() {
return rowKeyToIndex;
}
@Override
V getValue(int keyIndex) {
return values[keyIndex][columnIndex];
}
@Override
boolean isPartialView() {
return true;
}
}
private final class RowMap extends ImmutableArrayMap> {
private RowMap() {
super(rowCounts.length);
}
@Override
ImmutableMap keyToIndex() {
return rowKeyToIndex;
}
@Override
Map getValue(int keyIndex) {
return new Row(keyIndex);
}
@Override
boolean isPartialView() {
return false;
}
}
private final class ColumnMap extends ImmutableArrayMap> {
private ColumnMap() {
super(columnCounts.length);
}
@Override
ImmutableMap keyToIndex() {
return columnKeyToIndex;
}
@Override
Map getValue(int keyIndex) {
return new Column(keyIndex);
}
@Override
boolean isPartialView() {
return false;
}
}
@Override public ImmutableMap column(C columnKey) {
Integer columnIndex = columnKeyToIndex.get(checkNotNull(columnKey));
if (columnIndex == null) {
return ImmutableMap.of();
} else {
return new Column(columnIndex);
}
}
@Override public ImmutableSet columnKeySet() {
return columnKeyToIndex.keySet();
}
@Override public ImmutableMap> columnMap() {
return columnMap;
}
@Override public boolean contains(@Nullable Object rowKey,
@Nullable Object columnKey) {
return (get(rowKey, columnKey) != null);
}
@Override public boolean containsColumn(@Nullable Object columnKey) {
return columnKeyToIndex.containsKey(columnKey);
}
@Override public boolean containsRow(@Nullable Object rowKey) {
return rowKeyToIndex.containsKey(rowKey);
}
@Override public V get(@Nullable Object rowKey,
@Nullable Object columnKey) {
Integer rowIndex = rowKeyToIndex.get(rowKey);
Integer columnIndex = columnKeyToIndex.get(columnKey);
return ((rowIndex == null) || (columnIndex == null)) ? null
: values[rowIndex][columnIndex];
}
@Override public ImmutableMap row(R rowKey) {
checkNotNull(rowKey);
Integer rowIndex = rowKeyToIndex.get(rowKey);
if (rowIndex == null) {
return ImmutableMap.of();
} else {
return new Row(rowIndex);
}
}
@Override public ImmutableSet rowKeySet() {
return rowKeyToIndex.keySet();
}
@Override
public ImmutableMap> rowMap() {
return rowMap;
}
@Override
ImmutableCollection createValues() {
return new ImmutableList() {
@Override
public int size() {
return iterationOrderRow.length;
}
@Override
public V get(int index) {
return values[iterationOrderRow[index]][iterationOrderColumn[index]];
}
@Override
boolean isPartialView() {
return true;
}
};
}
@Override
public int size() {
return iterationOrderRow.length;
}
@Override
ImmutableSet> createCellSet() {
return new DenseCellSet();
}
class DenseCellSet extends CellSet {
@Override
public UnmodifiableIterator> iterator() {
return asList().iterator();
}
@Override
ImmutableList> createAsList() {
return new ImmutableAsList>() {
@Override
public Cell get(int index) {
int rowIndex = iterationOrderRow[index];
int columnIndex = iterationOrderColumn[index];
R rowKey = rowKeySet().asList().get(rowIndex);
C columnKey = columnKeySet().asList().get(columnIndex);
V value = values[rowIndex][columnIndex];
return Tables.immutableCell(rowKey, columnKey, value);
}
@Override
ImmutableCollection> delegateCollection() {
return DenseCellSet.this;
}
};
}
}
}
}
| | | | | | | | | | | | | | | | | | | | | | |
© 2015 - 2025 Weber Informatics LLC | Privacy Policy