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

org.jhotdraw8.icollection.MutableRedBlackMap Maven / Gradle / Ivy

The newest version!
package org.jhotdraw8.icollection;

import org.jhotdraw8.icollection.facade.CollectionFacade;
import org.jhotdraw8.icollection.facade.NavigableSetFacade;
import org.jhotdraw8.icollection.facade.ReadOnlySequencedMapFacade;
import org.jhotdraw8.icollection.facade.SetFacade;
import org.jhotdraw8.icollection.impl.iteration.FailFastIterator;
import org.jhotdraw8.icollection.impl.iteration.FailFastSpliterator;
import org.jhotdraw8.icollection.impl.iteration.MappedIterator;
import org.jhotdraw8.icollection.impl.iteration.MappedSpliterator;
import org.jhotdraw8.icollection.impl.redblack.RedBlackTree;
import org.jhotdraw8.icollection.navigable.DescendingNavigableMapView;
import org.jhotdraw8.icollection.navigable.SubsetNavigableMapView;
import org.jhotdraw8.icollection.readonly.ReadOnlyNavigableMap;
import org.jhotdraw8.icollection.readonly.ReadOnlySequencedMap;
import org.jhotdraw8.icollection.serialization.SortedMapSerializationProxy;
import org.jspecify.annotations.Nullable;

import java.io.ObjectStreamException;
import java.io.Serial;
import java.io.Serializable;
import java.util.AbstractMap;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.NavigableMap;
import java.util.NavigableSet;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.SortedMap;
import java.util.Spliterator;
import java.util.Spliterators;

public class MutableRedBlackMap extends AbstractMap implements NavigableMap, ReadOnlyNavigableMap, Cloneable, Serializable {
    @Serial
    private final static long serialVersionUID = 0L;
    transient RedBlackTree root;
    @SuppressWarnings({"serial", "RedundantSuppression"})//Conditionally serializable
    final Comparator comparator;
    transient private int modCount;

    @SuppressWarnings("unchecked")
    @Override
    public MutableRedBlackMap clone() {
        try {
            return (MutableRedBlackMap) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }

    int getModCount() {
        return modCount;
    }

    /**
     * Constructs a new empty map, using the natural ordering of its
     * keys.
     */
    public MutableRedBlackMap() {
        root = RedBlackTree.empty();
        comparator = NaturalComparator.instance();
    }

    /**
     * Constructs a new empty map, that uses the specified comparator
     * for ordering its keys.
     *
     * @param comparator the comparator that will be used to order this map.
     *                   If null, the natural ordering of the keys is used.
     */
    public MutableRedBlackMap(@Nullable Comparator comparator) {
        root = RedBlackTree.empty();
        this.comparator = comparator == null ? NaturalComparator.instance() : comparator;
    }

    /**
     * Constructs a map containing the same entries as in the specified
     * {@link Map}, using the natural ordering of its
     * keys.
     *
     * @param m a map
     */
    @SuppressWarnings("this-escape")
    public MutableRedBlackMap(Map m) {
        this.comparator = NaturalComparator.instance();
        if (m instanceof MutableRedBlackMap r && r.comparator() == null) {
            @SuppressWarnings("unchecked")
            MutableRedBlackMap that = (MutableRedBlackMap) m;
            this.root = that.root;
        } else {
            this.root = RedBlackTree.empty();
            this.putAll(m);
        }
    }

    /**
     * Constructs a map containing the same entries as in the specified
     * {@link Map}, using the same ordering as used by the provided map.
     *
     * @param m a map
     */
    @SuppressWarnings({"unchecked", "this-escape"})
    public MutableRedBlackMap(SortedMap m) {
        this.comparator = m.comparator() == null ? NaturalComparator.instance() : (Comparator) m.comparator();
        if (m instanceof MutableRedBlackMap r && r.comparator() == null) {
            MutableRedBlackMap that = (MutableRedBlackMap) m;
            this.root = that.root;
        } else {
            this.root = RedBlackTree.empty();
            this.putAll(m);
        }
    }

    /**
     * Constructs a map containing the same entries as in the specified
     * {@link Iterable}, using the natural ordering of its
     * keys.
     *
     * @param m an iterable
     */
    @SuppressWarnings({"unchecked", "this-escape"})
    public MutableRedBlackMap(Iterable> m) {
        this.comparator = NaturalComparator.instance();
        if (m instanceof RedBlackMap) {
            RedBlackMap that = (RedBlackMap) m;
            this.root = that.root;
        } else {
            this.root = RedBlackTree.empty();
            for (Entry e : m) {
                this.put(e.getKey(), e.getValue());
            }
        }
    }

    MutableRedBlackMap(RedBlackTree root, Comparator comparator) {
        this.root = root;
        this.comparator = comparator;
    }

    @Override
    public Entry lowerEntry(K key) {
        return root.lower(key, comparator).entryOrNull();
    }

    @Override
    public K lowerKey(K key) {
        return root.lower(key, comparator).keyOrNull();
    }

    @Override
    public Entry floorEntry(K key) {
        return root.floor(key, comparator).entryOrNull();
    }

    @Override
    public K floorKey(K key) {
        return root.floor(key, comparator).keyOrNull();
    }

    @Override
    public Entry ceilingEntry(K key) {
        return root.ceiling(key, comparator).entryOrNull();
    }

    @Override
    public K ceilingKey(K key) {
        return root.ceiling(key, comparator).keyOrNull();
    }

    @Override
    public Entry higherEntry(K key) {
        return root.higher(key, comparator).entryOrNull();
    }

    @Override
    public K higherKey(K key) {
        return root.higher(key, comparator).keyOrNull();
    }

    @Override
    public ReadOnlySequencedMap readOnlyReversed() {
        return new ReadOnlySequencedMapFacade<>(
                this::iterator,
                this::reverseIterator,
                this::size,
                this::containsKey,
                this::get,
                this::lastEntry,
                this::firstEntry,
                Spliterator.SIZED | Spliterator.DISTINCT | Spliterator.ORDERED, null);
    }

    @Override
    public Map.@Nullable Entry firstEntry() {
        return root.min().entryOrNull();
    }

    @Override
    public Map.@Nullable Entry lastEntry() {
        return root.max().entryOrNull();
    }

    @Override
    public @Nullable Entry pollFirstEntry() {
        var min = root.min();
        if (!min.isEmpty()) {
            root = root.delete(min.getKey(), comparator);
        }
        return min.entryOrNull();
    }

    @Override
    public @Nullable Entry pollLastEntry() {
        var max = root.max();
        if (max.isEmpty()) {
            root = root.delete(max.getKey(), comparator);
        }
        return max.entryOrNull();
    }

    @Override
    public NavigableMap descendingMap() {
        return new DescendingNavigableMapView<>(this, this::getModCount);
    }

    @Override
    public NavigableSet navigableKeySet() {
        return NavigableSetFacade.createKeySet(this);
    }

    @Override
    public NavigableSet descendingKeySet() {
        return navigableKeySet().reversed();
    }

    @Override
    public NavigableMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) {
        return new SubsetNavigableMapView<>(this, this::getModCount,
                false, fromKey, fromInclusive, false, toKey, toInclusive, true);
    }

    @Override
    public NavigableMap headMap(K toKey, boolean inclusive) {
        return new SubsetNavigableMapView<>(this, this::getModCount,
                true, null, true, false, toKey, inclusive, true);
    }

    @Override
    public NavigableMap tailMap(K fromKey, boolean inclusive) {
        return new SubsetNavigableMapView<>(this, this::getModCount,
                false, fromKey, inclusive, true, null, true, true);
    }

    @Override
    public @Nullable Comparator comparator() {
        return comparator == NaturalComparator.instance() ? null : comparator;
    }

    @Override
    public SortedMap subMap(K fromKey, K toKey) {
        return subMap(fromKey, true, toKey, false);
    }

    @Override
    public SortedMap headMap(K toKey) {
        return headMap(toKey, true);
    }

    @Override
    public SortedMap tailMap(K fromKey) {
        return tailMap(fromKey, true);
    }

    @Override
    public K firstKey() {
        Map.Entry entry = firstEntry();
        if (entry == null) {
            throw new NoSuchElementException();
        }
        return entry.getKey();
    }

    @Override
    public K lastKey() {
        Map.Entry entry = lastEntry();
        if (entry == null) {
            throw new NoSuchElementException();
        }
        return entry.getKey();
    }

    @Override
    public int size() {
        return root.size();
    }


    @SuppressWarnings("unchecked")
    @Override
    public boolean containsKey(Object key) {
        return root.contains((K) key, comparator);
    }

    @Override
    public boolean containsValue(Object value) {
        for (var node : root) {
            if (Objects.equals(value, node.getValue())) {
                return true;
            }
        }
        return false;
    }

    @SuppressWarnings("unchecked")
    @Override
    public @Nullable V get(Object key) {
        return root.find((K) key, comparator).valueOrNull();
    }


    @Override
    public @Nullable V put(K key, V value) {
        var newRoot = root.insert(key, value, comparator);
        if (newRoot != root) {
            if (newRoot.size() != root.size()) {
                modCount++;
            }
            V oldValue = newRoot.size() == root.size() ? root.find(key, comparator).getValue() : null;
            root = newRoot;
            return oldValue;
        }
        return null;
    }

    @Override
    @SuppressWarnings("unchecked")
    public @Nullable V remove(Object key) {
        var newRoot = root.delete((K) key, comparator);
        if (newRoot != root) {
            modCount++;
            V oldValue = root.find((K) key, comparator).getValue();
            root = newRoot;
            return oldValue;
        }
        return null;
    }


    @Override
    public void clear() {
        if (!isEmpty()) {
            root = RedBlackTree.empty();
        }
    }

    private void iteratorPutIfPresent(@Nullable K k, @Nullable V v) {
        if (containsKey(k)) {
            put(k, v);
        }
    }

    private void iteratorRemove(Map.Entry entry) {
        remove(entry.getKey());
    }


    public RedBlackMap toImmutable() {
        return new RedBlackMap<>(root, comparator);
    }

    public Iterator> iterator() {
        return new FailFastIterator<>(
                new MappedIterator<>(root.iterator(),
                        e -> new MutableMapEntry<>(this::iteratorPutIfPresent, e.getKey(), e.getValue())),
                this::iteratorRemove, this::getModCount
        );
    }

    Iterator> reverseIterator() {
        return new FailFastIterator<>(
                new MappedIterator<>(root.reverseIterator(),
                        e -> new MutableMapEntry<>(this::iteratorPutIfPresent, e.getKey(), e.getValue())),
                this::iteratorRemove, this::getModCount
        );
    }

    public Spliterator> spliterator() {
        //noinspection MagicConstant
        Spliterator> spliterator = Spliterators.spliterator(root.iterator(), size(),
                Spliterator.NONNULL | characteristics());
        return new FailFastSpliterator<>(
                spliterator,
                this::getModCount, comparator == NaturalComparator.instance() ? null : Entry.comparingByKey(comparator));
    }


    @Override
    public Set> entrySet() {
        return new SetFacade<>(
                this::iterator,
                this::spliterator,
                this::size,
                this::containsEntry,
                this::clear,
                null,
                this::removeEntry
        );
    }


    @Override
    public Set keySet() {
        return new SetFacade<>(
                () -> new MappedIterator<>(iterator(), Entry::getKey),
                () -> new MappedSpliterator<>(spliterator(), Entry::getKey, characteristics(), comparator()),
                this::size,
                this::containsKey,
                this::clear,
                null,
                this::removeKey
        );
    }

    @Override
    public Collection values() {
        return new CollectionFacade<>(
                () -> new MappedIterator<>(iterator(), Entry::getValue),
                () -> new MappedSpliterator<>(spliterator(),
                        Entry::getValue, characteristics() & ~(Spliterator.DISTINCT | Spliterator.NONNULL), null),
                this::size,
                this::containsKey,
                this::clear,
                null,
                this::removeKey
        );
    }

    /**
     * Removes the specified entry from the map.
     *
     * @param o an entry (should be a {@link Map.Entry}).
     * @return true if the element was contained in the map
     */
    @SuppressWarnings("unchecked")
    private boolean removeEntry(@Nullable Object o) {
        if (containsEntry(o)) {
            assert o != null;
            remove(((Entry) o).getKey());
            return true;
        }
        return false;
    }

    private boolean removeKey(@Nullable Object o) {
        if (containsKey(o)) {
            remove(o);
            return true;
        }
        return false;
    }

    @Override
    public V getOrDefault(Object key, V defaultValue) {
        return super.getOrDefault(key, defaultValue);
    }

    public void putAll(Iterable> m) {
        for (Map.Entry e : m) {
            put(e.getKey(), e.getValue());
        }
    }

    @Serial
    private Object writeReplace() throws ObjectStreamException {
        return new MutableRedBlackMap.SerializationProxy<>(this);
    }

    private static class SerializationProxy extends SortedMapSerializationProxy {
        @Serial
        private static final long serialVersionUID = 0L;

        protected SerializationProxy(SortedMap target) {
            super(target);
        }

        @Serial
        @Override
        protected Object readResolve() {
            MutableRedBlackMap m = new MutableRedBlackMap<>(deserializedComparator);
            m.putAll(deserializedEntries);
            return m;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy