18
18
19
19
import static com .google .common .base .Preconditions .checkNotNull ;
20
20
import static com .google .common .collect .CollectPreconditions .checkEntryNotNull ;
21
+ import static com .google .common .collect .CollectPreconditions .checkNonnegative ;
21
22
import static com .google .common .collect .Maps .immutableEntry ;
22
23
import static java .util .Objects .requireNonNull ;
23
24
@@ -127,6 +128,19 @@ public static <K, V> Builder<K, V> builder() {
127
128
return new Builder <>();
128
129
}
129
130
131
+ /**
132
+ * Returns a new builder with a hint for how many distinct keys are expected to be added. The
133
+ * generated builder is equivalent to that returned by {@link #builder}, but may perform better if
134
+ * {@code expectedKeys} is a good estimate.
135
+ *
136
+ * @throws IllegalArgumentException if {@code expectedKeys} is negative
137
+ * @since NEXT
138
+ */
139
+ public static <K , V > Builder <K , V > builderWithExpectedKeys (int expectedKeys ) {
140
+ checkNonnegative (expectedKeys , "expectedKeys" );
141
+ return new Builder <>(expectedKeys );
142
+ }
143
+
130
144
/**
131
145
* A builder for creating immutable multimap instances, especially {@code public static final}
132
146
* multimaps ("constant multimaps"). Example:
@@ -151,13 +165,22 @@ public static class Builder<K, V> {
151
165
@ CheckForNull Map <K , ImmutableCollection .Builder <V >> builderMap ;
152
166
@ CheckForNull Comparator <? super K > keyComparator ;
153
167
@ CheckForNull Comparator <? super V > valueComparator ;
168
+ int expectedValuesPerKey = ImmutableCollection .Builder .DEFAULT_INITIAL_CAPACITY ;
154
169
155
170
/**
156
171
* Creates a new builder. The returned builder is equivalent to the builder generated by {@link
157
172
* ImmutableMultimap#builder}.
158
173
*/
159
174
public Builder () {}
160
175
176
+ /** Creates a new builder with a hint for the number of distinct keys. */
177
+ Builder (int expectedKeys ) {
178
+ if (expectedKeys > 0 ) {
179
+ builderMap = Platform .preservesInsertionOrderOnPutsMapWithExpectedSize (expectedKeys );
180
+ }
181
+ // otherwise, leave it null to be constructed lazily
182
+ }
183
+
161
184
Map <K , ImmutableCollection .Builder <V >> ensureBuilderMapNonNull () {
162
185
Map <K , ImmutableCollection .Builder <V >> result = builderMap ;
163
186
if (result == null ) {
@@ -167,8 +190,46 @@ Map<K, ImmutableCollection.Builder<V>> ensureBuilderMapNonNull() {
167
190
return result ;
168
191
}
169
192
170
- ImmutableCollection .Builder <V > newValueCollectionBuilder () {
171
- return ImmutableList .builder ();
193
+ ImmutableCollection .Builder <V > newValueCollectionBuilderWithExpectedSize (int expectedSize ) {
194
+ return ImmutableList .builderWithExpectedSize (expectedSize );
195
+ }
196
+
197
+ /**
198
+ * Provides a hint for how many values will be associated with each key newly added to the
199
+ * builder after this call. This does not change semantics, but may improve performance if
200
+ * {@code expectedValuesPerKey} is a good estimate.
201
+ *
202
+ * <p>This may be called more than once; each newly added key will use the most recent call to
203
+ * {@link #expectedValuesPerKey} as its hint.
204
+ *
205
+ * @throws IllegalArgumentException if {@code expectedValuesPerKey} is negative
206
+ * @since NEXT
207
+ */
208
+ @ CanIgnoreReturnValue
209
+ public Builder <K , V > expectedValuesPerKey (int expectedValuesPerKey ) {
210
+ checkNonnegative (expectedValuesPerKey , "expectedValuesPerKey" );
211
+
212
+ // Always presize to at least 1, since we only bother creating a value collection if there's
213
+ // at least one element.
214
+ this .expectedValuesPerKey = Math .max (expectedValuesPerKey , 1 );
215
+
216
+ return this ;
217
+ }
218
+
219
+ /**
220
+ * By default, if we are handed a value collection bigger than expectedValuesPerKey, presize to
221
+ * accept that many elements.
222
+ *
223
+ * <p>This gets overridden in ImmutableSetMultimap.Builder to only trust the size of {@code
224
+ * values} if it is a Set and therefore probably already deduplicated.
225
+ */
226
+ int expectedValueCollectionSize (int defaultExpectedValues , Iterable <?> values ) {
227
+ if (values instanceof Collection <?>) {
228
+ Collection <?> collection = (Collection <?>) values ;
229
+ return Math .max (defaultExpectedValues , collection .size ());
230
+ } else {
231
+ return defaultExpectedValues ;
232
+ }
172
233
}
173
234
174
235
/** Adds a key-value mapping to the built multimap. */
@@ -177,7 +238,7 @@ public Builder<K, V> put(K key, V value) {
177
238
checkEntryNotNull (key , value );
178
239
ImmutableCollection .Builder <V > valuesBuilder = ensureBuilderMapNonNull ().get (key );
179
240
if (valuesBuilder == null ) {
180
- valuesBuilder = newValueCollectionBuilder ( );
241
+ valuesBuilder = newValueCollectionBuilderWithExpectedSize ( expectedValuesPerKey );
181
242
ensureBuilderMapNonNull ().put (key , valuesBuilder );
182
243
}
183
244
valuesBuilder .add (value );
@@ -224,7 +285,9 @@ public Builder<K, V> putAll(K key, Iterable<? extends V> values) {
224
285
}
225
286
ImmutableCollection .Builder <V > valuesBuilder = ensureBuilderMapNonNull ().get (key );
226
287
if (valuesBuilder == null ) {
227
- valuesBuilder = newValueCollectionBuilder ();
288
+ valuesBuilder =
289
+ newValueCollectionBuilderWithExpectedSize (
290
+ expectedValueCollectionSize (expectedValuesPerKey , values ));
228
291
ensureBuilderMapNonNull ().put (key , valuesBuilder );
229
292
}
230
293
while (valuesItr .hasNext ()) {
0 commit comments