/* * 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 com.google.common.annotations.GwtCompatible; import java.util.Map; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; /** * A {@code RegularImmutableTable} optimized for dense data. */ @GwtCompatible @Immutable 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(); } /** * An immutable map implementation backed by an indexed nullable array. */ 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() { 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(); } }; } }; } } 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> columnMap() { return columnMap; } @Override public ImmutableMap> rowMap() { return rowMap; } @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 int size() { return iterationOrderRow.length; } @Override Cell getCell(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 cellOf(rowKey, columnKey, value); } @Override V getValue(int index) { return values[iterationOrderRow[index]][iterationOrderColumn[index]]; } }