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

org.organicdesign.fp.collections.PersistentHashSet Maven / Gradle / Ivy

/*
 *   Copyright (c) Rich Hickey. All rights reserved.
 *   The use and distribution terms for this software are covered by the
 *   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
 *   which can be found in the file epl-v10.html at the root of this distribution.
 *   By using this software in any fashion, you are agreeing to be bound by
 * 	 the terms of this license.
 *   You must not remove this notice, or any other, from this software.
 **/

/* rich Mar 3, 2008 */
/* glen added types 2015-06-06 any errors are now mine. */

package org.organicdesign.fp.collections;

import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Map;

/**
 A wrapper that turns a PersistentTreeMap into a set.

 This file is a derivative work based on a Clojure collection licensed under the Eclipse Public
 License 1.0 Copyright Rich Hickey
*/
public class PersistentHashSet extends AbstractUnmodSet
        implements ImSet, Serializable {

    // If you don't put this here, it inherits EMPTY from UnmodSet, which does not have .equals()
    // defined.  UnmodSet.empty won't put() either.
    public static final PersistentHashSet EMPTY =
            new PersistentHashSet<>(PersistentHashMap.EMPTY);

    @SuppressWarnings("unchecked")
    public static  PersistentHashSet empty() { return (PersistentHashSet) EMPTY; }

    /** Works around some type inference limitations of Java 8. */
    public static  MutableHashSet emptyMutable() {
        return PersistentHashSet.empty().mutable();
    }

    public static  PersistentHashSet empty(Equator eq) {
        return new PersistentHashSet<>(PersistentHashMap.empty(eq));
    }

    /** Works around some type inference limitations of Java 8. */
    public static  MutableHashSet emptyMutable(Equator eq) {
        return empty(eq).mutable();
    }

    /**
     Returns a new PersistentHashSet of the values.  The vararg version of this method is
     {@link org.organicdesign.fp.StaticImports#set(Object...)}   If the input contains duplicate
     elements, later values overwrite earlier ones.

     @param elements The items to put into a vector.
     @return a new PersistentHashSet of the given elements.
     */
    public static  PersistentHashSet of(Iterable elements) {
        PersistentHashSet empty = empty();
        MutableSet ret = empty.mutable();
        for (E e : elements) {
            ret.put(e);
        }
        return (PersistentHashSet) ret.immutable();
    }

    public static  PersistentHashSet ofEq(Equator eq, Iterable init) {
        MutableSet ret = emptyMutable(eq);
        for (E e : init) {
            ret.put(e);
        }
        return (PersistentHashSet) ret.immutable();
    }

    @SuppressWarnings("unchecked")
    public static  PersistentHashSet ofMap(ImMap map) {
        return new PersistentHashSet<>((ImMap) map);
    }

    // ==================================== Instance Variables ====================================
    private final ImMap impl;

    // ======================================= Constructor =======================================
    private PersistentHashSet(ImMap i) { impl = i; }

    // ======================================= Serialization =======================================
    // This class has a custom serialized form designed to be as small as possible.  It does not
    // have the same internal structure as an instance of this class.

    // For serializable.  Make sure to change whenever internal data format changes.
    private static final long serialVersionUID = 20160904155600L;

    // Check out Josh Bloch Item 78, p. 312 for an explanation of what's going on here.
    private static class SerializationProxy implements Serializable {
        // For serializable.  Make sure to change whenever internal data format changes.
        private static final long serialVersionUID = 20160904155600L;

        private final int size;
        private transient ImMap theMap;
        SerializationProxy(ImMap phm) {
            size = phm.size();
            theMap = phm;
        }

        // Taken from Josh Bloch Item 75, p. 298
        private void writeObject(ObjectOutputStream s) throws IOException {
            s.defaultWriteObject();
            // Write out all elements in the proper order
            for (Map.Entry entry : theMap) {
                s.writeObject(entry.getKey());
            }
        }

        @SuppressWarnings("unchecked")
        private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
            s.defaultReadObject();
            MutableMap tempMap = PersistentHashMap.empty().mutable();
            for (int i = 0; i < size; i++) {
                K k = (K) s.readObject();
                tempMap = tempMap.assoc(k, k);
            }
            theMap = tempMap.immutable();
        }

        private Object readResolve() { return PersistentHashSet.ofMap(theMap); }
    }

    private Object writeReplace() { return new SerializationProxy<>(impl); }

    private void readObject(java.io.ObjectInputStream in) throws IOException,
            ClassNotFoundException {
        throw new InvalidObjectException("Proxy required");
    }

    // ===================================== Instance Methods =====================================
    @Override public boolean contains(Object key) {
        //noinspection SuspiciousMethodCalls
        return impl.containsKey(key);
    }

    /** Returns the Equator used by this set for equals comparisons and hashCodes */
    public Equator equator() { return impl.equator(); }

    @Override public PersistentHashSet without(E key) {
        if (contains(key))
            return new PersistentHashSet<>(impl.without(key));
        return this;
    }

    @Override public PersistentHashSet put(E o) {
        if (contains(o))
            return this;
        return new PersistentHashSet<>(impl.assoc(o, o));
    }

//    @Override public Sequence seq() { return impl.seq().map(e -> e.getKey()); }

    @Override public UnmodIterator iterator() { return impl.keyIterator(); }

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

    public MutableHashSet mutable() {
        return new MutableHashSet<>(impl.mutable());
    }

    public static final class MutableHashSet extends AbstractUnmodSet
            implements MutableSet {

        MutableMap impl;

        MutableHashSet(MutableMap impl) { this.impl = impl; }

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

        @Override public MutableHashSet put(E val) {
            MutableMap m = impl.assoc(val, val);
            if (m != impl) this.impl = m;
            return this;
        }

        @Override
        public UnmodIterator iterator() { return impl.keyIterator(); }

        @SuppressWarnings("unchecked")
        @Override public boolean contains(Object key) {
            return impl.entry((E) key).isSome();
        }

        @Override public MutableHashSet without(E key) {
            MutableMap m = impl.without(key);
            if (m != impl) this.impl = m;
            return this;
        }

        @Override  public PersistentHashSet immutable() {
            return new PersistentHashSet<>(impl.immutable());
        }
    }

}