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