001 /*
002 * Copyright (C) 2008 Google Inc.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package com.google.common.collect;
018
019 import static com.google.common.base.Preconditions.checkArgument;
020
021 import com.google.common.annotations.Beta;
022 import com.google.common.annotations.GwtCompatible;
023 import com.google.common.base.Supplier;
024
025 import java.io.Serializable;
026 import java.util.HashMap;
027 import java.util.Map;
028
029 import javax.annotation.Nullable;
030
031 /**
032 * Implementation of {@link Table} using hash tables.
033 *
034 * <p>The views returned by {@link #column}, {@link #columnKeySet()}, and {@link
035 * #columnMap()} have iterators that don't support {@code remove()}. Otherwise,
036 * all optional operations are supported. Null row keys, columns keys, and
037 * values are not supported.
038 *
039 * <p>Lookups by row key are often faster than lookups by column key, because
040 * the data is stored in a {@code Map<R, Map<C, V>>}. A method call like {@code
041 * column(columnKey).get(rowKey)} still runs quickly, since the row key is
042 * provided. However, {@code column(columnKey).size()} takes longer, since an
043 * iteration across all row keys occurs.
044 *
045 * <p>Note that this implementation is not synchronized. If multiple threads
046 * access this table concurrently and one of the threads modifies the table, it
047 * must be synchronized externally.
048 *
049 * @author Jared Levy
050 * @since 7
051 */
052 @GwtCompatible(serializable = true)
053 @Beta
054 public class HashBasedTable<R, C, V> extends StandardTable<R, C, V> {
055 private static class Factory<C, V>
056 implements Supplier<Map<C, V>>, Serializable {
057 final int expectedSize;
058 Factory(int expectedSize) {
059 this.expectedSize = expectedSize;
060 }
061 public Map<C, V> get() {
062 return Maps.newHashMapWithExpectedSize(expectedSize);
063 }
064 private static final long serialVersionUID = 0;
065 }
066
067 /**
068 * Creates an empty {@code HashBasedTable}.
069 */
070 public static <R, C, V> HashBasedTable<R, C, V> create() {
071 return new HashBasedTable<R, C, V>(
072 new HashMap<R, Map<C, V>>(), new Factory<C, V>(0));
073 }
074
075 /**
076 * Creates an empty {@code HashBasedTable} with the specified map sizes.
077 *
078 * @param expectedRows the expected number of distinct row keys
079 * @param expectedCellsPerRow the expected number of column key / value
080 * mappings in each row
081 * @throws IllegalArgumentException if {@code expectedRows} or {@code
082 * expectedCellsPerRow} is negative
083 */
084 public static <R, C, V> HashBasedTable<R, C, V> create(
085 int expectedRows, int expectedCellsPerRow) {
086 checkArgument(expectedCellsPerRow >= 0);
087 Map<R, Map<C, V>> backingMap = Maps.newHashMapWithExpectedSize(expectedRows);
088 return new HashBasedTable<R, C, V>(
089 backingMap, new Factory<C, V>(expectedCellsPerRow));
090 }
091
092 /**
093 * Creates a {@code HashBasedTable} with the same mappings as the specified
094 * table.
095 *
096 * @param table the table to copy
097 * @throws NullPointerException if any of the row keys, column keys, or values
098 * in {@code table} are null.
099 */
100 public static <R, C, V> HashBasedTable<R, C, V> create(
101 Table<? extends R, ? extends C, ? extends V> table) {
102 HashBasedTable<R, C, V> result = create();
103 result.putAll(table);
104 return result;
105 }
106
107 HashBasedTable(Map<R, Map<C, V>> backingMap, Factory<C, V> factory) {
108 super(backingMap, factory);
109 }
110
111 // Overriding so NullPointerTester test passes.
112
113 @Override public boolean contains(
114 @Nullable Object rowKey, @Nullable Object columnKey) {
115 return super.contains(rowKey, columnKey);
116 }
117
118 @Override public boolean containsColumn(@Nullable Object columnKey) {
119 return super.containsColumn(columnKey);
120 }
121
122 @Override public boolean containsRow(@Nullable Object rowKey) {
123 return super.containsRow(rowKey);
124 }
125
126 @Override public boolean containsValue(@Nullable Object value) {
127 return super.containsValue(value);
128 }
129
130 @Override public V get(@Nullable Object rowKey, @Nullable Object columnKey) {
131 return super.get(rowKey, columnKey);
132 }
133
134 @Override public boolean equals(@Nullable Object obj) {
135 return super.equals(obj);
136 }
137
138 @Override public V remove(
139 @Nullable Object rowKey, @Nullable Object columnKey) {
140 return super.remove(rowKey, columnKey);
141 }
142
143 private static final long serialVersionUID = 0;
144 }