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

com.yahoo.collections.ListMap Maven / Gradle / Ivy

Go to download

Library for use in Java components of Vespa. Shared code which do not fit anywhere else.

There is a newer version: 8.409.18
Show newest version
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.collections;

import java.lang.reflect.InvocationTargetException;
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.BiConsumer;

/**
 * A map holding multiple items at each key (using ArrayList and HashMap).
 *
 * @author  bratseth
 */
public class ListMap {

    private boolean frozen = false;

    private Map> map;

    public ListMap() {
        this(HashMap.class);
    }

    /** Copy constructor. This will not be frozen even if the argument map is */
    public ListMap(ListMap original) {
        map = new HashMap<>();
        original.map.forEach((k, v) -> this.map.put(k, new ArrayList<>(v)));
    }

    @SuppressWarnings("unchecked")
    public ListMap(@SuppressWarnings("rawtypes") Class implementation) {
        try {
            this.map = implementation.getDeclaredConstructor().newInstance();
        } catch (InvocationTargetException e) {
            // For backwards compatibility from when this method used implementation.newInstance()
            throw new IllegalArgumentException(e.getCause());
        } catch (Exception e) {
            throw new IllegalArgumentException(e);
        }
    }

    /** Puts an element into this. Multiple elements at the same position are added to the list at this key */
    public void put(K key, V value) {
        List list = map.computeIfAbsent(key, k -> new ArrayList<>());
        list.add(value);
    }

    /** Put a key without adding a new value, such that there is an empty list of values if no values are already added */
    public void put(K key) {
        map.computeIfAbsent(key, k -> new ArrayList<>());
    }

    /** Put this map in the state where it has just the given value of the given key */
    public void replace(K key, V value) {
        List list = map.get(key);
        if (list == null) {
            put(key);
        }
        else {
            list.clear();
            list.add(value);
        }
    }

    public void removeAll(K key) {
        map.remove(key);
    }

    public boolean removeValue(K key, V value) {
        List list = map.get(key);
        if (list != null)
            return list.remove(value);
        else
            return false;
    }

    /**
     * Removes the value at the given index.
     *
     * @return the removed value
     * @throws IndexOutOfBoundsException if there is no value at the given index for this key
     */
    public V removeValue(K key, int index) {
        List list = map.get(key);
        if (list != null)
            return list.remove(index);
        else
            throw new IndexOutOfBoundsException("The list at '" + key + "' is empty");
    }

    /**
     * Returns the List containing the elements with this key, or an empty list
     * if there are no elements for this key.
     * The returned list can be modified to add and remove values if the value exists.
     */
    public List get(K key) {
        List list = map.get(key);
        if (list == null) return List.of();;
        return list;
    }

    /** The same as get */
    public List getList(K key) {
        return get(key);
    }

    /** Returns the entries of this. Entries will be unmodifiable if this is frozen. */
    public Set>> entrySet() { return map.entrySet(); }

    /** Returns the keys of this */
    public Set keySet() { return map.keySet(); }

    /** Returns the list values of this */
    public Collection> values() { return map.values(); }

    /**
     * Irreversibly prevent changes to the content of this.
     * If this is already frozen, this method does nothing.
     */
    public void freeze() {
        if (frozen) return;
        frozen = true;

        for (Map.Entry> entry : map.entrySet())
            entry.setValue(List.copyOf(entry.getValue()));
        this.map = Map.copyOf(this.map);
    }

    /** Returns whether this allows changes */
    public boolean isFrozen() { return frozen; }

    @Override
    public String toString() {
        return "ListMap{" +
                "frozen=" + frozen +
                ", map=" + map +
                '}';
    }

    /** Returns the number of keys in this map */
    public int size() { return map.size(); }

    public void forEach(BiConsumer> action) { map.forEach(action); }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy