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

com.gs.collections.impl.set.mutable.UnifiedSet Maven / Gradle / Ivy

Go to download

GS Collections is a collections framework for Java. It has JDK-compatible List, Set and Map implementations with a rich API and set of utility classes that work with any JDK compatible Collections, Arrays, Maps or Strings. The iteration protocol was inspired by the Smalltalk collection framework.

There is a newer version: 7.0.3
Show newest version
/*
 * Copyright 2014 Goldman Sachs.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.gs.collections.impl.set.mutable;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.ExecutorService;

import com.gs.collections.api.LazyIterable;
import com.gs.collections.api.RichIterable;
import com.gs.collections.api.annotation.Beta;
import com.gs.collections.api.bag.MutableBag;
import com.gs.collections.api.block.function.Function;
import com.gs.collections.api.block.function.Function0;
import com.gs.collections.api.block.function.Function2;
import com.gs.collections.api.block.function.Function3;
import com.gs.collections.api.block.function.primitive.BooleanFunction;
import com.gs.collections.api.block.function.primitive.ByteFunction;
import com.gs.collections.api.block.function.primitive.CharFunction;
import com.gs.collections.api.block.function.primitive.DoubleFunction;
import com.gs.collections.api.block.function.primitive.DoubleObjectToDoubleFunction;
import com.gs.collections.api.block.function.primitive.FloatFunction;
import com.gs.collections.api.block.function.primitive.FloatObjectToFloatFunction;
import com.gs.collections.api.block.function.primitive.IntFunction;
import com.gs.collections.api.block.function.primitive.IntObjectToIntFunction;
import com.gs.collections.api.block.function.primitive.LongFunction;
import com.gs.collections.api.block.function.primitive.LongObjectToLongFunction;
import com.gs.collections.api.block.function.primitive.ShortFunction;
import com.gs.collections.api.block.predicate.Predicate;
import com.gs.collections.api.block.predicate.Predicate2;
import com.gs.collections.api.block.procedure.Procedure;
import com.gs.collections.api.block.procedure.Procedure2;
import com.gs.collections.api.block.procedure.primitive.ObjectIntProcedure;
import com.gs.collections.api.collection.primitive.MutableBooleanCollection;
import com.gs.collections.api.collection.primitive.MutableByteCollection;
import com.gs.collections.api.collection.primitive.MutableCharCollection;
import com.gs.collections.api.collection.primitive.MutableDoubleCollection;
import com.gs.collections.api.collection.primitive.MutableFloatCollection;
import com.gs.collections.api.collection.primitive.MutableIntCollection;
import com.gs.collections.api.collection.primitive.MutableLongCollection;
import com.gs.collections.api.collection.primitive.MutableShortCollection;
import com.gs.collections.api.list.MutableList;
import com.gs.collections.api.map.MutableMap;
import com.gs.collections.api.map.sorted.MutableSortedMap;
import com.gs.collections.api.multimap.MutableMultimap;
import com.gs.collections.api.partition.set.PartitionMutableSet;
import com.gs.collections.api.set.ImmutableSet;
import com.gs.collections.api.set.MutableSet;
import com.gs.collections.api.set.ParallelUnsortedSetIterable;
import com.gs.collections.api.set.Pool;
import com.gs.collections.api.set.SetIterable;
import com.gs.collections.api.set.UnsortedSetIterable;
import com.gs.collections.api.set.primitive.MutableBooleanSet;
import com.gs.collections.api.set.primitive.MutableByteSet;
import com.gs.collections.api.set.primitive.MutableCharSet;
import com.gs.collections.api.set.primitive.MutableDoubleSet;
import com.gs.collections.api.set.primitive.MutableFloatSet;
import com.gs.collections.api.set.primitive.MutableIntSet;
import com.gs.collections.api.set.primitive.MutableLongSet;
import com.gs.collections.api.set.primitive.MutableShortSet;
import com.gs.collections.api.set.sorted.MutableSortedSet;
import com.gs.collections.api.tuple.Pair;
import com.gs.collections.api.tuple.Twin;
import com.gs.collections.impl.Counter;
import com.gs.collections.impl.bag.mutable.HashBag;
import com.gs.collections.impl.block.factory.Comparators;
import com.gs.collections.impl.block.factory.Predicates2;
import com.gs.collections.impl.block.factory.Procedures2;
import com.gs.collections.impl.block.procedure.CollectIfProcedure;
import com.gs.collections.impl.block.procedure.CollectProcedure;
import com.gs.collections.impl.block.procedure.CountProcedure;
import com.gs.collections.impl.block.procedure.FlatCollectProcedure;
import com.gs.collections.impl.block.procedure.MultimapEachPutProcedure;
import com.gs.collections.impl.block.procedure.MultimapPutProcedure;
import com.gs.collections.impl.block.procedure.MutatingAggregationProcedure;
import com.gs.collections.impl.block.procedure.NonMutatingAggregationProcedure;
import com.gs.collections.impl.block.procedure.PartitionPredicate2Procedure;
import com.gs.collections.impl.block.procedure.PartitionProcedure;
import com.gs.collections.impl.block.procedure.RejectProcedure;
import com.gs.collections.impl.block.procedure.SelectInstancesOfProcedure;
import com.gs.collections.impl.block.procedure.SelectProcedure;
import com.gs.collections.impl.block.procedure.ZipWithIndexProcedure;
import com.gs.collections.impl.block.procedure.primitive.CollectBooleanProcedure;
import com.gs.collections.impl.block.procedure.primitive.CollectByteProcedure;
import com.gs.collections.impl.block.procedure.primitive.CollectCharProcedure;
import com.gs.collections.impl.block.procedure.primitive.CollectDoubleProcedure;
import com.gs.collections.impl.block.procedure.primitive.CollectFloatProcedure;
import com.gs.collections.impl.block.procedure.primitive.CollectIntProcedure;
import com.gs.collections.impl.block.procedure.primitive.CollectLongProcedure;
import com.gs.collections.impl.block.procedure.primitive.CollectShortProcedure;
import com.gs.collections.impl.factory.Lists;
import com.gs.collections.impl.factory.Sets;
import com.gs.collections.impl.lazy.AbstractLazyIterable;
import com.gs.collections.impl.lazy.parallel.AbstractBatch;
import com.gs.collections.impl.lazy.parallel.set.AbstractParallelUnsortedSetIterable;
import com.gs.collections.impl.lazy.parallel.set.CollectUnsortedSetBatch;
import com.gs.collections.impl.lazy.parallel.set.RootUnsortedSetBatch;
import com.gs.collections.impl.lazy.parallel.set.SelectUnsortedSetBatch;
import com.gs.collections.impl.lazy.parallel.set.UnsortedSetBatch;
import com.gs.collections.impl.list.mutable.FastList;
import com.gs.collections.impl.map.mutable.UnifiedMap;
import com.gs.collections.impl.map.sorted.mutable.TreeSortedMap;
import com.gs.collections.impl.multimap.set.UnifiedSetMultimap;
import com.gs.collections.impl.parallel.BatchIterable;
import com.gs.collections.impl.partition.set.PartitionUnifiedSet;
import com.gs.collections.impl.set.mutable.primitive.BooleanHashSet;
import com.gs.collections.impl.set.mutable.primitive.ByteHashSet;
import com.gs.collections.impl.set.mutable.primitive.CharHashSet;
import com.gs.collections.impl.set.mutable.primitive.DoubleHashSet;
import com.gs.collections.impl.set.mutable.primitive.FloatHashSet;
import com.gs.collections.impl.set.mutable.primitive.IntHashSet;
import com.gs.collections.impl.set.mutable.primitive.LongHashSet;
import com.gs.collections.impl.set.mutable.primitive.ShortHashSet;
import com.gs.collections.impl.set.sorted.mutable.TreeSortedSet;
import com.gs.collections.impl.tuple.Tuples;
import com.gs.collections.impl.utility.ArrayIterate;
import com.gs.collections.impl.utility.Iterate;
import com.gs.collections.impl.utility.LazyIterate;
import com.gs.collections.impl.utility.internal.IterableIterate;
import com.gs.collections.impl.utility.internal.MutableCollectionIterate;
import com.gs.collections.impl.utility.internal.SetIterables;
import net.jcip.annotations.NotThreadSafe;

@NotThreadSafe
public class UnifiedSet
        implements MutableSet, Externalizable, Pool, BatchIterable
{
    protected static final Object NULL_KEY = new Object()
    {
        @Override
        public boolean equals(Object obj)
        {
            throw new RuntimeException("Possible corruption through unsynchronized concurrent modification.");
        }

        @Override
        public int hashCode()
        {
            throw new RuntimeException("Possible corruption through unsynchronized concurrent modification.");
        }

        @Override
        public String toString()
        {
            return "UnifiedSet.NULL_KEY";
        }
    };

    protected static final float DEFAULT_LOAD_FACTOR = 0.75f;

    protected static final int DEFAULT_INITIAL_CAPACITY = 8;

    private static final long serialVersionUID = 1L;

    protected transient Object[] table;

    protected transient int occupied;

    protected float loadFactor = DEFAULT_LOAD_FACTOR;

    protected int maxSize;

    public UnifiedSet()
    {
        this.allocate(DEFAULT_INITIAL_CAPACITY << 1);
    }

    public UnifiedSet(int initialCapacity)
    {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }

    public UnifiedSet(int initialCapacity, float loadFactor)
    {
        if (initialCapacity < 0)
        {
            throw new IllegalArgumentException("initial capacity cannot be less than 0");
        }
        this.loadFactor = loadFactor;
        this.init(this.fastCeil(initialCapacity / loadFactor));
    }

    public UnifiedSet(Collection collection)
    {
        this(Math.max(collection.size(), DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
        this.addAll(collection);
    }

    public UnifiedSet(UnifiedSet set)
    {
        this.maxSize = set.maxSize;
        this.loadFactor = set.loadFactor;
        this.occupied = set.occupied;
        this.allocateTable(set.table.length);

        for (int i = 0; i < set.table.length; i++)
        {
            Object key = set.table[i];
            if (key instanceof ChainedBucket)
            {
                this.table[i] = ((ChainedBucket) key).copy();
            }
            else if (key != null)
            {
                this.table[i] = key;
            }
        }
    }

    public static  UnifiedSet newSet()
    {
        return new UnifiedSet();
    }

    public static  UnifiedSet newSet(int size)
    {
        return new UnifiedSet(size);
    }

    public static  UnifiedSet newSet(Iterable source)
    {
        if (source instanceof UnifiedSet)
        {
            return new UnifiedSet((UnifiedSet) source);
        }
        if (source instanceof Collection)
        {
            return new UnifiedSet((Collection) source);
        }
        if (source == null)
        {
            throw new NullPointerException();
        }
        UnifiedSet result = source instanceof RichIterable
                ? UnifiedSet.newSet(((RichIterable) source).size())
                : UnifiedSet.newSet();
        Iterate.forEachWith(source, Procedures2.addToCollection(), result);
        return result;
    }

    public static  UnifiedSet newSet(int size, float loadFactor)
    {
        return new UnifiedSet(size, loadFactor);
    }

    public static  UnifiedSet newSetWith(K... elements)
    {
        return UnifiedSet.newSet(elements.length).with(elements);
    }

    private int fastCeil(float v)
    {
        int possibleResult = (int) v;
        if (v - possibleResult > 0.0F)
        {
            possibleResult++;
        }
        return possibleResult;
    }

    protected int init(int initialCapacity)
    {
        int capacity = 1;
        while (capacity < initialCapacity)
        {
            capacity <<= 1;
        }

        return this.allocate(capacity);
    }

    protected int allocate(int capacity)
    {
        this.allocateTable(capacity);
        this.computeMaxSize(capacity);

        return capacity;
    }

    protected void allocateTable(int sizeToAllocate)
    {
        this.table = new Object[sizeToAllocate];
    }

    protected void computeMaxSize(int capacity)
    {
        // need at least one free slot for open addressing
        this.maxSize = Math.min(capacity - 1, (int) (capacity * this.loadFactor));
    }

    protected final int index(Object key)
    {
        // This function ensures that hashCodes that differ only by
        // constant multiples at each bit position have a bounded
        // number of collisions (approximately 8 at default load factor).
        int h = key == null ? 0 : key.hashCode();
        h ^= h >>> 20 ^ h >>> 12;
        h ^= h >>> 7 ^ h >>> 4;
        return h & this.table.length - 1;
    }

    public void clear()
    {
        if (this.occupied == 0)
        {
            return;
        }
        this.occupied = 0;
        Object[] set = this.table;

        for (int i = set.length; i-- > 0; )
        {
            set[i] = null;
        }
    }

    public boolean add(T key)
    {
        int index = this.index(key);
        Object cur = this.table[index];
        if (cur == null)
        {
            this.table[index] = toSentinelIfNull(key);
            if (++this.occupied > this.maxSize)
            {
                this.rehash();
            }
            return true;
        }
        if (cur instanceof ChainedBucket || !this.nonNullTableObjectEquals(cur, key))
        {
            return this.chainedAdd(key, index);
        }
        return false;
    }

    private boolean chainedAdd(T key, int index)
    {
        Object realKey = toSentinelIfNull(key);
        if (this.table[index] instanceof ChainedBucket)
        {
            ChainedBucket bucket = (ChainedBucket) this.table[index];
            do
            {
                if (this.nonNullTableObjectEquals(bucket.zero, key))
                {
                    return false;
                }
                if (bucket.one == null)
                {
                    bucket.one = realKey;
                    if (++this.occupied > this.maxSize)
                    {
                        this.rehash();
                    }
                    return true;
                }
                if (this.nonNullTableObjectEquals(bucket.one, key))
                {
                    return false;
                }
                if (bucket.two == null)
                {
                    bucket.two = realKey;
                    if (++this.occupied > this.maxSize)
                    {
                        this.rehash();
                    }
                    return true;
                }
                if (this.nonNullTableObjectEquals(bucket.two, key))
                {
                    return false;
                }
                if (bucket.three instanceof ChainedBucket)
                {
                    bucket = (ChainedBucket) bucket.three;
                    continue;
                }
                if (bucket.three == null)
                {
                    bucket.three = realKey;
                    if (++this.occupied > this.maxSize)
                    {
                        this.rehash();
                    }
                    return true;
                }
                if (this.nonNullTableObjectEquals(bucket.three, key))
                {
                    return false;
                }
                bucket.three = new ChainedBucket(bucket.three, realKey);
                if (++this.occupied > this.maxSize)
                {
                    this.rehash();
                }
                return true;
            }
            while (true);
        }
        ChainedBucket newBucket = new ChainedBucket(this.table[index], realKey);
        this.table[index] = newBucket;
        if (++this.occupied > this.maxSize)
        {
            this.rehash();
        }
        return true;
    }

    protected void rehash()
    {
        this.rehash(this.table.length << 1);
    }

    protected void rehash(int newCapacity)
    {
        int oldLength = this.table.length;
        Object[] old = this.table;
        this.allocate(newCapacity);
        this.occupied = 0;

        for (int i = 0; i < oldLength; i++)
        {
            Object oldKey = old[i];
            if (oldKey instanceof ChainedBucket)
            {
                ChainedBucket bucket = (ChainedBucket) oldKey;
                do
                {
                    if (bucket.zero != null)
                    {
                        this.add(this.nonSentinel(bucket.zero));
                    }
                    if (bucket.one == null)
                    {
                        break;
                    }
                    this.add(this.nonSentinel(bucket.one));
                    if (bucket.two == null)
                    {
                        break;
                    }
                    this.add(this.nonSentinel(bucket.two));
                    if (bucket.three != null)
                    {
                        if (bucket.three instanceof ChainedBucket)
                        {
                            bucket = (ChainedBucket) bucket.three;
                            continue;
                        }
                        this.add(this.nonSentinel(bucket.three));
                    }
                    break;
                }
                while (true);
            }
            else if (oldKey != null)
            {
                this.add(this.nonSentinel(oldKey));
            }
        }
    }

    public boolean contains(Object key)
    {
        int index = this.index(key);
        Object cur = this.table[index];
        if (cur == null)
        {
            return false;
        }
        if (cur instanceof ChainedBucket)
        {
            return this.chainContains((ChainedBucket) cur, (T) key);
        }
        return this.nonNullTableObjectEquals(cur, (T) key);
    }

    private boolean chainContains(ChainedBucket bucket, T key)
    {
        do
        {
            if (this.nonNullTableObjectEquals(bucket.zero, key))
            {
                return true;
            }
            if (bucket.one == null)
            {
                return false;
            }
            if (this.nonNullTableObjectEquals(bucket.one, key))
            {
                return true;
            }
            if (bucket.two == null)
            {
                return false;
            }
            if (this.nonNullTableObjectEquals(bucket.two, key))
            {
                return true;
            }
            if (bucket.three == null)
            {
                return false;
            }
            if (bucket.three instanceof ChainedBucket)
            {
                bucket = (ChainedBucket) bucket.three;
                continue;
            }
            return this.nonNullTableObjectEquals(bucket.three, key);
        }
        while (true);
    }

    public int getBatchCount(int batchSize)
    {
        return Math.max(1, this.table.length / batchSize);
    }

    public void batchForEach(Procedure procedure, int sectionIndex, int sectionCount)
    {
        Object[] set = this.table;
        int sectionSize = set.length / sectionCount;
        int start = sectionSize * sectionIndex;
        int end = sectionIndex == sectionCount - 1 ? set.length : start + sectionSize;
        for (int i = start; i < end; i++)
        {
            Object cur = set[i];
            if (cur != null)
            {
                if (cur instanceof ChainedBucket)
                {
                    this.chainedForEach((ChainedBucket) cur, procedure);
                }
                else
                {
                    procedure.value(this.nonSentinel(cur));
                }
            }
        }
    }

    public void forEach(Procedure procedure)
    {
        for (int i = 0; i < this.table.length; i++)
        {
            Object cur = this.table[i];
            if (cur instanceof ChainedBucket)
            {
                this.chainedForEach((ChainedBucket) cur, procedure);
            }
            else if (cur != null)
            {
                procedure.value(this.nonSentinel(cur));
            }
        }
    }

    private void chainedForEach(ChainedBucket bucket, Procedure procedure)
    {
        do
        {
            procedure.value(this.nonSentinel(bucket.zero));
            if (bucket.one == null)
            {
                return;
            }
            procedure.value(this.nonSentinel(bucket.one));
            if (bucket.two == null)
            {
                return;
            }
            procedure.value(this.nonSentinel(bucket.two));
            if (bucket.three == null)
            {
                return;
            }
            if (bucket.three instanceof ChainedBucket)
            {
                bucket = (ChainedBucket) bucket.three;
                continue;
            }
            procedure.value(this.nonSentinel(bucket.three));
            return;
        }
        while (true);
    }

    public 

void forEachWith(Procedure2 procedure, P parameter) { for (int i = 0; i < this.table.length; i++) { Object cur = this.table[i]; if (cur instanceof ChainedBucket) { this.chainedForEachWith((ChainedBucket) cur, procedure, parameter); } else if (cur != null) { procedure.value(this.nonSentinel(cur), parameter); } } } private

void chainedForEachWith( ChainedBucket bucket, Procedure2 procedure, P parameter) { do { procedure.value(this.nonSentinel(bucket.zero), parameter); if (bucket.one == null) { return; } procedure.value(this.nonSentinel(bucket.one), parameter); if (bucket.two == null) { return; } procedure.value(this.nonSentinel(bucket.two), parameter); if (bucket.three == null) { return; } if (bucket.three instanceof ChainedBucket) { bucket = (ChainedBucket) bucket.three; continue; } procedure.value(this.nonSentinel(bucket.three), parameter); return; } while (true); } public void forEachWithIndex(ObjectIntProcedure objectIntProcedure) { int count = 0; for (int i = 0; i < this.table.length; i++) { Object cur = this.table[i]; if (cur instanceof ChainedBucket) { count = this.chainedForEachWithIndex((ChainedBucket) cur, objectIntProcedure, count); } else if (cur != null) { objectIntProcedure.value(this.nonSentinel(cur), count++); } } } private int chainedForEachWithIndex(ChainedBucket bucket, ObjectIntProcedure procedure, int count) { do { procedure.value(this.nonSentinel(bucket.zero), count++); if (bucket.one == null) { return count; } procedure.value(this.nonSentinel(bucket.one), count++); if (bucket.two == null) { return count; } procedure.value(this.nonSentinel(bucket.two), count++); if (bucket.three == null) { return count; } if (bucket.three instanceof ChainedBucket) { bucket = (ChainedBucket) bucket.three; continue; } procedure.value(this.nonSentinel(bucket.three), count++); return count; } while (true); } public UnifiedSet newEmpty() { return UnifiedSet.newSet(); } public T getFirst() { return this.isEmpty() ? null : this.iterator().next(); } public T getLast() { return this.getFirst(); } public UnifiedSet select(Predicate predicate) { return this.select(predicate, this.newEmpty()); } public > R select(Predicate predicate, R target) { this.forEach(new SelectProcedure(predicate, target)); return target; } public

UnifiedSet selectWith( Predicate2 predicate, P parameter) { return this.selectWith(predicate, parameter, this.newEmpty()); } public > R selectWith( final Predicate2 predicate, P parameter, final R targetCollection) { this.forEachWith(new Procedure2() { public void value(T each, P parm) { if (predicate.accept(each, parm)) { targetCollection.add(each); } } }, parameter); return targetCollection; } public UnifiedSet reject(Predicate predicate) { return this.reject(predicate, this.newEmpty()); } public > R reject(Predicate predicate, R target) { this.forEach(new RejectProcedure(predicate, target)); return target; } public

UnifiedSet rejectWith( Predicate2 predicate, P parameter) { return this.rejectWith(predicate, parameter, this.newEmpty()); } public > R rejectWith( final Predicate2 predicate, P parameter, final R targetCollection) { this.forEachWith(new Procedure2() { public void value(T each, P parm) { if (!predicate.accept(each, parm)) { targetCollection.add(each); } } }, parameter); return targetCollection; } public

Twin> selectAndRejectWith( final Predicate2 predicate, P parameter) { final MutableList positiveResult = Lists.mutable.of(); final MutableList negativeResult = Lists.mutable.of(); this.forEachWith(new Procedure2() { public void value(T each, P parm) { (predicate.accept(each, parm) ? positiveResult : negativeResult).add(each); } }, parameter); return Tuples.twin(positiveResult, negativeResult); } public PartitionMutableSet partition(Predicate predicate) { PartitionMutableSet partitionUnifiedSet = new PartitionUnifiedSet(); this.forEach(new PartitionProcedure(predicate, partitionUnifiedSet)); return partitionUnifiedSet; } public

PartitionMutableSet partitionWith(Predicate2 predicate, P parameter) { PartitionMutableSet partitionUnifiedSet = new PartitionUnifiedSet(); this.forEach(new PartitionPredicate2Procedure(predicate, parameter, partitionUnifiedSet)); return partitionUnifiedSet; } public UnifiedSet selectInstancesOf(Class clazz) { UnifiedSet result = UnifiedSet.newSet(); this.forEach(new SelectInstancesOfProcedure(clazz, result)); return result; } public void removeIf(Predicate predicate) { IterableIterate.removeIf(this, predicate); } public

void removeIfWith(Predicate2 predicate, P parameter) { IterableIterate.removeIfWith(this, predicate, parameter); } public UnifiedSet collect(Function function) { return this.collect(function, UnifiedSet.newSet()); } public MutableBooleanSet collectBoolean(BooleanFunction booleanFunction) { return this.collectBoolean(booleanFunction, new BooleanHashSet()); } public R collectBoolean(BooleanFunction booleanFunction, R target) { this.forEach(new CollectBooleanProcedure(booleanFunction, target)); return target; } public MutableByteSet collectByte(ByteFunction byteFunction) { return this.collectByte(byteFunction, new ByteHashSet()); } public R collectByte(ByteFunction byteFunction, R target) { this.forEach(new CollectByteProcedure(byteFunction, target)); return target; } public MutableCharSet collectChar(CharFunction charFunction) { return this.collectChar(charFunction, new CharHashSet()); } public R collectChar(CharFunction charFunction, R target) { this.forEach(new CollectCharProcedure(charFunction, target)); return target; } public MutableDoubleSet collectDouble(DoubleFunction doubleFunction) { return this.collectDouble(doubleFunction, new DoubleHashSet()); } public R collectDouble(DoubleFunction doubleFunction, R target) { this.forEach(new CollectDoubleProcedure(doubleFunction, target)); return target; } public MutableFloatSet collectFloat(FloatFunction floatFunction) { return this.collectFloat(floatFunction, new FloatHashSet()); } public R collectFloat(FloatFunction floatFunction, R target) { this.forEach(new CollectFloatProcedure(floatFunction, target)); return target; } public MutableIntSet collectInt(IntFunction intFunction) { return this.collectInt(intFunction, new IntHashSet()); } public R collectInt(IntFunction intFunction, R target) { this.forEach(new CollectIntProcedure(intFunction, target)); return target; } public MutableLongSet collectLong(LongFunction longFunction) { return this.collectLong(longFunction, new LongHashSet()); } public R collectLong(LongFunction longFunction, R target) { this.forEach(new CollectLongProcedure(longFunction, target)); return target; } public MutableShortSet collectShort(ShortFunction shortFunction) { return this.collectShort(shortFunction, new ShortHashSet()); } public R collectShort(ShortFunction shortFunction, R target) { this.forEach(new CollectShortProcedure(shortFunction, target)); return target; } public > R collect(Function function, R target) { this.forEach(new CollectProcedure(function, target)); return target; } public UnifiedSet flatCollect(Function> function) { return this.flatCollect(function, UnifiedSet.newSet()); } public > R flatCollect( Function> function, R target) { this.forEach(new FlatCollectProcedure(function, target)); return target; } public UnifiedSet collectWith(Function2 function, P parameter) { return this.collectWith(function, parameter, UnifiedSet.newSet()); } public > R collectWith( final Function2 function, P parameter, final R targetCollection) { this.forEachWith(new Procedure2() { public void value(T each, P parm) { targetCollection.add(function.value(each, parm)); } }, parameter); return targetCollection; } public UnifiedSet collectIf( Predicate predicate, Function function) { return this.collectIf(predicate, function, UnifiedSet.newSet()); } public > R collectIf( Predicate predicate, Function function, R target) { this.forEach(new CollectIfProcedure(target, function, predicate)); return target; } public T detect(Predicate predicate) { for (int i = 0; i < this.table.length; i++) { Object cur = this.table[i]; if (cur instanceof ChainedBucket) { Object chainedDetect = this.chainedDetect((ChainedBucket) cur, predicate); if (chainedDetect != null) { return this.nonSentinel(chainedDetect); } } else if (cur != null) { T each = this.nonSentinel(cur); if (predicate.accept(each)) { return each; } } } return null; } private Object chainedDetect(ChainedBucket bucket, Predicate predicate) { do { if (predicate.accept(this.nonSentinel(bucket.zero))) { return bucket.zero; } if (bucket.one == null) { return null; } if (predicate.accept(this.nonSentinel(bucket.one))) { return bucket.one; } if (bucket.two == null) { return null; } if (predicate.accept(this.nonSentinel(bucket.two))) { return bucket.two; } if (bucket.three == null) { return null; } if (bucket.three instanceof ChainedBucket) { bucket = (ChainedBucket) bucket.three; continue; } if (predicate.accept(this.nonSentinel(bucket.three))) { return bucket.three; } return null; } while (true); } public T min(Comparator comparator) { return Iterate.min(this, comparator); } public T max(Comparator comparator) { return Iterate.max(this, comparator); } public T min() { return Iterate.min(this); } public T max() { return Iterate.max(this); } public > T minBy(Function function) { return IterableIterate.minBy(this, function); } public > T maxBy(Function function) { return IterableIterate.maxBy(this, function); } public T detectIfNone(Predicate predicate, Function0 function) { T result = this.detect(predicate); return result == null ? function.value() : result; } public

T detectWith(Predicate2 predicate, P parameter) { return IterableIterate.detectWith(this, predicate, parameter); } public

T detectWithIfNone( Predicate2 predicate, P parameter, Function0 function) { T result = this.detectWith(predicate, parameter); return result == null ? function.value() : result; } public int count(Predicate predicate) { CountProcedure procedure = new CountProcedure(predicate); this.forEach(procedure); return procedure.getCount(); } public

int countWith(final Predicate2 predicate, P parameter) { final Counter count = new Counter(); this.forEachWith(new Procedure2() { public void value(T each, P parm) { if (predicate.accept(each, parm)) { count.increment(); } } }, parameter); return count.getCount(); } public boolean anySatisfy(Predicate predicate) { for (int i = 0; i < this.table.length; i++) { Object cur = this.table[i]; if (cur instanceof ChainedBucket) { if (this.chainedAnySatisfy((ChainedBucket) cur, predicate)) { return true; } } else if (cur != null) { if (predicate.accept(this.nonSentinel(cur))) { return true; } } } return false; } private boolean chainedAnySatisfy(ChainedBucket bucket, Predicate predicate) { do { if (predicate.accept(this.nonSentinel(bucket.zero))) { return true; } if (bucket.one == null) { return false; } if (predicate.accept(this.nonSentinel(bucket.one))) { return true; } if (bucket.two == null) { return false; } if (predicate.accept(this.nonSentinel(bucket.two))) { return true; } if (bucket.three == null) { return false; } if (bucket.three instanceof ChainedBucket) { bucket = (ChainedBucket) bucket.three; continue; } return predicate.accept(this.nonSentinel(bucket.three)); } while (true); } public

boolean anySatisfyWith( Predicate2 predicate, P parameter) { return IterableIterate.anySatisfyWith(this, predicate, parameter); } public boolean allSatisfy(Predicate predicate) { for (int i = 0; i < this.table.length; i++) { Object cur = this.table[i]; if (cur instanceof ChainedBucket) { if (!this.chainedAllSatisfy((ChainedBucket) cur, predicate)) { return false; } } else if (cur != null) { if (!predicate.accept(this.nonSentinel(cur))) { return false; } } } return true; } private boolean chainedAllSatisfy(ChainedBucket bucket, Predicate predicate) { do { if (!predicate.accept(this.nonSentinel(bucket.zero))) { return false; } if (bucket.one == null) { return true; } if (!predicate.accept(this.nonSentinel(bucket.one))) { return false; } if (bucket.two == null) { return true; } if (!predicate.accept(this.nonSentinel(bucket.two))) { return false; } if (bucket.three == null) { return true; } if (bucket.three instanceof ChainedBucket) { bucket = (ChainedBucket) bucket.three; continue; } return predicate.accept(this.nonSentinel(bucket.three)); } while (true); } public

boolean allSatisfyWith( Predicate2 predicate, P parameter) { return IterableIterate.allSatisfyWith(this, predicate, parameter); } public boolean noneSatisfy(Predicate predicate) { return IterableIterate.noneSatisfy(this, predicate); } public

boolean noneSatisfyWith( Predicate2 predicate, P parameter) { return IterableIterate.noneSatisfyWith(this, predicate, parameter); } public IV injectInto(IV injectedValue, Function2 function) { return IterableIterate.injectInto(injectedValue, this, function); } public int injectInto(int injectedValue, IntObjectToIntFunction function) { return IterableIterate.injectInto(injectedValue, this, function); } public long injectInto(long injectedValue, LongObjectToLongFunction function) { return IterableIterate.injectInto(injectedValue, this, function); } public double injectInto(double injectedValue, DoubleObjectToDoubleFunction function) { return IterableIterate.injectInto(injectedValue, this, function); } public float injectInto(float injectedValue, FloatObjectToFloatFunction function) { return IterableIterate.injectInto(injectedValue, this, function); } public long sumOfInt(IntFunction function) { return IterableIterate.sumOfInt(this, function); } public double sumOfFloat(FloatFunction function) { return IterableIterate.sumOfFloat(this, function); } public long sumOfLong(LongFunction function) { return IterableIterate.sumOfLong(this, function); } public double sumOfDouble(DoubleFunction function) { return IterableIterate.sumOfDouble(this, function); } public IV injectIntoWith( IV injectValue, Function3 function, P parameter) { return IterableIterate.injectIntoWith(injectValue, this, function, parameter); } public MutableList toList() { return FastList.newList(this); } public MutableList toSortedList() { return FastList.newList(this).sortThis(); } public MutableList toSortedList(Comparator comparator) { return FastList.newList(this).sortThis(comparator); } public > MutableList toSortedListBy( Function function) { return this.toSortedList(Comparators.byFunction(function)); } public MutableSortedSet toSortedSet() { return TreeSortedSet.newSet(null, this); } public MutableSortedSet toSortedSet(Comparator comparator) { return TreeSortedSet.newSet(comparator, this); } public > MutableSortedSet toSortedSetBy(Function function) { return this.toSortedSet(Comparators.byFunction(function)); } public UnifiedSet toSet() { return UnifiedSet.newSet(this); } public MutableBag toBag() { return HashBag.newBag(this); } public MutableMap toMap( Function keyFunction, Function valueFunction) { return UnifiedMap.newMap(this.size()).collectKeysAndValues(this, keyFunction, valueFunction); } public MutableSortedMap toSortedMap( Function keyFunction, Function valueFunction) { return TreeSortedMap.newMap().collectKeysAndValues(this, keyFunction, valueFunction); } public MutableSortedMap toSortedMap( Comparator comparator, Function keyFunction, Function valueFunction) { return TreeSortedMap.newMap(comparator).collectKeysAndValues(this, keyFunction, valueFunction); } public LazyIterable asLazy() { return LazyIterate.adapt(this); } public MutableSet asUnmodifiable() { return UnmodifiableMutableSet.of(this); } public MutableSet asSynchronized() { return SynchronizedMutableSet.of(this); } public ImmutableSet toImmutable() { return Sets.immutable.ofAll(this); } public boolean notEmpty() { return !this.isEmpty(); } public boolean isEmpty() { return this.occupied == 0; } public UnifiedSet with(T element) { this.add(element); return this; } public UnifiedSet with(T element1, T element2) { this.add(element1); this.add(element2); return this; } public UnifiedSet with(T element1, T element2, T element3) { this.add(element1); this.add(element2); this.add(element3); return this; } public UnifiedSet with(T... elements) { this.addAll(Arrays.asList(elements)); return this; } public UnifiedSet withAll(Iterable iterable) { this.addAllIterable(iterable); return this; } public UnifiedSet without(T element) { this.remove(element); return this; } public UnifiedSet withoutAll(Iterable elements) { this.removeAllIterable(elements); return this; } public boolean addAll(Collection collection) { return this.addAllIterable(collection); } public boolean addAllIterable(Iterable iterable) { if (iterable instanceof UnifiedSet) { return this.copySet((UnifiedSet) iterable); } int size = Iterate.sizeOf(iterable); this.ensureCapacity(size); int oldSize = this.size(); Iterate.forEachWith(iterable, Procedures2.addToCollection(), this); return this.size() != oldSize; } private void ensureCapacity(int size) { if (size > this.maxSize) { size = (int) (size / this.loadFactor) + 1; int capacity = Integer.highestOneBit(size); if (size != capacity) { capacity <<= 1; } this.rehash(capacity); } } protected boolean copySet(UnifiedSet unifiedset) { //todo: optimize for current size == 0 boolean changed = false; for (int i = 0; i < unifiedset.table.length; i++) { Object cur = unifiedset.table[i]; if (cur instanceof ChainedBucket) { changed |= this.copyChain((ChainedBucket) cur); } else if (cur != null) { changed |= this.add(this.nonSentinel(cur)); } } return changed; } private boolean copyChain(ChainedBucket bucket) { boolean changed = false; do { changed |= this.add(this.nonSentinel(bucket.zero)); if (bucket.one == null) { return changed; } changed |= this.add(this.nonSentinel(bucket.one)); if (bucket.two == null) { return changed; } changed |= this.add(this.nonSentinel(bucket.two)); if (bucket.three == null) { return changed; } if (bucket.three instanceof ChainedBucket) { bucket = (ChainedBucket) bucket.three; continue; } changed |= this.add(this.nonSentinel(bucket.three)); return changed; } while (true); } public boolean remove(Object key) { int index = this.index(key); Object cur = this.table[index]; if (cur == null) { return false; } if (cur instanceof ChainedBucket) { return this.removeFromChain((ChainedBucket) cur, (T) key, index); } if (this.nonNullTableObjectEquals(cur, (T) key)) { this.table[index] = null; this.occupied--; return true; } return false; } private boolean removeFromChain(ChainedBucket bucket, T key, int index) { if (this.nonNullTableObjectEquals(bucket.zero, key)) { bucket.zero = bucket.removeLast(0); if (bucket.zero == null) { this.table[index] = null; } this.occupied--; return true; } if (bucket.one == null) { return false; } if (this.nonNullTableObjectEquals(bucket.one, key)) { bucket.one = bucket.removeLast(1); this.occupied--; return true; } if (bucket.two == null) { return false; } if (this.nonNullTableObjectEquals(bucket.two, key)) { bucket.two = bucket.removeLast(2); this.occupied--; return true; } if (bucket.three == null) { return false; } if (bucket.three instanceof ChainedBucket) { return this.removeDeepChain(bucket, key); } if (this.nonNullTableObjectEquals(bucket.three, key)) { bucket.three = bucket.removeLast(3); this.occupied--; return true; } return false; } private boolean removeDeepChain(ChainedBucket oldBucket, T key) { do { ChainedBucket bucket = (ChainedBucket) oldBucket.three; if (this.nonNullTableObjectEquals(bucket.zero, key)) { bucket.zero = bucket.removeLast(0); if (bucket.zero == null) { oldBucket.three = null; } this.occupied--; return true; } if (bucket.one == null) { return false; } if (this.nonNullTableObjectEquals(bucket.one, key)) { bucket.one = bucket.removeLast(1); this.occupied--; return true; } if (bucket.two == null) { return false; } if (this.nonNullTableObjectEquals(bucket.two, key)) { bucket.two = bucket.removeLast(2); this.occupied--; return true; } if (bucket.three == null) { return false; } if (bucket.three instanceof ChainedBucket) { oldBucket = bucket; continue; } if (this.nonNullTableObjectEquals(bucket.three, key)) { bucket.three = bucket.removeLast(3); this.occupied--; return true; } return false; } while (true); } public int size() { return this.occupied; } @Override public boolean equals(Object object) { if (this == object) { return true; } if (!(object instanceof Set)) { return false; } Set other = (Set) object; return this.size() == other.size() && this.containsAll(other); } @Override public int hashCode() { int hashCode = 0; for (int i = 0; i < this.table.length; i++) { Object cur = this.table[i]; if (cur instanceof ChainedBucket) { hashCode += this.chainedHashCode((ChainedBucket) cur); } else if (cur != null) { hashCode += cur == NULL_KEY ? 0 : cur.hashCode(); } } return hashCode; } private int chainedHashCode(ChainedBucket bucket) { int hashCode = 0; do { hashCode += bucket.zero == NULL_KEY ? 0 : bucket.zero.hashCode(); if (bucket.one == null) { return hashCode; } hashCode += bucket.one == NULL_KEY ? 0 : bucket.one.hashCode(); if (bucket.two == null) { return hashCode; } hashCode += bucket.two == NULL_KEY ? 0 : bucket.two.hashCode(); if (bucket.three == null) { return hashCode; } if (bucket.three instanceof ChainedBucket) { bucket = (ChainedBucket) bucket.three; continue; } hashCode += bucket.three == NULL_KEY ? 0 : bucket.three.hashCode(); return hashCode; } while (true); } @Override public String toString() { return this.makeString("[", ", ", "]"); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { int size = in.readInt(); this.loadFactor = in.readFloat(); this.init(Math.max((int) (size / this.loadFactor) + 1, DEFAULT_INITIAL_CAPACITY)); for (int i = 0; i < size; i++) { this.add((T) in.readObject()); } } public void writeExternal(ObjectOutput out) throws IOException { out.writeInt(this.size()); out.writeFloat(this.loadFactor); for (int i = 0; i < this.table.length; i++) { Object o = this.table[i]; if (o != null) { if (o instanceof ChainedBucket) { this.writeExternalChain(out, (ChainedBucket) o); } else { out.writeObject(this.nonSentinel(o)); } } } } private void writeExternalChain(ObjectOutput out, ChainedBucket bucket) throws IOException { do { out.writeObject(this.nonSentinel(bucket.zero)); if (bucket.one == null) { return; } out.writeObject(this.nonSentinel(bucket.one)); if (bucket.two == null) { return; } out.writeObject(this.nonSentinel(bucket.two)); if (bucket.three == null) { return; } if (bucket.three instanceof ChainedBucket) { bucket = (ChainedBucket) bucket.three; continue; } out.writeObject(this.nonSentinel(bucket.three)); return; } while (true); } public boolean containsAll(Collection collection) { return Iterate.allSatisfyWith(collection, Predicates2.in(), this); } public boolean containsAllIterable(Iterable source) { return Iterate.allSatisfyWith(source, Predicates2.in(), this); } public boolean containsAllArguments(Object... elements) { return ArrayIterate.allSatisfyWith(elements, Predicates2.in(), this); } public boolean removeAll(Collection collection) { return this.removeAllIterable(collection); } public boolean removeAllIterable(Iterable iterable) { boolean changed = false; for (Object each : iterable) { changed |= this.remove(each); } return changed; } private void addIfFound(T key, UnifiedSet other) { int index = this.index(key); Object cur = this.table[index]; if (cur == null) { return; } if (cur instanceof ChainedBucket) { this.addIfFoundFromChain((ChainedBucket) cur, key, other); return; } if (this.nonNullTableObjectEquals(cur, key)) { other.add(this.nonSentinel(cur)); } } private void addIfFoundFromChain(ChainedBucket bucket, T key, UnifiedSet other) { do { if (this.nonNullTableObjectEquals(bucket.zero, key)) { other.add(this.nonSentinel(bucket.zero)); return; } if (bucket.one == null) { return; } if (this.nonNullTableObjectEquals(bucket.one, key)) { other.add(this.nonSentinel(bucket.one)); return; } if (bucket.two == null) { return; } if (this.nonNullTableObjectEquals(bucket.two, key)) { other.add(this.nonSentinel(bucket.two)); return; } if (bucket.three == null) { return; } if (bucket.three instanceof ChainedBucket) { bucket = (ChainedBucket) bucket.three; continue; } if (this.nonNullTableObjectEquals(bucket.three, key)) { other.add(this.nonSentinel(bucket.three)); return; } return; } while (true); } public boolean retainAll(Collection collection) { return this.retainAllIterable(collection); } public boolean retainAllIterable(Iterable iterable) { if (iterable instanceof Set) { return this.retainAllFromSet((Set) iterable); } return this.retainAllFromNonSet(iterable); } private boolean retainAllFromNonSet(Iterable iterable) { int retainedSize = Iterate.sizeOf(iterable); UnifiedSet retainedCopy = new UnifiedSet(retainedSize, this.loadFactor); for (Object key : iterable) { this.addIfFound((T) key, retainedCopy); } if (retainedCopy.size() < this.size()) { this.maxSize = retainedCopy.maxSize; this.occupied = retainedCopy.occupied; this.table = retainedCopy.table; return true; } return false; } private boolean retainAllFromSet(Set collection) { // TODO: turn iterator into a loop boolean result = false; Iterator e = this.iterator(); while (e.hasNext()) { if (!collection.contains(e.next())) { e.remove(); result = true; } } return result; } @Override public UnifiedSet clone() { return new UnifiedSet(this); } public Object[] toArray() { Object[] result = new Object[this.occupied]; this.copyToArray(result); return result; } private void copyToArray(Object[] result) { Object[] table = this.table; int count = 0; for (int i = 0; i < table.length; i++) { Object cur = table[i]; if (cur != null) { if (cur instanceof ChainedBucket) { ChainedBucket bucket = (ChainedBucket) cur; count = this.copyBucketToArray(result, bucket, count); } else { result[count++] = this.nonSentinel(cur); } } } } private int copyBucketToArray(Object[] result, ChainedBucket bucket, int count) { do { result[count++] = this.nonSentinel(bucket.zero); if (bucket.one == null) { break; } result[count++] = this.nonSentinel(bucket.one); if (bucket.two == null) { break; } result[count++] = this.nonSentinel(bucket.two); if (bucket.three == null) { break; } if (bucket.three instanceof ChainedBucket) { bucket = (ChainedBucket) bucket.three; continue; } result[count++] = this.nonSentinel(bucket.three); break; } while (true); return count; } public T[] toArray(T[] array) { int size = this.size(); T[] result = array.length < size ? (T[]) Array.newInstance(array.getClass().getComponentType(), size) : array; this.copyToArray(result); if (size < result.length) { result[size] = null; } return result; } public Iterator iterator() { return new PositionalIterator(); } protected class PositionalIterator implements Iterator { protected int count; protected int position; protected int chainPosition; protected boolean lastReturned; public boolean hasNext() { return this.count < UnifiedSet.this.size(); } public void remove() { if (!this.lastReturned) { throw new IllegalStateException("next() must be called as many times as remove()"); } this.count--; UnifiedSet.this.occupied--; if (this.chainPosition != 0) { this.removeFromChain(); return; } int pos = this.position - 1; Object key = UnifiedSet.this.table[pos]; if (key instanceof ChainedBucket) { this.removeLastFromChain((ChainedBucket) key, pos); return; } UnifiedSet.this.table[pos] = null; this.position = pos; this.lastReturned = false; } protected void removeFromChain() { ChainedBucket chain = (ChainedBucket) UnifiedSet.this.table[this.position]; chain.remove(--this.chainPosition); this.lastReturned = false; } protected void removeLastFromChain(ChainedBucket bucket, int tableIndex) { bucket.removeLast(0); if (bucket.zero == null) { UnifiedSet.this.table[tableIndex] = null; } this.lastReturned = false; } protected T nextFromChain() { ChainedBucket bucket = (ChainedBucket) UnifiedSet.this.table[this.position]; Object cur = bucket.get(this.chainPosition); this.chainPosition++; if (bucket.get(this.chainPosition) == null) { this.chainPosition = 0; this.position++; } this.lastReturned = true; return UnifiedSet.this.nonSentinel(cur); } public T next() { if (!this.hasNext()) { throw new NoSuchElementException("next() called, but the iterator is exhausted"); } this.count++; Object[] table = UnifiedSet.this.table; if (this.chainPosition != 0) { return this.nextFromChain(); } while (table[this.position] == null) { this.position++; } Object cur = table[this.position]; if (cur instanceof ChainedBucket) { return this.nextFromChain(); } this.position++; this.lastReturned = true; return UnifiedSet.this.nonSentinel(cur); } } private static final class ChainedBucket { private Object zero; private Object one; private Object two; private Object three; private ChainedBucket() { } private ChainedBucket(Object first, Object second) { this.zero = first; this.one = second; } public void remove(int i) { if (i > 3) { this.removeLongChain(this, i - 3); } else { switch (i) { case 0: this.zero = this.removeLast(0); return; case 1: this.one = this.removeLast(1); return; case 2: this.two = this.removeLast(2); return; case 3: if (this.three instanceof ChainedBucket) { this.removeLongChain(this, i - 3); return; } this.three = null; return; default: throw new AssertionError(); } } } private void removeLongChain(ChainedBucket oldBucket, int i) { do { ChainedBucket bucket = (ChainedBucket) oldBucket.three; switch (i) { case 0: bucket.zero = bucket.removeLast(0); return; case 1: bucket.one = bucket.removeLast(1); return; case 2: bucket.two = bucket.removeLast(2); return; case 3: if (bucket.three instanceof ChainedBucket) { i -= 3; oldBucket = bucket; continue; } bucket.three = null; return; default: throw new AssertionError(); } } while (true); } public Object get(int i) { ChainedBucket bucket = this; while (i > 3 && bucket.three instanceof ChainedBucket) { bucket = (ChainedBucket) bucket.three; i -= 3; } do { switch (i) { case 0: return bucket.zero; case 1: return bucket.one; case 2: return bucket.two; case 3: if (bucket.three instanceof ChainedBucket) { i -= 3; bucket = (ChainedBucket) bucket.three; continue; } return bucket.three; case 4: return null; // this happens when a bucket is exactly full and we're iterating default: throw new AssertionError(); } } while (true); } public Object removeLast(int cur) { if (this.three instanceof ChainedBucket) { return this.removeLast(this); } if (this.three != null) { Object result = this.three; this.three = null; return cur == 3 ? null : result; } if (this.two != null) { Object result = this.two; this.two = null; return cur == 2 ? null : result; } if (this.one != null) { Object result = this.one; this.one = null; return cur == 1 ? null : result; } this.zero = null; return null; } private Object removeLast(ChainedBucket oldBucket) { do { ChainedBucket bucket = (ChainedBucket) oldBucket.three; if (bucket.three instanceof ChainedBucket) { oldBucket = bucket; continue; } if (bucket.three != null) { Object result = bucket.three; bucket.three = null; return result; } if (bucket.two != null) { Object result = bucket.two; bucket.two = null; return result; } if (bucket.one != null) { Object result = bucket.one; bucket.one = null; return result; } Object result = bucket.zero; oldBucket.three = null; return result; } while (true); } public ChainedBucket copy() { ChainedBucket result = new ChainedBucket(); ChainedBucket dest = result; ChainedBucket src = this; do { dest.zero = src.zero; dest.one = src.one; dest.two = src.two; if (src.three instanceof ChainedBucket) { dest.three = new ChainedBucket(); src = (ChainedBucket) src.three; dest = (ChainedBucket) dest.three; continue; } dest.three = src.three; return result; } while (true); } } public String makeString() { return this.makeString(", "); } public String makeString(String separator) { return this.makeString("", separator, ""); } public String makeString(String start, String separator, String end) { Appendable stringBuilder = new StringBuilder(); this.appendString(stringBuilder, start, separator, end); return stringBuilder.toString(); } public void appendString(Appendable appendable) { this.appendString(appendable, ", "); } public void appendString(Appendable appendable, String separator) { this.appendString(appendable, "", separator, ""); } public void appendString(Appendable appendable, String start, String separator, String end) { IterableIterate.appendString(this, appendable, start, separator, end); } public UnifiedSetMultimap groupBy( Function function) { return this.groupBy(function, UnifiedSetMultimap.newMultimap()); } public > R groupBy( Function function, R target) { this.forEach(new MultimapPutProcedure(target, function)); return target; } public UnifiedSetMultimap groupByEach( Function> function) { return this.groupByEach(function, UnifiedSetMultimap.newMultimap()); } public > R groupByEach( Function> function, R target) { this.forEach(new MultimapEachPutProcedure(target, function)); return target; } public MutableMap groupByUniqueKey(Function function) { throw new UnsupportedOperationException(this.getClass().getSimpleName() + ".groupByUniqueKey() not implemented yet"); } public MutableSet> zip(Iterable that) { return this.zip(that, UnifiedSet.>newSet()); } public >> R zip(Iterable that, R target) { return IterableIterate.zip(this, that, target); } public MutableSet> zipWithIndex() { return this.zipWithIndex(UnifiedSet.>newSet()); } public >> R zipWithIndex(R target) { this.forEach(ZipWithIndexProcedure.create(target)); return target; } public RichIterable> chunk(int size) { return MutableCollectionIterate.chunk(this, size); } public MutableSet union(SetIterable set) { return SetIterables.unionInto(this, set, this.newEmpty()); } public > R unionInto(SetIterable set, R targetSet) { return SetIterables.unionInto(this, set, targetSet); } public MutableSet intersect(SetIterable set) { return SetIterables.intersectInto(this, set, this.newEmpty()); } public > R intersectInto(SetIterable set, R targetSet) { return SetIterables.intersectInto(this, set, targetSet); } public MutableSet difference(SetIterable subtrahendSet) { return SetIterables.differenceInto(this, subtrahendSet, this.newEmpty()); } public > R differenceInto(SetIterable subtrahendSet, R targetSet) { return SetIterables.differenceInto(this, subtrahendSet, targetSet); } public MutableSet symmetricDifference(SetIterable setB) { return SetIterables.symmetricDifferenceInto(this, setB, this.newEmpty()); } public > R symmetricDifferenceInto(SetIterable set, R targetSet) { return SetIterables.symmetricDifferenceInto(this, set, targetSet); } public boolean isSubsetOf(SetIterable candidateSuperset) { return SetIterables.isSubsetOf(this, candidateSuperset); } public boolean isProperSubsetOf(SetIterable candidateSuperset) { return SetIterables.isProperSubsetOf(this, candidateSuperset); } public MutableSet> powerSet() { return (MutableSet>) (MutableSet) SetIterables.powerSet(this); } public LazyIterable> cartesianProduct(SetIterable set) { return SetIterables.cartesianProduct(this, set); } public T get(T key) { int index = this.index(key); Object cur = this.table[index]; if (cur == null) { return null; } if (cur instanceof ChainedBucket) { return this.chainedGet(key, (ChainedBucket) cur); } if (this.nonNullTableObjectEquals(cur, key)) { return (T) cur; } return null; } private T chainedGet(T key, ChainedBucket bucket) { do { if (this.nonNullTableObjectEquals(bucket.zero, key)) { return this.nonSentinel(bucket.zero); } if (bucket.one == null) { return null; } if (this.nonNullTableObjectEquals(bucket.one, key)) { return this.nonSentinel(bucket.one); } if (bucket.two == null) { return null; } if (this.nonNullTableObjectEquals(bucket.two, key)) { return this.nonSentinel(bucket.two); } if (bucket.three instanceof ChainedBucket) { bucket = (ChainedBucket) bucket.three; continue; } if (bucket.three == null) { return null; } if (this.nonNullTableObjectEquals(bucket.three, key)) { return this.nonSentinel(bucket.three); } return null; } while (true); } public T put(T key) { int index = this.index(key); Object cur = this.table[index]; if (cur == null) { this.table[index] = toSentinelIfNull(key); if (++this.occupied > this.maxSize) { this.rehash(); } return key; } if (cur instanceof ChainedBucket || !this.nonNullTableObjectEquals(cur, key)) { return this.chainedPut(key, index); } return this.nonSentinel(cur); } private T chainedPut(T key, int index) { if (this.table[index] instanceof ChainedBucket) { ChainedBucket bucket = (ChainedBucket) this.table[index]; do { if (this.nonNullTableObjectEquals(bucket.zero, key)) { return this.nonSentinel(bucket.zero); } if (bucket.one == null) { bucket.one = toSentinelIfNull(key); if (++this.occupied > this.maxSize) { this.rehash(); } return key; } if (this.nonNullTableObjectEquals(bucket.one, key)) { return this.nonSentinel(bucket.one); } if (bucket.two == null) { bucket.two = toSentinelIfNull(key); if (++this.occupied > this.maxSize) { this.rehash(); } return key; } if (this.nonNullTableObjectEquals(bucket.two, key)) { return this.nonSentinel(bucket.two); } if (bucket.three instanceof ChainedBucket) { bucket = (ChainedBucket) bucket.three; continue; } if (bucket.three == null) { bucket.three = toSentinelIfNull(key); if (++this.occupied > this.maxSize) { this.rehash(); } return key; } if (this.nonNullTableObjectEquals(bucket.three, key)) { return this.nonSentinel(bucket.three); } bucket.three = new ChainedBucket(bucket.three, key); if (++this.occupied > this.maxSize) { this.rehash(); } return key; } while (true); } ChainedBucket newBucket = new ChainedBucket(this.table[index], key); this.table[index] = newBucket; if (++this.occupied > this.maxSize) { this.rehash(); } return key; } public T removeFromPool(T key) { int index = this.index(key); Object cur = this.table[index]; if (cur == null) { return null; } if (cur instanceof ChainedBucket) { return this.removeFromChainForPool((ChainedBucket) cur, key, index); } if (this.nonNullTableObjectEquals(cur, key)) { this.table[index] = null; this.occupied--; return this.nonSentinel(cur); } return null; } private T removeFromChainForPool(ChainedBucket bucket, T key, int index) { if (this.nonNullTableObjectEquals(bucket.zero, key)) { Object result = bucket.zero; bucket.zero = bucket.removeLast(0); if (bucket.zero == null) { this.table[index] = null; } this.occupied--; return this.nonSentinel(result); } if (bucket.one == null) { return null; } if (this.nonNullTableObjectEquals(bucket.one, key)) { Object result = bucket.one; bucket.one = bucket.removeLast(1); this.occupied--; return this.nonSentinel(result); } if (bucket.two == null) { return null; } if (this.nonNullTableObjectEquals(bucket.two, key)) { Object result = bucket.two; bucket.two = bucket.removeLast(2); this.occupied--; return this.nonSentinel(result); } if (bucket.three == null) { return null; } if (bucket.three instanceof ChainedBucket) { return this.removeDeepChainForPool(bucket, key); } if (this.nonNullTableObjectEquals(bucket.three, key)) { Object result = bucket.three; bucket.three = bucket.removeLast(3); this.occupied--; return this.nonSentinel(result); } return null; } private T removeDeepChainForPool(ChainedBucket oldBucket, T key) { do { ChainedBucket bucket = (ChainedBucket) oldBucket.three; if (this.nonNullTableObjectEquals(bucket.zero, key)) { Object result = bucket.zero; bucket.zero = bucket.removeLast(0); if (bucket.zero == null) { oldBucket.three = null; } this.occupied--; return this.nonSentinel(result); } if (bucket.one == null) { return null; } if (this.nonNullTableObjectEquals(bucket.one, key)) { Object result = bucket.one; bucket.one = bucket.removeLast(1); this.occupied--; return this.nonSentinel(result); } if (bucket.two == null) { return null; } if (this.nonNullTableObjectEquals(bucket.two, key)) { Object result = bucket.two; bucket.two = bucket.removeLast(2); this.occupied--; return this.nonSentinel(result); } if (bucket.three == null) { return null; } if (bucket.three instanceof ChainedBucket) { oldBucket = bucket; continue; } if (this.nonNullTableObjectEquals(bucket.three, key)) { Object result = bucket.three; bucket.three = bucket.removeLast(3); this.occupied--; return this.nonSentinel(result); } return null; } while (true); } private T nonSentinel(Object key) { return key == NULL_KEY ? null : (T) key; } private static Object toSentinelIfNull(Object key) { if (key == null) { return NULL_KEY; } return key; } private boolean nonNullTableObjectEquals(Object cur, T key) { return cur == key || (cur == NULL_KEY ? key == null : cur.equals(key)); } public MutableMap aggregateInPlaceBy( Function groupBy, Function0 zeroValueFactory, Procedure2 mutatingAggregator) { MutableMap map = UnifiedMap.newMap(); this.forEach(new MutatingAggregationProcedure(map, groupBy, zeroValueFactory, mutatingAggregator)); return map; } public MutableMap aggregateBy( Function groupBy, Function0 zeroValueFactory, Function2 nonMutatingAggregator) { MutableMap map = UnifiedMap.newMap(); this.forEach(new NonMutatingAggregationProcedure(map, groupBy, zeroValueFactory, nonMutatingAggregator)); return map; } @Beta public ParallelUnsortedSetIterable asParallel(ExecutorService executorService, int batchSize) { if (executorService == null) { throw new NullPointerException(); } if (batchSize < 1) { throw new IllegalArgumentException(); } return new UnifiedSetParallelUnsortedIterable(executorService, batchSize); } private final class UnifiedUnsortedSetBatch extends AbstractBatch implements RootUnsortedSetBatch { private final int chunkStartIndex; private final int chunkEndIndex; private UnifiedUnsortedSetBatch(int chunkStartIndex, int chunkEndIndex) { this.chunkStartIndex = chunkStartIndex; this.chunkEndIndex = chunkEndIndex; } public void forEach(Procedure procedure) { for (int i = this.chunkStartIndex; i < this.chunkEndIndex; i++) { Object cur = UnifiedSet.this.table[i]; if (cur instanceof ChainedBucket) { UnifiedSet.this.chainedForEach((ChainedBucket) cur, procedure); } else if (cur != null) { procedure.value(UnifiedSet.this.nonSentinel(cur)); } } } public boolean anySatisfy(Predicate predicate) { for (int i = this.chunkStartIndex; i < this.chunkEndIndex; i++) { Object cur = UnifiedSet.this.table[i]; if (cur instanceof ChainedBucket) { UnifiedSet.this.chainedAnySatisfy((ChainedBucket) cur, predicate); } else if (cur != null) { if (predicate.accept(UnifiedSet.this.nonSentinel(cur))) { return true; } } } return false; } public boolean allSatisfy(Predicate predicate) { for (int i = this.chunkStartIndex; i < this.chunkEndIndex; i++) { Object cur = UnifiedSet.this.table[i]; if (cur instanceof ChainedBucket) { UnifiedSet.this.chainedAllSatisfy((ChainedBucket) cur, predicate); } else if (cur != null) { if (!predicate.accept(UnifiedSet.this.nonSentinel(cur))) { return false; } } } return true; } public T detect(Predicate predicate) { for (int i = this.chunkStartIndex; i < this.chunkEndIndex; i++) { Object cur = UnifiedSet.this.table[i]; if (cur instanceof ChainedBucket) { Object chainedDetect = UnifiedSet.this.chainedDetect((ChainedBucket) cur, predicate); if (chainedDetect != null) { return UnifiedSet.this.nonSentinel(chainedDetect); } } else if (cur != null) { T each = UnifiedSet.this.nonSentinel(cur); if (predicate.accept(each)) { return each; } } } return null; } public UnsortedSetBatch select(Predicate predicate) { return new SelectUnsortedSetBatch(this, predicate); } public UnsortedSetBatch collect(Function function) { return new CollectUnsortedSetBatch(this, function); } } private final class UnifiedSetParallelUnsortedIterable extends AbstractParallelUnsortedSetIterable> { private final ExecutorService executorService; private final int batchSize; private UnifiedSetParallelUnsortedIterable(ExecutorService executorService, int batchSize) { this.executorService = executorService; this.batchSize = batchSize; } @Override public ExecutorService getExecutorService() { return this.executorService; } @Override public LazyIterable> split() { return new UnifiedSetParallelSplitLazyIterable(); } public void forEach(Procedure procedure) { forEach(this, procedure); } public boolean anySatisfy(Predicate predicate) { return anySatisfy(this, predicate); } public boolean allSatisfy(Predicate predicate) { return allSatisfy(this, predicate); } public T detect(Predicate predicate) { return detect(this, predicate); } private class UnifiedSetParallelSplitIterator implements Iterator> { protected int chunkIndex; public boolean hasNext() { return this.chunkIndex * UnifiedSetParallelUnsortedIterable.this.batchSize < UnifiedSet.this.table.length; } public RootUnsortedSetBatch next() { int chunkStartIndex = this.chunkIndex * UnifiedSetParallelUnsortedIterable.this.batchSize; int chunkEndIndex = (this.chunkIndex + 1) * UnifiedSetParallelUnsortedIterable.this.batchSize; int truncatedChunkEndIndex = Math.min(chunkEndIndex, UnifiedSet.this.table.length); this.chunkIndex++; return new UnifiedUnsortedSetBatch(chunkStartIndex, truncatedChunkEndIndex); } public void remove() { throw new UnsupportedOperationException("Cannot call remove() on " + this.getClass().getSimpleName()); } } private class UnifiedSetParallelSplitLazyIterable extends AbstractLazyIterable> { public void forEach(Procedure> procedure) { for (RootUnsortedSetBatch chunk : this) { procedure.value(chunk); } } public

void forEachWith(Procedure2, ? super P> procedure, P parameter) { for (RootUnsortedSetBatch chunk : this) { procedure.value(chunk, parameter); } } public void forEachWithIndex(ObjectIntProcedure> objectIntProcedure) { throw new UnsupportedOperationException(this.getClass().getSimpleName() + ".forEachWithIndex() not implemented yet"); } public Iterator> iterator() { return new UnifiedSetParallelSplitIterator(); } } } }