/** * * Copyright © 2015 Florian Schmaus * * 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.jivesoftware.smack.util; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * A lightweight implementation of a MultiMap, that is a Map that is able to hold multiple values for every key. *

* This MultiMap uses a {@link LinkedHashMap} together with a {@link ArrayList} in order to keep the order of its entries. *

* * @param the type of the keys the map uses. * @param the type of the values the map uses. */ public class MultiMap { /** * The constant value {@value}. */ public static final int DEFAULT_MAP_SIZE = 6; private static final int ENTRY_LIST_SIZE = 3; private final Map> map; /** * Constructs a new MultiMap with a initial capacity of {@link #DEFAULT_MAP_SIZE}. */ public MultiMap() { this(DEFAULT_MAP_SIZE); } /** * Constructs a new MultiMap. * * @param size the initial capacity. */ public MultiMap(int size) { map = new LinkedHashMap<>(size); } public int size() { int size = 0; for (List list : map.values()) { size += list.size(); } return size; } public boolean isEmpty() { return map.isEmpty(); } public boolean containsKey(Object key) { return map.containsKey(key); } public boolean containsValue(Object value) { for (List list : map.values()) { if (list.contains(value)) { return true; } } return false; } /** * Get the first value for the given key, or null if there are no entries. * * @param key * @return the first value or null. */ public V getFirst(Object key) { List res = getAll(key); if (res.isEmpty()) { return null; } else { return res.iterator().next(); } } /** * Get all values for the given key. Returns the empty set if there are none. *

* Changes to the returned set will update the underlying MultiMap if the return set is not empty. *

* * @param key * @return all values for the given key. */ public List getAll(Object key) { List res = map.get(key); if (res == null) { res = Collections.emptyList(); } return res; } public boolean put(K key, V value) { boolean keyExisted; List list = map.get(key); if (list == null) { list = new ArrayList<>(ENTRY_LIST_SIZE); list.add(value); map.put(key, list); keyExisted = false; } else { list.add(value); keyExisted = true; } return keyExisted; } /** * Removes all mappings for the given key and returns the first value if there where mappings or null if not. * * @param key * @return the first value of the given key or null. */ public V remove(Object key) { List res = map.remove(key); if (res == null) { return null; } assert (!res.isEmpty()); return res.iterator().next(); } /** * Remove the mapping of the given key to the value. *

* Returns true if the mapping was removed and false if the mapping did not exist. *

* * @param key * @param value * @return true if the mapping was removed, false otherwise. */ public boolean removeOne(Object key, V value) { List list = map.get(key); if (list == null) { return false; } boolean res = list.remove(value); if (list.isEmpty()) { // Remove the key also if the value set is now empty map.remove(key); } return res; } public void putAll(Map map) { for (Map.Entry entry : map.entrySet()) { put(entry.getKey(), entry.getValue()); } } public void clear() { map.clear(); } public Set keySet() { return map.keySet(); } /** * Returns a new list containing all values of this multi map. * * @return a new list with all values. */ public List values() { List values = new ArrayList<>(size()); for (List list : map.values()) { values.addAll(list); } return values; } public Set> entrySet() { Set> entrySet = new LinkedHashSet<>(size()); for (Map.Entry> entries : map.entrySet()) { K key = entries.getKey(); for (V value : entries.getValue()) { entrySet.add(new SimpleMapEntry<>(key, value)); } } return entrySet; } private static final class SimpleMapEntry implements Map.Entry { private final K key; private V value; private SimpleMapEntry(K key, V value) { this.key = key; this.value = value; } @Override public K getKey() { return key; } @Override public V getValue() { return value; } @Override public V setValue(V value) { V tmp = this.value; this.value = value; return tmp; } } }