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

io.github.palexdev.mfxcore.collections.WeakHashSet Maven / Gradle / Ivy

There is a newer version: 11.26.0
Show newest version
/*
 * Copyright (C) 2023 Parisi Alessandro - [email protected]
 * This file is part of MaterialFX (https://github.com/palexdev/MaterialFX)
 *
 * MaterialFX is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 3 of the License,
 * or (at your option) any later version.
 *
 * MaterialFX is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with MaterialFX. If not, see .
 */

package io.github.palexdev.mfxcore.collections;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/**
 * A weak {@link HashSet}. An element stored in the {@code WeakHashSet} might be
 * garbage collected, if there is no strong reference to this element.
 */
public class WeakHashSet extends HashSet {
    //================================================================================
    // Properties
    //================================================================================
    private final Set> set = new HashSet<>();
    // Helps detect GCed values
    private final ReferenceQueue queue = new ReferenceQueue<>();

    //================================================================================
    // Constructors
    //================================================================================

    public WeakHashSet() {
    }

    public WeakHashSet(Collection c) {
        super(c);
    }

    public WeakHashSet(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor);
    }

    public WeakHashSet(int initialCapacity) {
        super(initialCapacity);
    }

    //================================================================================
    // Methods
    //================================================================================

    /**
     * Adds the specified element to this set if it is not already
     * present.
     *
     * @param t element to be added to this set.
     * @return `true` if the set did not already contain the specified
     * element.
     */
    @Override
    public boolean add(T t) {
        processQueue();
        return set.add(WeakElement.of(t, queue));
    }

    @Override
    public boolean addAll(Collection c) {
        for (T t : c) {
            if (!add(t)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean retainAll(Collection c) {
        Set itemsToRemove = new HashSet<>(c.size());
        for (T t : this) {
            if (!c.contains(t)) {
                itemsToRemove.add(t);
            }
        }
        if (itemsToRemove.isEmpty()) return false;

        for (T t : itemsToRemove) {
            remove(t);
        }
        return true;
    }

    /**
     * Removes the given element from this set if it is present.
     *
     * @param o object to be removed from this set, if present.
     * @return `true` if the set contained the specified element.
     */
    @Override
    public boolean remove(Object o) {
        boolean ret = set.remove(WeakElement.of(o));
        processQueue();
        return ret;
    }

    @Override
    public boolean removeAll(Collection c) {
        boolean changed = false;
        for (Object o : c) {
            if (remove(o)) changed = true;
        }
        return changed;
    }

    @Override
    public void clear() {
    }

    /**
     * Returns `true` if this set contains the specified element.
     *
     * @param o element whose presence in this set is to be tested.
     * @return `true` if this set contains the specified element.
     */
    @Override
    public boolean contains(Object o) {
        return set.contains(WeakElement.of(o));
    }

    @Override
    public boolean containsAll(Collection c) {
        for (Object o : c) {
            if (!contains(o)) {
                return false;
            }
        }
        return true;
    }

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

    @Override
    public boolean isEmpty() {
        return set.isEmpty();
    }

    /**
     * Returns an iterator over the elements in this set. The elements
     * are returned in no particular order.
     *
     * @return an Iterator over the elements in this set.
     */
    @Override
    public Iterator iterator() {
        // Remove GCed elements
        processQueue();

        Iterator> it = set.iterator();
        return new Iterator<>() {
            @Override
            public boolean hasNext() {
                return it.hasNext();
            }

            @Override
            public T next() {
                return it.next().get();
            }

            @Override
            public void remove() {
                it.remove();
            }
        };
    }

    /**
     * Removes all GCed values from the delegate Set.
     * Since we don't know how much the ReferenceQueue.poll() operation
     * costs, we should call it only in methods that change the Set, like add or remove.
     */
    protected void processQueue() {
        WeakElement e;
        while (true) {
            e = (WeakElement) queue.poll();
            if (e == null) break;
            set.remove(e);
        }
    }

    //================================================================================
    // Internal Classes
    //================================================================================

    /**
     * A {@code WeakHasSet} stores objects of class WeakElement.
     * A {@code WeakElement} wraps the element that should be stored in the {@code WeakHashSet}.
     * {@code WeakElement} inherits from {@link WeakReference}.
     * It redefines equals and hashCode which delegate to the corresponding methods
     * of the wrapped element.
     */
    protected static class WeakElement extends WeakReference {
        private final int hash;

        public WeakElement(T t) {
            super(t);
            this.hash = (t != null) ? t.hashCode() : 0;
        }

        public WeakElement(T t, ReferenceQueue q) {
            super(t, q);
            this.hash = (t != null) ? t.hashCode() : 0;
        }

        public static  WeakElement of(T t) {
            return new WeakElement<>(t);
        }

        public static  WeakElement of(T t, ReferenceQueue q) {
            return new WeakElement<>(t, q);
        }

        /**
         * A {@code WeakElement} is equal to another {@code WeakElement} if they both refer to objects
         * that are, in turn, equal according to their own equals methods
         */
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            T t = get();
            Object oT = ((WeakElement) o).get();
            if (t == oT) return true;
            return (t == null || oT == null) ? false : t.equals(oT);
        }

        @Override
        public int hashCode() {
            return hash;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy