com.google.common.collect.ImmutableMapBuilder Maven / Gradle / Ivy
/*
* Copyright (C) 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.common.collect;
import com.google.common.base.Nullable;
import static com.google.common.base.Preconditions.checkState;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* A convenient way to populate immutable Map instances, especially static-final
* "constant Maps". Code such as
*
*
* static final Map<String,Integer> ENGLISH_TO_INTEGER_MAP
* = createNumbersMap();
*
* static Map<String,Integer> createNumbersMap() {
* Map<String,Integer> map = Maps.newHashMap();
* map.put("one", 1);
* map.put("two", 2);
* map.put("three", 3);
* return Collections.unmodifiableMap(map);
* }
*
* ... can be rewritten far more simply as ...
*
* static final Map<String,Integer> ENGLISH_TO_INTEGER_MAP
* = new ImmutableMapBuilder<String,Integer>()
* .put("one", 1)
* .put("two", 2)
* .put("three", 3)
* .getMap();
*
* (Actually, for small immutable Maps, you can use members of the
* even-more-convenient {@link Maps#immutableMap()} family of methods.)
*
* @author Kevin Bourrillion
*/
public class ImmutableMapBuilder {
private ImmutableHashMap map;
/**
* Creates a new ImmutableMapBuilder populated with the contents of {@code
* map}.
*/
public static ImmutableMapBuilder fromMap(Map map) {
ImmutableMapBuilder builder
= new ImmutableMapBuilder(map.size() * 3 / 2);
for (Map.Entry entry : map.entrySet()) {
builder.put(entry.getKey(), entry.getValue());
}
return builder;
}
/** Creates a new ImmutableMapBuilder with an unspecified expected size. */
public ImmutableMapBuilder() {
this(8);
}
/**
* Creates a new ImmutableMapBuilder with the given expected size.
*
* @param expectedSize the approximate number of key-value pairs you expect
* this map to contain
*/
public ImmutableMapBuilder(int expectedSize) {
map = new ImmutableHashMap(expectedSize);
}
/**
* Adds a key-value mapping to the map that will be returned by {@code
* getMap}.
*
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return this map builder (to enable call chaining)
* @throws IllegalStateException if {@code getMap} has already been called
*/
public ImmutableMapBuilder put(@Nullable K key, @Nullable V value) {
checkState(map != null, "map has already been created");
map.secretPut(key, value);
return this;
}
/**
* Returns a newly-created, immutable HashMap instance containing the keys and
* values that were specified using {@code put}.
*
* @return a new, immutable HashMap instance
* @throws IllegalStateException if {@code getMap} has already been called
*/
public Map getMap() {
checkState(map != null, "map has already been created");
try {
return map;
} finally {
map = null;
}
}
private static class ImmutableHashMap extends HashMap {
ImmutableHashMap(int expectedSize) {
// avoid collisions by using 2-4x as many buckets as expected entries
super(expectedSize * 2);
}
transient volatile Set keySet;
@Override public Set keySet() {
if (keySet == null) {
keySet = Collections.unmodifiableSet(super.keySet());
}
return keySet;
}
transient volatile Collection values;
@Override public Collection values() {
if (values == null) {
values = Collections.unmodifiableCollection(super.values());
}
return values;
}
transient volatile Set> entrySet;
@Override public Set> entrySet() {
if (entrySet == null) {
entrySet = Maps.unmodifiableEntrySet(super.entrySet());
}
return entrySet;
}
/*
* This doesn't have to be volatile, for two reasons. Since Integer is
* immutable, it's threadsafe. Multiple threads calculating the hash code
* concurrently won't interfere with each other, and it's fine if one
* overwrites the cachedHashCode generated by the other.
*/
transient Integer cachedHashCode;
/*
* This works because no one can call hashCode() until after all the calls
* to secretPut() are finished.
*/
@Override public int hashCode() {
if (cachedHashCode == null) {
cachedHashCode = super.hashCode();
}
return cachedHashCode;
}
private void secretPut(K key, V value) {
super.put(key, value);
}
@Override public V put(K key, V value) {
throw up();
}
@Override public void putAll(Map extends K, ? extends V> m) {
throw up();
}
@Override public V remove(Object key) {
throw up();
}
@Override public void clear() {
throw up();
}
static UnsupportedOperationException up() {
return new UnsupportedOperationException();
}
private static final long serialVersionUID = -5187626034923451074L;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy