diff options
Diffstat (limited to 'guava/src/com/google/common/collect/ArrayTable.java')
-rw-r--r-- | guava/src/com/google/common/collect/ArrayTable.java | 382 |
1 files changed, 192 insertions, 190 deletions
diff --git a/guava/src/com/google/common/collect/ArrayTable.java b/guava/src/com/google/common/collect/ArrayTable.java index 554265c..28eb5b8 100644 --- a/guava/src/com/google/common/collect/ArrayTable.java +++ b/guava/src/com/google/common/collect/ArrayTable.java @@ -17,23 +17,21 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkElementIndex; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.Beta; -import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; import com.google.common.base.Objects; import java.io.Serializable; import java.lang.reflect.Array; import java.util.AbstractCollection; +import java.util.AbstractMap; import java.util.AbstractSet; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; -import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import javax.annotation.Nullable; @@ -76,15 +74,10 @@ import javax.annotation.Nullable; * implementations, synchronization is unnecessary between a thread that writes * to one cell and a thread that reads from another. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Table"> - * {@code Table}</a>. - * * @author Jared Levy * @since 10.0 */ @Beta -@GwtCompatible(emulated = true) public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { /** @@ -162,23 +155,22 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { * columnKeys is empty but rowKeys isn't, the table is empty but * containsRow() can return true and rowKeySet() isn't empty. */ - rowKeyToIndex = index(rowList); - columnKeyToIndex = index(columnList); + ImmutableMap.Builder<R, Integer> rowBuilder = ImmutableMap.builder(); + for (int i = 0; i < rowList.size(); i++) { + rowBuilder.put(rowList.get(i), i); + } + rowKeyToIndex = rowBuilder.build(); + + ImmutableMap.Builder<C, Integer> columnBuilder = ImmutableMap.builder(); + for (int i = 0; i < columnList.size(); i++) { + columnBuilder.put(columnList.get(i), i); + } + columnKeyToIndex = columnBuilder.build(); @SuppressWarnings("unchecked") V[][] tmpArray = (V[][]) new Object[rowList.size()][columnList.size()]; array = tmpArray; - // Necessary because in GWT the arrays are initialized with "undefined" instead of null. - eraseAll(); - } - - private static <E> ImmutableMap<E, Integer> index(List<E> list) { - ImmutableMap.Builder<E, Integer> columnBuilder = ImmutableMap.builder(); - for (int i = 0; i < list.size(); i++) { - columnBuilder.put(list.get(i), i); - } - return columnBuilder.build(); } private ArrayTable(Table<R, C, V> table) { @@ -194,116 +186,11 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { @SuppressWarnings("unchecked") V[][] copy = (V[][]) new Object[rowList.size()][columnList.size()]; array = copy; - // Necessary because in GWT the arrays are initialized with "undefined" instead of null. - eraseAll(); for (int i = 0; i < rowList.size(); i++) { System.arraycopy(table.array[i], 0, copy[i], 0, table.array[i].length); } } - private abstract static class ArrayMap<K, V> extends Maps.ImprovedAbstractMap<K, V> { - private final ImmutableMap<K, Integer> keyIndex; - - private ArrayMap(ImmutableMap<K, Integer> keyIndex) { - this.keyIndex = keyIndex; - } - - @Override - public Set<K> keySet() { - return keyIndex.keySet(); - } - - K getKey(int index) { - return keyIndex.keySet().asList().get(index); - } - - abstract String getKeyRole(); - - @Nullable abstract V getValue(int index); - - @Nullable abstract V setValue(int index, V newValue); - - @Override - public int size() { - return keyIndex.size(); - } - - @Override - public boolean isEmpty() { - return keyIndex.isEmpty(); - } - - @Override - protected Set<Entry<K, V>> createEntrySet() { - return new Maps.EntrySet<K, V>() { - @Override - Map<K, V> map() { - return ArrayMap.this; - } - - @Override - public Iterator<Entry<K, V>> iterator() { - return new AbstractIndexedListIterator<Entry<K, V>>(size()) { - @Override - protected Entry<K, V> get(final int index) { - return new AbstractMapEntry<K, V>() { - @Override - public K getKey() { - return ArrayMap.this.getKey(index); - } - - @Override - public V getValue() { - return ArrayMap.this.getValue(index); - } - - @Override - public V setValue(V value) { - return ArrayMap.this.setValue(index, value); - } - }; - } - }; - } - }; - } - - @Override - public boolean containsKey(@Nullable Object key) { - return keyIndex.containsKey(key); - } - - @Override - public V get(@Nullable Object key) { - Integer index = keyIndex.get(key); - if (index == null) { - return null; - } else { - return getValue(index); - } - } - - @Override - public V put(K key, V value) { - Integer index = keyIndex.get(key); - if (index == null) { - throw new IllegalArgumentException( - getKeyRole() + " " + key + " not in " + keyIndex.keySet()); - } - return setValue(index, value); - } - - @Override - public V remove(Object key) { - throw new UnsupportedOperationException(); - } - - @Override - public void clear() { - throw new UnsupportedOperationException(); - } - } - /** * Returns, as an immutable list, the row keys provided when the table was * constructed, including those that are mapped to null values only. @@ -335,9 +222,6 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { * allowed column keys */ public V at(int rowIndex, int columnIndex) { - // In GWT array access never throws IndexOutOfBoundsException. - checkElementIndex(rowIndex, rowList.size()); - checkElementIndex(columnIndex, columnList.size()); return array[rowIndex][columnIndex]; } @@ -357,9 +241,6 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { * allowed column keys */ public V set(int rowIndex, int columnIndex, @Nullable V value) { - // In GWT array access never throws IndexOutOfBoundsException. - checkElementIndex(rowIndex, rowList.size()); - checkElementIndex(columnIndex, columnList.size()); V oldValue = array[rowIndex][columnIndex]; array[rowIndex][columnIndex] = value; return oldValue; @@ -375,7 +256,6 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { * * @param valueClass class of values stored in the returned array */ - @GwtIncompatible("reflection") public V[][] toArray(Class<V> valueClass) { // Can change to use varargs in JDK 1.6 if we want @SuppressWarnings("unchecked") // TODO: safe? @@ -451,8 +331,12 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { public V get(@Nullable Object rowKey, @Nullable Object columnKey) { Integer rowIndex = rowKeyToIndex.get(rowKey); Integer columnIndex = columnKeyToIndex.get(columnKey); + return getIndexed(rowIndex, columnIndex); + } + + private V getIndexed(Integer rowIndex, Integer columnIndex) { return (rowIndex == null || columnIndex == null) - ? null : at(rowIndex, columnIndex); + ? null : array[rowIndex][columnIndex]; } /** @@ -602,7 +486,7 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { } @Override public V getValue() { - return at(rowIndex, columnIndex); + return array[rowIndex][columnIndex]; } }; } @@ -620,7 +504,7 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { Integer columnIndex = columnKeyToIndex.get(cell.getColumnKey()); return rowIndex != null && columnIndex != null - && Objects.equal(at(rowIndex, columnIndex), cell.getValue()); + && Objects.equal(array[rowIndex][columnIndex], cell.getValue()); } return false; } @@ -646,27 +530,68 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { ? ImmutableMap.<R, V>of() : new Column(columnIndex); } - private class Column extends ArrayMap<R, V> { + private class Column extends AbstractMap<R, V> { final int columnIndex; Column(int columnIndex) { - super(rowKeyToIndex); this.columnIndex = columnIndex; } - @Override - String getKeyRole() { - return "Row"; + ColumnEntrySet entrySet; + + @Override public Set<Entry<R, V>> entrySet() { + ColumnEntrySet set = entrySet; + return (set == null) ? entrySet = new ColumnEntrySet(columnIndex) : set; + } + + @Override public V get(Object rowKey) { + Integer rowIndex = rowKeyToIndex.get(rowKey); + return getIndexed(rowIndex, columnIndex); + } + + @Override public boolean containsKey(Object rowKey) { + return rowKeyToIndex.containsKey(rowKey); + } + + @Override public V put(R rowKey, V value) { + checkNotNull(rowKey); + Integer rowIndex = rowKeyToIndex.get(rowKey); + checkArgument(rowIndex != null, "Row %s not in %s", rowKey, rowList); + return set(rowIndex, columnIndex, value); + } + + @Override public Set<R> keySet() { + return rowKeySet(); + } + } + + private class ColumnEntrySet extends AbstractSet<Entry<R, V>> { + final int columnIndex; + + ColumnEntrySet(int columnIndex) { + this.columnIndex = columnIndex; } - @Override - V getValue(int index) { - return at(index, columnIndex); + @Override public Iterator<Entry<R, V>> iterator() { + return new AbstractIndexedListIterator<Entry<R, V>>(size()) { + @Override protected Entry<R, V> get(final int rowIndex) { + return new AbstractMapEntry<R, V>() { + @Override public R getKey() { + return rowList.get(rowIndex); + } + @Override public V getValue() { + return array[rowIndex][columnIndex]; + } + @Override public V setValue(V value) { + return ArrayTable.this.set(rowIndex, columnIndex, value); + } + }; + } + }; } - @Override - V setValue(int index, V newValue) { - return set(index, columnIndex, newValue); + @Override public int size() { + return rowList.size(); } } @@ -689,32 +614,47 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { return (map == null) ? columnMap = new ColumnMap() : map; } - private class ColumnMap extends ArrayMap<C, Map<R, V>> { - private ColumnMap() { - super(columnKeyToIndex); + private class ColumnMap extends AbstractMap<C, Map<R, V>> { + transient ColumnMapEntrySet entrySet; + + @Override public Set<Entry<C, Map<R, V>>> entrySet() { + ColumnMapEntrySet set = entrySet; + return (set == null) ? entrySet = new ColumnMapEntrySet() : set; } - @Override - String getKeyRole() { - return "Column"; + @Override public Map<R, V> get(Object columnKey) { + Integer columnIndex = columnKeyToIndex.get(columnKey); + return (columnIndex == null) ? null : new Column(columnIndex); } - @Override - Map<R, V> getValue(int index) { - return new Column(index); + @Override public boolean containsKey(Object columnKey) { + return containsColumn(columnKey); } - @Override - Map<R, V> setValue(int index, Map<R, V> newValue) { - throw new UnsupportedOperationException(); + @Override public Set<C> keySet() { + return columnKeySet(); } - @Override - public Map<R, V> put(C key, Map<R, V> value) { + @Override public Map<R, V> remove(Object columnKey) { throw new UnsupportedOperationException(); } } + private class ColumnMapEntrySet extends AbstractSet<Entry<C, Map<R, V>>> { + @Override public Iterator<Entry<C, Map<R, V>>> iterator() { + return new AbstractIndexedListIterator<Entry<C, Map<R, V>>>(size()) { + @Override protected Entry<C, Map<R, V>> get(int index) { + return Maps.<C, Map<R, V>>immutableEntry(columnList.get(index), + new Column(index)); + } + }; + } + + @Override public int size() { + return columnList.size(); + } + } + /** * Returns a view of all mappings that have the given row key. If the * row key isn't in {@link #rowKeySet()}, an empty immutable map is @@ -735,27 +675,69 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { return (rowIndex == null) ? ImmutableMap.<C, V>of() : new Row(rowIndex); } - private class Row extends ArrayMap<C, V> { + private class Row extends AbstractMap<C, V> { final int rowIndex; Row(int rowIndex) { - super(columnKeyToIndex); this.rowIndex = rowIndex; } - @Override - String getKeyRole() { - return "Column"; + RowEntrySet entrySet; + + @Override public Set<Entry<C, V>> entrySet() { + RowEntrySet set = entrySet; + return (set == null) ? entrySet = new RowEntrySet(rowIndex) : set; + } + + @Override public V get(Object columnKey) { + Integer columnIndex = columnKeyToIndex.get(columnKey); + return getIndexed(rowIndex, columnIndex); + } + + @Override public boolean containsKey(Object columnKey) { + return containsColumn(columnKey); + } + + @Override public V put(C columnKey, V value) { + checkNotNull(columnKey); + Integer columnIndex = columnKeyToIndex.get(columnKey); + checkArgument(columnIndex != null, + "Column %s not in %s", columnKey, columnList); + return set(rowIndex, columnIndex, value); } - @Override - V getValue(int index) { - return at(rowIndex, index); + @Override public Set<C> keySet() { + return columnKeySet(); } + } + + private class RowEntrySet extends AbstractSet<Entry<C, V>> { + final int rowIndex; - @Override - V setValue(int index, V newValue) { - return set(rowIndex, index, newValue); + RowEntrySet(int rowIndex) { + this.rowIndex = rowIndex; + } + + @Override public Iterator<Entry<C, V>> iterator() { + return new AbstractIndexedListIterator<Entry<C, V>>(size()) { + @Override protected Entry<C, V> get(final int columnIndex) { + return new AbstractMapEntry<C, V>() { + @Override public C getKey() { + return columnList.get(columnIndex); + } + @Override public V getValue() { + return array[rowIndex][columnIndex]; + } + @Override public V setValue(V value) { + return ArrayTable.this.set(rowIndex, columnIndex, value); + } + }; + } + }; + } + + @Override public int size() { + return columnList.size(); } } @@ -778,32 +760,47 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { return (map == null) ? rowMap = new RowMap() : map; } - private class RowMap extends ArrayMap<R, Map<C, V>> { - private RowMap() { - super(rowKeyToIndex); + private class RowMap extends AbstractMap<R, Map<C, V>> { + transient RowMapEntrySet entrySet; + + @Override public Set<Entry<R, Map<C, V>>> entrySet() { + RowMapEntrySet set = entrySet; + return (set == null) ? entrySet = new RowMapEntrySet() : set; } - @Override - String getKeyRole() { - return "Row"; + @Override public Map<C, V> get(Object rowKey) { + Integer rowIndex = rowKeyToIndex.get(rowKey); + return (rowIndex == null) ? null : new Row(rowIndex); } - @Override - Map<C, V> getValue(int index) { - return new Row(index); + @Override public boolean containsKey(Object rowKey) { + return containsRow(rowKey); } - @Override - Map<C, V> setValue(int index, Map<C, V> newValue) { - throw new UnsupportedOperationException(); + @Override public Set<R> keySet() { + return rowKeySet(); } - @Override - public Map<C, V> put(R key, Map<C, V> value) { + @Override public Map<C, V> remove(Object rowKey) { throw new UnsupportedOperationException(); } } + private class RowMapEntrySet extends AbstractSet<Entry<R, Map<C, V>>> { + @Override public Iterator<Entry<R, Map<C, V>>> iterator() { + return new AbstractIndexedListIterator<Entry<R, Map<C, V>>>(size()) { + @Override protected Entry<R, Map<C, V>> get(int index) { + return Maps.<R, Map<C, V>>immutableEntry(rowList.get(index), + new Row(index)); + } + }; + } + + @Override public int size() { + return rowList.size(); + } + } + private transient Collection<V> values; /** @@ -823,10 +820,11 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { private class Values extends AbstractCollection<V> { @Override public Iterator<V> iterator() { - return new TransformedIterator<Cell<R, C, V>, V>(cellSet().iterator()) { - @Override - V transform(Cell<R, C, V> cell) { - return cell.getValue(); + return new AbstractIndexedListIterator<V>(size()) { + @Override protected V get(int index) { + int rowIndex = index / columnList.size(); + int columnIndex = index % columnList.size(); + return array[rowIndex][columnIndex]; } }; } @@ -834,6 +832,10 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { @Override public int size() { return ArrayTable.this.size(); } + + @Override public boolean contains(Object value) { + return containsValue(value); + } } private static final long serialVersionUID = 0; |