/*
* Copyright (C) 2007 The Guava Authors
*
* 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 org.glassfish.jersey.internal.guava;
import java.util.AbstractCollection;
import java.util.AbstractMap;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.function.Function;
import java.util.function.Predicate;
import org.glassfish.jersey.internal.guava.Joiner.MapJoiner;
import static org.glassfish.jersey.internal.guava.Preconditions.checkArgument;
import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull;
import static org.glassfish.jersey.internal.guava.Predicates.compose;
Static utility methods pertaining to Map
instances (including instances of SortedMap
, BiMap
, etc.). Also see this class's counterparts Lists
, Sets
and Queues
.
See the Guava User Guide article on Maps
.
Author: Kevin Bourrillion, Mike Bostock, Isaac Shum, Louis Wasserman Since: 2.0 (imported from Google Collections Library)
/**
* Static utility methods pertaining to {@link Map} instances (including instances of
* {@link SortedMap}, {@link BiMap}, etc.). Also see this class's counterparts
* {@link Lists}, {@link Sets} and {@link Queues}.
* <p>
* <p>See the Guava User Guide article on <a href=
* "http://code.google.com/p/guava-libraries/wiki/CollectionUtilitiesExplained#Maps">
* {@code Maps}</a>.
*
* @author Kevin Bourrillion
* @author Mike Bostock
* @author Isaac Shum
* @author Louis Wasserman
* @since 2.0 (imported from Google Collections Library)
*/
public final class Maps {
private static final MapJoiner STANDARD_JOINER =
Collections2.STANDARD_JOINER.withKeyValueSeparator();
private Maps() {
}
@SuppressWarnings("unchecked")
private static <K> Function<Entry<K, ?>, K> keyFunction() {
return (Function) EntryFunction.KEY;
}
@SuppressWarnings("unchecked")
private static <V> Function<Entry<?, V>, V> valueFunction() {
return (Function) EntryFunction.VALUE;
}
private static <K, V> Iterator<K> keyIterator(Iterator<Entry<K, V>> entryIterator) {
return Iterators.transform(entryIterator, Maps.keyFunction());
}
static <K, V> Iterator<V> valueIterator(Iterator<Entry<K, V>> entryIterator) {
return Iterators.transform(entryIterator, Maps.valueFunction());
}
Creates a HashMap
instance, with a high enough "initial capacity" that it should hold expectedSize
elements without growth. This behavior cannot be broadly guaranteed, but it is observed to be true for OpenJDK 1.6. It also can't be guaranteed that the method isn't inadvertently oversizing the returned map.
Params: - expectedSize – the number of elements you expect to add to the
returned map
Throws: - IllegalArgumentException – if
expectedSize
is negative
Returns: a new, empty HashMap
with enough capacity to hold
expectedSize
elements without resizing
/**
* Creates a {@code HashMap} instance, with a high enough "initial capacity"
* that it <i>should</i> hold {@code expectedSize} elements without growth.
* This behavior cannot be broadly guaranteed, but it is observed to be true
* for OpenJDK 1.6. It also can't be guaranteed that the method isn't
* inadvertently <i>oversizing</i> the returned map.
*
* @param expectedSize the number of elements you expect to add to the
* returned map
* @return a new, empty {@code HashMap} with enough capacity to hold {@code
* expectedSize} elements without resizing
* @throws IllegalArgumentException if {@code expectedSize} is negative
*/
public static <K, V> HashMap<K, V> newHashMapWithExpectedSize(
int expectedSize) {
return new HashMap<K, V>(capacity(expectedSize));
}
Returns a capacity that is sufficient to keep the map from being resized as
long as it grows no larger than expectedSize and the load factor is >= its
default (0.75).
/**
* Returns a capacity that is sufficient to keep the map from being resized as
* long as it grows no larger than expectedSize and the load factor is >= its
* default (0.75).
*/
static int capacity(int expectedSize) {
if (expectedSize < 3) {
CollectPreconditions.checkNonnegative(expectedSize, "expectedSize");
return expectedSize + 1;
}
if (expectedSize < Ints.MAX_POWER_OF_TWO) {
return expectedSize + expectedSize / 3;
}
return Integer.MAX_VALUE; // any large value
}
static <K, V> Iterator<Entry<K, V>> asMapEntryIterator(
Set<K> set, final Function<? super K, V> function) {
return new TransformedIterator<K, Entry<K, V>>(set.iterator()) {
@Override
Entry<K, V> transform(final K key) {
return immutableEntry(key, function.apply(key));
}
};
}
private static <E> Set<E> removeOnlySet(final Set<E> set) {
return new ForwardingSet<E>() {
@Override
protected Set<E> delegate() {
return set;
}
@Override
public boolean add(E element) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAll(Collection<? extends E> es) {
throw new UnsupportedOperationException();
}
};
}
private static <E> SortedSet<E> removeOnlySortedSet(final SortedSet<E> set) {
return new ForwardingSortedSet<E>() {
@Override
protected SortedSet<E> delegate() {
return set;
}
@Override
public boolean add(E element) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAll(Collection<? extends E> es) {
throw new UnsupportedOperationException();
}
@Override
public SortedSet<E> headSet(E toElement) {
return removeOnlySortedSet(super.headSet(toElement));
}
@Override
public SortedSet<E> subSet(E fromElement, E toElement) {
return removeOnlySortedSet(super.subSet(fromElement, toElement));
}
@Override
public SortedSet<E> tailSet(E fromElement) {
return removeOnlySortedSet(super.tailSet(fromElement));
}
};
}
Returns an immutable map entry with the specified key and value. The Entry.setValue
operation throws an UnsupportedOperationException
.
The returned entry is serializable.
Params: - key – the key to be associated with the returned entry
- value – the value to be associated with the returned entry
/**
* Returns an immutable map entry with the specified key and value. The {@link
* Entry#setValue} operation throws an {@link UnsupportedOperationException}.
* <p>
* <p>The returned entry is serializable.
*
* @param key the key to be associated with the returned entry
* @param value the value to be associated with the returned entry
*/
public static <K, V> Entry<K, V> immutableEntry(
K key, V value) {
return new ImmutableEntry<K, V>(key, value);
}
static <K> Predicate<Entry<K, ?>> keyPredicateOnEntries(Predicate<? super K> keyPredicate) {
return compose(keyPredicate, Maps.<K>keyFunction());
}
static <V> Predicate<Entry<?, V>> valuePredicateOnEntries(Predicate<? super V> valuePredicate) {
return compose(valuePredicate, Maps.<V>valueFunction());
}
/**
* Delegates to {@link Map#get}. Returns {@code null} on {@code
* ClassCastException} and {@code NullPointerException}.
*/
static <V> V safeGet(Map<?, V> map, Object key) {
checkNotNull(map);
try {
return map.get(key);
} catch (ClassCastException e) {
return null;
} catch (NullPointerException e) {
return null;
}
}
/**
* Delegates to {@link Map#containsKey}. Returns {@code false} on {@code
* ClassCastException} and {@code NullPointerException}.
*/
static boolean safeContainsKey(Map<?, ?> map, Object key) {
checkNotNull(map);
try {
return map.containsKey(key);
} catch (ClassCastException e) {
return false;
} catch (NullPointerException e) {
return false;
}
}
/**
* Delegates to {@link Map#remove}. Returns {@code null} on {@code
* ClassCastException} and {@code NullPointerException}.
*/
static <V> V safeRemove(Map<?, V> map, Object key) {
checkNotNull(map);
try {
return map.remove(key);
} catch (ClassCastException e) {
return null;
} catch (NullPointerException e) {
return null;
}
}
private enum EntryFunction implements Function<Entry<?, ?>, Object> {
KEY {
@Override
public Object apply(Entry<?, ?> entry) {
return entry.getKey();
}
},
VALUE {
@Override
public Object apply(Entry<?, ?> entry) {
return entry.getValue();
}
}
}
private static class AsMapView<K, V> extends ImprovedAbstractMap<K, V> {
final Function<? super K, V> function;
private final Set<K> set;
AsMapView(Set<K> set, Function<? super K, V> function) {
this.set = checkNotNull(set);
this.function = checkNotNull(function);
}
Set<K> backingSet() {
return set;
}
@Override
public Set<K> createKeySet() {
return removeOnlySet(backingSet());
}
@Override
Collection<V> createValues() {
return Collections2.transform(set, function);
}
@Override
public int size() {
return backingSet().size();
}
@Override
public boolean containsKey(Object key) {
return backingSet().contains(key);
}
@Override
public V get(Object key) {
if (Collections2.safeContains(backingSet(), key)) {
@SuppressWarnings("unchecked") // unsafe, but Javadoc warns about it
K k = (K) key;
return function.apply(k);
} else {
return null;
}
}
@Override
public V remove(Object key) {
if (backingSet().remove(key)) {
@SuppressWarnings("unchecked") // unsafe, but Javadoc warns about it
K k = (K) key;
return function.apply(k);
} else {
return null;
}
}
@Override
public void clear() {
backingSet().clear();
}
@Override
protected Set<Entry<K, V>> createEntrySet() {
return new EntrySet<K, V>() {
@Override
Map<K, V> map() {
return Maps.AsMapView.this;
}
@Override
public Iterator<Entry<K, V>> iterator() {
return asMapEntryIterator(backingSet(), function);
}
};
}
}
AbstractMap
extension that implements AbstractMap.isEmpty()
as
entrySet().isEmpty()
instead of size() == 0
to speed up implementations where size()
is O(n), and it delegates the
isEmpty()
methods of its key set and value collection to this implementation. /**
* {@code AbstractMap} extension that implements {@link #isEmpty()} as {@code
* entrySet().isEmpty()} instead of {@code size() == 0} to speed up
* implementations where {@code size()} is O(n), and it delegates the {@code
* isEmpty()} methods of its key set and value collection to this
* implementation.
*/
abstract static class ImprovedAbstractMap<K, V> extends AbstractMap<K, V> {
private transient Set<Entry<K, V>> entrySet;
private transient Set<K> keySet;
private transient Collection<V> values;
Creates the entry set to be returned by entrySet()
. This method is invoked at most once on a given map, at the time when entrySet
is first called. /**
* Creates the entry set to be returned by {@link #entrySet()}. This method
* is invoked at most once on a given map, at the time when {@code entrySet}
* is first called.
*/
abstract Set<Entry<K, V>> createEntrySet();
@Override
public Set<Entry<K, V>> entrySet() {
Set<Entry<K, V>> result = entrySet;
return (result == null) ? entrySet = createEntrySet() : result;
}
@Override
public Set<K> keySet() {
Set<K> result = keySet;
return (result == null) ? keySet = createKeySet() : result;
}
Set<K> createKeySet() {
return new KeySet<K, V>(this);
}
@Override
public Collection<V> values() {
Collection<V> result = values;
return (result == null) ? values = createValues() : result;
}
Collection<V> createValues() {
return new Values<K, V>(this);
}
}
static class KeySet<K, V> extends Sets.ImprovedAbstractSet<K> {
final Map<K, V> map;
KeySet(Map<K, V> map) {
this.map = checkNotNull(map);
}
Map<K, V> map() {
return map;
}
@Override
public Iterator<K> iterator() {
return keyIterator(map().entrySet().iterator());
}
@Override
public int size() {
return map().size();
}
@Override
public boolean isEmpty() {
return map().isEmpty();
}
@Override
public boolean contains(Object o) {
return map().containsKey(o);
}
@Override
public boolean remove(Object o) {
if (contains(o)) {
map().remove(o);
return true;
}
return false;
}
@Override
public void clear() {
map().clear();
}
}
static class Values<K, V> extends AbstractCollection<V> {
final Map<K, V> map;
Values(Map<K, V> map) {
this.map = checkNotNull(map);
}
final Map<K, V> map() {
return map;
}
@Override
public Iterator<V> iterator() {
return valueIterator(map().entrySet().iterator());
}
@Override
public boolean remove(Object o) {
try {
return super.remove(o);
} catch (UnsupportedOperationException e) {
for (Entry<K, V> entry : map().entrySet()) {
if (Objects.equals(o, entry.getValue())) {
map().remove(entry.getKey());
return true;
}
}
return false;
}
}
@Override
public boolean removeAll(Collection<?> c) {
try {
return super.removeAll(checkNotNull(c));
} catch (UnsupportedOperationException e) {
Set<K> toRemove = Sets.newHashSet();
for (Entry<K, V> entry : map().entrySet()) {
if (c.contains(entry.getValue())) {
toRemove.add(entry.getKey());
}
}
return map().keySet().removeAll(toRemove);
}
}
@Override
public boolean retainAll(Collection<?> c) {
try {
return super.retainAll(checkNotNull(c));
} catch (UnsupportedOperationException e) {
Set<K> toRetain = Sets.newHashSet();
for (Entry<K, V> entry : map().entrySet()) {
if (c.contains(entry.getValue())) {
toRetain.add(entry.getKey());
}
}
return map().keySet().retainAll(toRetain);
}
}
@Override
public int size() {
return map().size();
}
@Override
public boolean isEmpty() {
return map().isEmpty();
}
@Override
public boolean contains(Object o) {
return map().containsValue(o);
}
@Override
public void clear() {
map().clear();
}
}
abstract static class EntrySet<K, V>
extends Sets.ImprovedAbstractSet<Entry<K, V>> {
abstract Map<K, V> map();
@Override
public int size() {
return map().size();
}
@Override
public void clear() {
map().clear();
}
@Override
public boolean contains(Object o) {
if (o instanceof Entry) {
Entry<?, ?> entry = (Entry<?, ?>) o;
Object key = entry.getKey();
V value = Maps.safeGet(map(), key);
return Objects.equals(value, entry.getValue())
&& (value != null || map().containsKey(key));
}
return false;
}
@Override
public boolean isEmpty() {
return map().isEmpty();
}
@Override
public boolean remove(Object o) {
if (contains(o)) {
Entry<?, ?> entry = (Entry<?, ?>) o;
return map().keySet().remove(entry.getKey());
}
return false;
}
@Override
public boolean removeAll(Collection<?> c) {
try {
return super.removeAll(checkNotNull(c));
} catch (UnsupportedOperationException e) {
// if the iterators don't support remove
return Sets.removeAllImpl(this, c.iterator());
}
}
@Override
public boolean retainAll(Collection<?> c) {
try {
return super.retainAll(checkNotNull(c));
} catch (UnsupportedOperationException e) {
// if the iterators don't support remove
Set<Object> keys = Sets.newHashSetWithExpectedSize(c.size());
for (Object o : c) {
if (contains(o)) {
Entry<?, ?> entry = (Entry<?, ?>) o;
keys.add(entry.getKey());
}
}
return map().keySet().retainAll(keys);
}
}
}
}