
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