/*
 * 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:
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 Map.get. Returns null on ClassCastException and NullPointerException.
/** * 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 Map.containsKey. Returns false on ClassCastException and NullPointerException.
/** * 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 Map.remove. Returns null on ClassCastException and NullPointerException.
/** * 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); } } } }