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

org.javimmutable.collections.hash.set.SetBuilder Maven / Gradle / Ivy

///###////////////////////////////////////////////////////////////////////////
//
// Burton Computer Corporation
// http://www.burton-computer.com
//
// Copyright (c) 2019, Burton Computer Corporation
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     Redistributions of source code must retain the above copyright
//     notice, this list of conditions and the following disclaimer.
//
//     Redistributions in binary form must reproduce the above copyright
//     notice, this list of conditions and the following disclaimer in
//     the documentation and/or other materials provided with the
//     distribution.
//
//     Neither the name of the Burton Computer Corporation nor the names
//     of its contributors may be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package org.javimmutable.collections.hash.set;

import org.javimmutable.collections.common.CollisionSet;
import org.javimmutable.collections.list.ListCollisionSet;
import org.javimmutable.collections.tree.TreeCollisionSet;

import javax.annotation.Nonnull;
import javax.annotation.concurrent.NotThreadSafe;

import static org.javimmutable.collections.hash.set.SetBranchNode.*;


@NotThreadSafe
public class SetBuilder
{
    private CollisionSet collisionSet = ListCollisionSet.instance();
    private Node root = new Empty<>();

    @Nonnull
    public SetNode build()
    {
        return root.toSet(collisionSet);
    }

    public void clear()
    {
        collisionSet = ListCollisionSet.instance();
        root = new Empty<>();
    }

    public void add(@Nonnull T value)
    {
        if (root.isEmpty()) {
            collisionSet = selectCollisionSetForValue(value);
            root = new Leaf<>(collisionSet, value.hashCode(), value);
        } else {
            root = root.add(collisionSet, value.hashCode(), value);
        }
    }

    @Nonnull
    public CollisionSet getCollisionSet()
    {
        return collisionSet;
    }

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

    public static  CollisionSet selectCollisionSetForValue(@Nonnull T value)
    {
        if (value instanceof Comparable) {
            return TreeCollisionSet.instance();
        } else {
            return ListCollisionSet.instance();
        }
    }

    private static abstract class Node
    {
        @Nonnull
        abstract Node add(@Nonnull CollisionSet collisionSet,
                             int hashCode,
                             @Nonnull T value);

        @Nonnull
        abstract SetNode toSet(@Nonnull CollisionSet collisionSet);

        abstract int size();

        abstract boolean isEmpty();
    }

    private static class Empty
        extends Node
    {
        @Nonnull
        @Override
        Node add(@Nonnull CollisionSet collisionSet,
                    int hashCode,
                    @Nonnull T value)
        {
            return new Leaf<>(collisionSet, hashCode, value);
        }

        @Nonnull
        @Override
        SetNode toSet(@Nonnull CollisionSet collisionSet)
        {
            return SetEmptyNode.of();
        }

        @Override
        int size()
        {
            return 0;
        }

        @Override
        boolean isEmpty()
        {
            return true;
        }
    }

    private static class Leaf
        extends Node
    {
        private final int hashCode;
        private CollisionSet.Node values;
        private int size;

        private Leaf(@Nonnull Leaf other)
        {
            this.hashCode = other.hashCode >>> SHIFT;
            this.values = other.values;
            size = other.size;
        }

        private Leaf(@Nonnull CollisionSet collisionSet,
                     int hashCode,
                     @Nonnull T value)
        {
            this.hashCode = hashCode;
            this.values = collisionSet.single(value);
            size = 1;
        }

        @Nonnull
        @Override
        Node add(@Nonnull CollisionSet collisionSet,
                    int hashCode,
                    @Nonnull T value)
        {
            if (hashCode == this.hashCode) {
                values = collisionSet.insert(values, value);
                size = collisionSet.size(values);
                return this;
            } else {
                return new Branch<>(collisionSet, this, hashCode, value);
            }
        }

        @Nonnull
        @Override
        SetNode toSet(@Nonnull CollisionSet collisionSet)
        {
            return SetMultiValueLeafNode.createLeaf(collisionSet, hashCode, values);
        }

        @Override
        int size()
        {
            return size;
        }

        @Override
        boolean isEmpty()
        {
            return false;
        }
    }

    private static class Branch
        extends Node
    {
        private final Node[] children;
        private CollisionSet.Node values;
        private int size;

        private Branch(@Nonnull CollisionSet collisionSet,
                       @Nonnull Leaf leaf,
                       int hashCode,
                       @Nonnull T value)
        {
            assert hashCode != leaf.hashCode;
            children = new Node[32];
            if (leaf.hashCode == 0) {
                values = leaf.values;
            } else {
                values = collisionSet.empty();
                children[leaf.hashCode & MASK] = new Leaf<>(leaf);
            }
            size = leaf.size;
            add(collisionSet, hashCode, value);
        }

        @Nonnull
        @Override
        Node add(@Nonnull CollisionSet collisionSet,
                    int hashCode,
                    @Nonnull T value)
        {
            if (hashCode == 0) {
                int beforeSize = collisionSet.size(values);
                values = collisionSet.insert(values, value);
                size = size - beforeSize + collisionSet.size(values);
            } else {
                final int index = hashCode & MASK;
                final Node beforeChild = children[index];
                if (beforeChild == null) {
                    children[index] = new Leaf<>(collisionSet, hashCode >>> SHIFT, value);
                    size += 1;
                } else {
                    // note: afterChild might be same object as beforeChild so capture size now
                    final int beforeSize = beforeChild.size();
                    final Node afterChild = beforeChild.add(collisionSet, hashCode >>> SHIFT, value);
                    children[index] = afterChild;
                    size = size - beforeSize + afterChild.size();
                }
            }
            assert invariant(collisionSet);
            return this;
        }

        @Nonnull
        @Override
        SetNode toSet(@Nonnull CollisionSet collisionSet)
        {
            int count = 0;
            for (Node child : children) {
                if (child != null) {
                    count += 1;
                }
            }
            int bitmask = 0;
            int bit = 1;
            final SetNode[] nodes = new SetNode[count];
            int index = 0;
            for (Node child : children) {
                if (child != null) {
                    final SetNode hamt = child.toSet(collisionSet);
                    nodes[index++] = hamt;
                    bitmask |= bit;
                }
                bit <<= 1;
            }
            return new SetBranchNode<>(bitmask, values, nodes, size);
        }

        @Override
        int size()
        {
            return size;
        }

        @Override
        boolean isEmpty()
        {
            return false;
        }

        private boolean invariant(@Nonnull CollisionSet collisionSet)
        {
            final int valuesSize = collisionSet.size(values);
            int childCount = 0;
            int leafCount = 0;
            for (Node child : children) {
                if (child != null) {
                    childCount += 1;
                    if (child instanceof Leaf) {
                        leafCount += 1;
                    }
                }
            }
            return (childCount > 0) && (valuesSize > 0 || childCount > 1 || leafCount == 0);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy