All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.conqat.lib.commons.collections.TwoDimHashMap Maven / Gradle / Ivy

There is a newer version: 2024.7.2
Show newest version
/*
 * Copyright (c) CQSE GmbH
 *
 * 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.conqat.lib.commons.collections;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;

import org.conqat.lib.commons.test.IndexValueClass;

/**
 * A 2-dimensional hash map. Allows storage of items identified by two different keys. This can be
 * used to store the following data structure:
 * 
 * 
    *
  • Project A *
      *
    • Dan — Testing
    • *
    • Flo — Documentation
    • *
    *
  • *
  • Project B *
      *
    • Flo — Design
    • *
    • Dan — QA
    • *
    • Markus — CM
    • *
    • Jorge — Testing
    • *
    *
  • *
*/ @IndexValueClass public class TwoDimHashMap implements Serializable { private static final long serialVersionUID = 1L; /** The first level map. */ private final Map> data; /** Create a new doubly hashed map. */ public TwoDimHashMap() { data = new HashMap<>(); } /** Create a new two dimensional map using the data in the given map. */ public TwoDimHashMap(Map> map) { data = map; } /** Put all values of another {@link TwoDimHashMap} into this map. */ public void putAll(TwoDimHashMap otherMap) { for (K1 key1 : otherMap.getFirstKeys()) { for (K2 key2 : otherMap.getSecondKeys(key1)) { V value = otherMap.getValue(key1, key2); putValue(key1, key2, value); } } } /** * Puts the given value into this map under the given keys. Any potentially existing value will be * overwritten. * * @param key1 * first level key * @param key2 * second level key * @param value * the value */ public void putValue(K1 key1, K2 key2, V value) { Map map = data.computeIfAbsent(key1, ignored -> new HashMap<>()); map.put(key2, value); } /** * Get a value by specifying first and second level key. * * @param firstKey * first level key * @param secondKey * second level key * @return the value. Is null if first or second level key does not exist or if * null was explicitly stored. */ public V getValue(K1 firstKey, K2 secondKey) { Map map = data.get(firstKey); if (map == null) { return null; } return map.get(secondKey); } /** * The same as {@link #getValue(Object, Object)} but returns the default value instead of null when * the map doesn't contain an entry for the keys. */ public V getValueOrDefault(K1 firstKey, K2 secondKey, V defaultValue) { V value = getValue(firstKey, secondKey); if (value == null) { return defaultValue; } return value; } /** * If the map does not {@link #containsKey(Object, Object)} firstKey secondKey, the result of the * function get put into the map. Returns the value for the both keys. */ public V computeIfAbsent(K1 firstKey, K2 secondKey, BiFunction mappingFunction) { if (containsKey(firstKey, secondKey)) { return getValue(firstKey, secondKey); } V result = mappingFunction.apply(firstKey, secondKey); putValue(firstKey, secondKey, result); return result; } /** * Returns whether the given key combination is available in the map. * * @param firstKey * first level key * @param secondKey * second level key */ public boolean containsKey(K1 firstKey, K2 secondKey) { Map map = data.get(firstKey); if (map == null) { return false; } return map.containsKey(secondKey); } /** * Get all values referenced by a first level key. * * @param firstKey * the first level key * @return a list of values referenced by the specified first level key */ public Collection getValuesByFirstKey(K1 firstKey) { Map map = data.get(firstKey); if (map == null) { return null; } return map.values(); } /** * Get all first level keys. */ public Set getFirstKeys() { return data.keySet(); } /** * Get all the second level keys stored under the given first level key. * * @param firstKey * the first level key. * @return all second level keys for a first level key. */ public Set getSecondKeys(K1 firstKey) { Map map = data.get(firstKey); if (map == null) { return CollectionUtils.emptySet(); } return map.keySet(); } /** * Get all values referenced by a second level key. *

* Note: This method's complexity is linear in the number of first level keys. * * @param secondKey * the second level key * @return a new list of values referenced by the specified second level key */ public List getValuesBySecondKey(K2 secondKey) { ArrayList result = new ArrayList<>(); for (Map map : data.values()) { if (map.containsKey(secondKey)) { result.add(map.get(secondKey)); } } return result; } /** * Get all values stored in the map. * * @return a new list of all values. */ public List getValues() { ArrayList result = new ArrayList<>(); for (Map map : data.values()) { result.addAll(map.values()); } return result; } /** * Get size of the map. * * @return the number of values stored in this map. */ public int getSize() { int size = 0; for (Map map : data.values()) { size += map.size(); } return size; } /** * Check if the map is empty, i.e. no values are stored in it. */ public boolean isEmpty() { return getSize() == 0; } /** * Get the size of the (second) map stored for a first key. * * @return the size or 0 if the first level key wasn't found. */ public int getSecondSize(K1 key1) { Map map = data.get(key1); if (map == null) { return 0; } return map.size(); } /** * Clear the whole map. */ public void clear() { data.clear(); } /** * Removes the value associated with the given key combination. * * @return previous value associated with specified keys, or null if there was no * mapping for those keys. A null return can also indicate that the map * previously associated null with the specified keys. */ public V remove(K1 key1, K2 key2) { Map map = data.get(key1); if (map == null) { return null; } if (!map.containsKey(key2)) { return null; } V result = map.remove(key2); if (map.isEmpty()) { data.remove(key1); } return result; } /** * Remove all values stored under the given first level key. * * @param key * first level key * @return true if the given key was present in the map, false otherwise. */ public boolean remove(K1 key) { Map result = data.remove(key); return result != null; } /** * Returns the data stored under the given first-level key as an unmodifiable map or * null if nothing is stored under that key. */ public UnmodifiableMap getSecondMap(K1 key) { Map secondMap = data.get(key); if (secondMap == null) { return null; } return CollectionUtils.asUnmodifiable(secondMap); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy