View Javadoc
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 com.google.common.base.Preconditions.checkArgument;
18  
19  import com.google.common.annotations.GwtCompatible;
20  
21  import java.util.Map;
22  
23  import javax.annotation.Nullable;
24  import javax.annotation.concurrent.Immutable;
25  
26  /**
27   * A {@code RegularImmutableTable} optimized for dense data.
28   */
29  @GwtCompatible
30  @Immutable
31  final class DenseImmutableTable<R, C, V>
32      extends RegularImmutableTable<R, C, V> {
33    private final ImmutableMap<R, Integer> rowKeyToIndex;
34    private final ImmutableMap<C, Integer> columnKeyToIndex;
35    private final ImmutableMap<R, Map<C, V>> rowMap;
36    private final ImmutableMap<C, Map<R, V>> columnMap;
37    private final int[] rowCounts;
38    private final int[] columnCounts;
39    private final V[][] values;
40    private final int[] iterationOrderRow;
41    private final int[] iterationOrderColumn;
42  
43    private static <E> ImmutableMap<E, Integer> makeIndex(ImmutableSet<E> set) {
44      ImmutableMap.Builder<E, Integer> indexBuilder = ImmutableMap.builder();
45      int i = 0;
46      for (E key : set) {
47        indexBuilder.put(key, i);
48        i++;
49      }
50      return indexBuilder.build();
51    }
52  
53    DenseImmutableTable(ImmutableList<Cell<R, C, V>> cellList,
54        ImmutableSet<R> rowSpace, ImmutableSet<C> columnSpace) {
55      @SuppressWarnings("unchecked")
56      V[][] array = (V[][]) new Object[rowSpace.size()][columnSpace.size()];
57      this.values = array;
58      this.rowKeyToIndex = makeIndex(rowSpace);
59      this.columnKeyToIndex = makeIndex(columnSpace);
60      rowCounts = new int[rowKeyToIndex.size()];
61      columnCounts = new int[columnKeyToIndex.size()];
62      int[] iterationOrderRow = new int[cellList.size()];
63      int[] iterationOrderColumn = new int[cellList.size()];
64      for (int i = 0; i < cellList.size(); i++) {
65        Cell<R, C, V> cell = cellList.get(i);
66        R rowKey = cell.getRowKey();
67        C columnKey = cell.getColumnKey();
68        int rowIndex = rowKeyToIndex.get(rowKey);
69        int columnIndex = columnKeyToIndex.get(columnKey);
70        V existingValue = values[rowIndex][columnIndex];
71        checkArgument(existingValue == null, "duplicate key: (%s, %s)", rowKey, columnKey);
72        values[rowIndex][columnIndex] = cell.getValue();
73        rowCounts[rowIndex]++;
74        columnCounts[columnIndex]++;
75        iterationOrderRow[i] = rowIndex;
76        iterationOrderColumn[i] = columnIndex;
77      }
78      this.iterationOrderRow = iterationOrderRow;
79      this.iterationOrderColumn = iterationOrderColumn;
80      this.rowMap = new RowMap();
81      this.columnMap = new ColumnMap();
82    }
83  
84    /**
85     * An immutable map implementation backed by an indexed nullable array.
86     */
87    private abstract static class ImmutableArrayMap<K, V> extends ImmutableMap<K, V> {
88      private final int size;
89    
90      ImmutableArrayMap(int size) {
91        this.size = size;
92      }
93    
94      abstract ImmutableMap<K, Integer> keyToIndex();
95    
96      // True if getValue never returns null.
97      private boolean isFull() {
98        return size == keyToIndex().size();
99      }
100   
101     K getKey(int index) {
102       return keyToIndex().keySet().asList().get(index);
103     }
104   
105     @Nullable abstract V getValue(int keyIndex);
106   
107     @Override
108     ImmutableSet<K> createKeySet() {
109       return isFull() ? keyToIndex().keySet() : super.createKeySet();
110     }
111   
112     @Override
113     public int size() {
114       return size;
115     }
116   
117     @Override
118     public V get(@Nullable Object key) {
119       Integer keyIndex = keyToIndex().get(key);
120       return (keyIndex == null) ? null : getValue(keyIndex);
121     }
122   
123     @Override
124     ImmutableSet<Entry<K, V>> createEntrySet() {
125       return new ImmutableMapEntrySet<K, V>() {
126         @Override ImmutableMap<K, V> map() {
127           return ImmutableArrayMap.this;
128         }
129 
130         @Override
131         public UnmodifiableIterator<Entry<K, V>> iterator() {
132           return new AbstractIterator<Entry<K, V>>() {
133             private int index = -1;
134             private final int maxIndex = keyToIndex().size();
135 
136             @Override
137             protected Entry<K, V> computeNext() {
138               for (index++; index < maxIndex; index++) {
139                 V value = getValue(index);
140                 if (value != null) {
141                   return Maps.immutableEntry(getKey(index), value);
142                 }
143               }
144               return endOfData();
145             }
146           };
147         }
148       };
149     }
150   }
151 
152   private final class Row extends ImmutableArrayMap<C, V> {
153     private final int rowIndex;
154 
155     Row(int rowIndex) {
156       super(rowCounts[rowIndex]);
157       this.rowIndex = rowIndex;
158     }
159 
160     @Override
161     ImmutableMap<C, Integer> keyToIndex() {
162       return columnKeyToIndex;
163     }
164 
165     @Override
166     V getValue(int keyIndex) {
167       return values[rowIndex][keyIndex];
168     }
169 
170     @Override
171     boolean isPartialView() {
172       return true;
173     }
174   }
175 
176   private final class Column extends ImmutableArrayMap<R, V> {
177     private final int columnIndex;
178 
179     Column(int columnIndex) {
180       super(columnCounts[columnIndex]);
181       this.columnIndex = columnIndex;
182     }
183 
184     @Override
185     ImmutableMap<R, Integer> keyToIndex() {
186       return rowKeyToIndex;
187     }
188 
189     @Override
190     V getValue(int keyIndex) {
191       return values[keyIndex][columnIndex];
192     }
193 
194     @Override
195     boolean isPartialView() {
196       return true;
197     }
198   }
199 
200   private final class RowMap extends ImmutableArrayMap<R, Map<C, V>> {
201     private RowMap() {
202       super(rowCounts.length);
203     }
204 
205     @Override
206     ImmutableMap<R, Integer> keyToIndex() {
207       return rowKeyToIndex;
208     }
209 
210     @Override
211     Map<C, V> getValue(int keyIndex) {
212       return new Row(keyIndex);
213     }
214 
215     @Override
216     boolean isPartialView() {
217       return false;
218     }
219   }
220 
221   private final class ColumnMap extends ImmutableArrayMap<C, Map<R, V>> {
222     private ColumnMap() {
223       super(columnCounts.length);
224     }
225 
226     @Override
227     ImmutableMap<C, Integer> keyToIndex() {
228       return columnKeyToIndex;
229     }
230 
231     @Override
232     Map<R, V> getValue(int keyIndex) {
233       return new Column(keyIndex);
234     }
235 
236     @Override
237     boolean isPartialView() {
238       return false;
239     }
240   }
241 
242   @Override public ImmutableMap<C, Map<R, V>> columnMap() {
243     return columnMap;
244   }
245 
246   @Override
247   public ImmutableMap<R, Map<C, V>> rowMap() {
248     return rowMap;
249   }
250 
251   @Override public V get(@Nullable Object rowKey,
252       @Nullable Object columnKey) {
253     Integer rowIndex = rowKeyToIndex.get(rowKey);
254     Integer columnIndex = columnKeyToIndex.get(columnKey);
255     return ((rowIndex == null) || (columnIndex == null)) ? null
256         : values[rowIndex][columnIndex];
257   }
258 
259   @Override
260   public int size() {
261     return iterationOrderRow.length;
262   }
263 
264   @Override
265   Cell<R, C, V> getCell(int index) {
266     int rowIndex = iterationOrderRow[index];
267     int columnIndex = iterationOrderColumn[index];
268     R rowKey = rowKeySet().asList().get(rowIndex);
269     C columnKey = columnKeySet().asList().get(columnIndex);
270     V value = values[rowIndex][columnIndex];
271     return cellOf(rowKey, columnKey, value);
272   }
273 
274   @Override
275   V getValue(int index) {
276     return values[iterationOrderRow[index]][iterationOrderColumn[index]];
277   }
278 }