• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 
15 package com.google.common.collect;
16 
17 import static java.util.Objects.requireNonNull;
18 
19 import com.google.common.annotations.GwtCompatible;
20 import com.google.common.collect.ImmutableMap.IteratorBasedImmutableMap;
21 import com.google.errorprone.annotations.Immutable;
22 import com.google.j2objc.annotations.WeakOuter;
23 import java.util.Map;
24 import javax.annotation.CheckForNull;
25 import org.checkerframework.checker.nullness.qual.Nullable;
26 
27 /** A {@code RegularImmutableTable} optimized for dense data. */
28 @GwtCompatible
29 @Immutable(containerOf = {"R", "C", "V"})
30 @ElementTypesAreNonnullByDefault
31 final class DenseImmutableTable<R, C, V> extends RegularImmutableTable<R, C, V> {
32   private final ImmutableMap<R, Integer> rowKeyToIndex;
33   private final ImmutableMap<C, Integer> columnKeyToIndex;
34   private final ImmutableMap<R, ImmutableMap<C, V>> rowMap;
35   private final ImmutableMap<C, ImmutableMap<R, V>> columnMap;
36 
37   @SuppressWarnings("Immutable") // We don't modify this after construction.
38   private final int[] rowCounts;
39 
40   @SuppressWarnings("Immutable") // We don't modify this after construction.
41   private final int[] columnCounts;
42 
43   @SuppressWarnings("Immutable") // We don't modify this after construction.
44   private final @Nullable V[][] values;
45 
46   // For each cell in iteration order, the index of that cell's row key in the row key list.
47   @SuppressWarnings("Immutable") // We don't modify this after construction.
48   private final int[] cellRowIndices;
49 
50   // For each cell in iteration order, the index of that cell's column key in the column key list.
51   @SuppressWarnings("Immutable") // We don't modify this after construction.
52   private final int[] cellColumnIndices;
53 
DenseImmutableTable( ImmutableList<Cell<R, C, V>> cellList, ImmutableSet<R> rowSpace, ImmutableSet<C> columnSpace)54   DenseImmutableTable(
55       ImmutableList<Cell<R, C, V>> cellList,
56       ImmutableSet<R> rowSpace,
57       ImmutableSet<C> columnSpace) {
58     @SuppressWarnings("unchecked")
59     @Nullable
60     V[][] array = (@Nullable V[][]) new Object[rowSpace.size()][columnSpace.size()];
61     this.values = array;
62     this.rowKeyToIndex = Maps.indexMap(rowSpace);
63     this.columnKeyToIndex = Maps.indexMap(columnSpace);
64     rowCounts = new int[rowKeyToIndex.size()];
65     columnCounts = new int[columnKeyToIndex.size()];
66     int[] cellRowIndices = new int[cellList.size()];
67     int[] cellColumnIndices = new int[cellList.size()];
68     for (int i = 0; i < cellList.size(); i++) {
69       Cell<R, C, V> cell = cellList.get(i);
70       R rowKey = cell.getRowKey();
71       C columnKey = cell.getColumnKey();
72       // The requireNonNull calls are safe because we construct the indexes with indexMap.
73       int rowIndex = requireNonNull(rowKeyToIndex.get(rowKey));
74       int columnIndex = requireNonNull(columnKeyToIndex.get(columnKey));
75       V existingValue = values[rowIndex][columnIndex];
76       checkNoDuplicate(rowKey, columnKey, existingValue, cell.getValue());
77       values[rowIndex][columnIndex] = cell.getValue();
78       rowCounts[rowIndex]++;
79       columnCounts[columnIndex]++;
80       cellRowIndices[i] = rowIndex;
81       cellColumnIndices[i] = columnIndex;
82     }
83     this.cellRowIndices = cellRowIndices;
84     this.cellColumnIndices = cellColumnIndices;
85     this.rowMap = new RowMap();
86     this.columnMap = new ColumnMap();
87   }
88 
89   /** An immutable map implementation backed by an indexed nullable array. */
90   private abstract static class ImmutableArrayMap<K, V> extends IteratorBasedImmutableMap<K, V> {
91     private final int size;
92 
ImmutableArrayMap(int size)93     ImmutableArrayMap(int size) {
94       this.size = size;
95     }
96 
keyToIndex()97     abstract ImmutableMap<K, Integer> keyToIndex();
98 
99     // True if getValue never returns null.
isFull()100     private boolean isFull() {
101       return size == keyToIndex().size();
102     }
103 
getKey(int index)104     K getKey(int index) {
105       return keyToIndex().keySet().asList().get(index);
106     }
107 
108     @CheckForNull
getValue(int keyIndex)109     abstract V getValue(int keyIndex);
110 
111     @Override
createKeySet()112     ImmutableSet<K> createKeySet() {
113       return isFull() ? keyToIndex().keySet() : super.createKeySet();
114     }
115 
116     @Override
size()117     public int size() {
118       return size;
119     }
120 
121     @Override
122     @CheckForNull
get(@heckForNull Object key)123     public V get(@CheckForNull Object key) {
124       Integer keyIndex = keyToIndex().get(key);
125       return (keyIndex == null) ? null : getValue(keyIndex);
126     }
127 
128     @Override
entryIterator()129     UnmodifiableIterator<Entry<K, V>> entryIterator() {
130       return new AbstractIterator<Entry<K, V>>() {
131         private int index = -1;
132         private final int maxIndex = keyToIndex().size();
133 
134         @Override
135         @CheckForNull
136         protected Entry<K, V> computeNext() {
137           for (index++; index < maxIndex; index++) {
138             V value = getValue(index);
139             if (value != null) {
140               return Maps.immutableEntry(getKey(index), value);
141             }
142           }
143           return endOfData();
144         }
145       };
146     }
147   }
148 
149   private final class Row extends ImmutableArrayMap<C, V> {
150     private final int rowIndex;
151 
Row(int rowIndex)152     Row(int rowIndex) {
153       super(rowCounts[rowIndex]);
154       this.rowIndex = rowIndex;
155     }
156 
157     @Override
keyToIndex()158     ImmutableMap<C, Integer> keyToIndex() {
159       return columnKeyToIndex;
160     }
161 
162     @Override
163     @CheckForNull
getValue(int keyIndex)164     V getValue(int keyIndex) {
165       return values[rowIndex][keyIndex];
166     }
167 
168     @Override
isPartialView()169     boolean isPartialView() {
170       return true;
171     }
172   }
173 
174   private final class Column extends ImmutableArrayMap<R, V> {
175     private final int columnIndex;
176 
Column(int columnIndex)177     Column(int columnIndex) {
178       super(columnCounts[columnIndex]);
179       this.columnIndex = columnIndex;
180     }
181 
182     @Override
keyToIndex()183     ImmutableMap<R, Integer> keyToIndex() {
184       return rowKeyToIndex;
185     }
186 
187     @Override
188     @CheckForNull
getValue(int keyIndex)189     V getValue(int keyIndex) {
190       return values[keyIndex][columnIndex];
191     }
192 
193     @Override
isPartialView()194     boolean isPartialView() {
195       return true;
196     }
197   }
198 
199   @WeakOuter
200   private final class RowMap extends ImmutableArrayMap<R, ImmutableMap<C, V>> {
RowMap()201     private RowMap() {
202       super(rowCounts.length);
203     }
204 
205     @Override
keyToIndex()206     ImmutableMap<R, Integer> keyToIndex() {
207       return rowKeyToIndex;
208     }
209 
210     @Override
getValue(int keyIndex)211     ImmutableMap<C, V> getValue(int keyIndex) {
212       return new Row(keyIndex);
213     }
214 
215     @Override
isPartialView()216     boolean isPartialView() {
217       return false;
218     }
219   }
220 
221   @WeakOuter
222   private final class ColumnMap extends ImmutableArrayMap<C, ImmutableMap<R, V>> {
ColumnMap()223     private ColumnMap() {
224       super(columnCounts.length);
225     }
226 
227     @Override
keyToIndex()228     ImmutableMap<C, Integer> keyToIndex() {
229       return columnKeyToIndex;
230     }
231 
232     @Override
getValue(int keyIndex)233     ImmutableMap<R, V> getValue(int keyIndex) {
234       return new Column(keyIndex);
235     }
236 
237     @Override
isPartialView()238     boolean isPartialView() {
239       return false;
240     }
241   }
242 
243   @Override
columnMap()244   public ImmutableMap<C, Map<R, V>> columnMap() {
245     // Casts without copying.
246     ImmutableMap<C, ImmutableMap<R, V>> columnMap = this.columnMap;
247     return ImmutableMap.<C, Map<R, V>>copyOf(columnMap);
248   }
249 
250   @Override
rowMap()251   public ImmutableMap<R, Map<C, V>> rowMap() {
252     // Casts without copying.
253     ImmutableMap<R, ImmutableMap<C, V>> rowMap = this.rowMap;
254     return ImmutableMap.<R, Map<C, V>>copyOf(rowMap);
255   }
256 
257   @Override
258   @CheckForNull
get(@heckForNull Object rowKey, @CheckForNull Object columnKey)259   public V get(@CheckForNull Object rowKey, @CheckForNull Object columnKey) {
260     Integer rowIndex = rowKeyToIndex.get(rowKey);
261     Integer columnIndex = columnKeyToIndex.get(columnKey);
262     return ((rowIndex == null) || (columnIndex == null)) ? null : values[rowIndex][columnIndex];
263   }
264 
265   @Override
size()266   public int size() {
267     return cellRowIndices.length;
268   }
269 
270   @Override
getCell(int index)271   Cell<R, C, V> getCell(int index) {
272     int rowIndex = cellRowIndices[index];
273     int columnIndex = cellColumnIndices[index];
274     R rowKey = rowKeySet().asList().get(rowIndex);
275     C columnKey = columnKeySet().asList().get(columnIndex);
276     // requireNonNull is safe because we use indexes that were populated by the constructor.
277     V value = requireNonNull(values[rowIndex][columnIndex]);
278     return cellOf(rowKey, columnKey, value);
279   }
280 
281   @Override
getValue(int index)282   V getValue(int index) {
283     // requireNonNull is safe because we use indexes that were populated by the constructor.
284     return requireNonNull(values[cellRowIndices[index]][cellColumnIndices[index]]);
285   }
286 
287   @Override
createSerializedForm()288   SerializedForm createSerializedForm() {
289     return SerializedForm.create(this, cellRowIndices, cellColumnIndices);
290   }
291 }
292