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

com.gs.collections.impl.set.strategy.mutable.UnifiedSetWithHashingStrategy 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.strategy.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 com.gs.collections.api.LazyIterable;
import com.gs.collections.api.RichIterable;
import com.gs.collections.api.bag.MutableBag;
import com.gs.collections.api.block.HashingStrategy;
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.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.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.HashingStrategySets;
import com.gs.collections.impl.factory.Lists;
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.multimap.set.strategy.UnifiedSetWithHashingStrategyMultimap;
import com.gs.collections.impl.parallel.BatchIterable;
import com.gs.collections.impl.partition.set.strategy.PartitionUnifiedSetWithHashingStrategy;
import com.gs.collections.impl.set.mutable.SynchronizedMutableSet;
import com.gs.collections.impl.set.mutable.UnifiedSet;
import com.gs.collections.impl.set.mutable.UnmodifiableMutableSet;
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 UnifiedSetWithHashingStrategy
        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 "UnifiedSetWithHashingStrategy.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;

    private HashingStrategy hashingStrategy;

    /**
     * @deprecated No argument default constructor used for serialization. Instantiating an UnifiedSetWithHashingStrategyMultimap with
     * this constructor will have a null hashingStrategy and throw NullPointerException when used.
     */
    @Deprecated
    public UnifiedSetWithHashingStrategy()
    {
    }

    public UnifiedSetWithHashingStrategy(HashingStrategy hashingStrategy)
    {
        if (hashingStrategy == null)
        {
            throw new IllegalArgumentException("Cannot Instantiate UnifiedSetWithHashingStrategy with null HashingStrategy");
        }
        this.hashingStrategy = hashingStrategy;
        this.allocate(DEFAULT_INITIAL_CAPACITY << 1);
    }

    public UnifiedSetWithHashingStrategy(HashingStrategy hashingStrategy, int initialCapacity)
    {
        this(hashingStrategy, initialCapacity, DEFAULT_LOAD_FACTOR);
    }

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

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

    public UnifiedSetWithHashingStrategy(HashingStrategy hashingStrategy, UnifiedSetWithHashingStrategy set)
    {
        this.hashingStrategy = hashingStrategy;
        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  UnifiedSetWithHashingStrategy newSet(HashingStrategy hashingStrategy)
    {
        return new UnifiedSetWithHashingStrategy(hashingStrategy);
    }

    public static  UnifiedSetWithHashingStrategy newSet(UnifiedSetWithHashingStrategy set)
    {
        return new UnifiedSetWithHashingStrategy(set.hashingStrategy, set);
    }

    public static  UnifiedSetWithHashingStrategy newSet(HashingStrategy hashingStrategy, int size)
    {
        return new UnifiedSetWithHashingStrategy(hashingStrategy, size);
    }

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

    public static  UnifiedSetWithHashingStrategy newSet(HashingStrategy hashingStrategy, int size, float loadFactor)
    {
        return new UnifiedSetWithHashingStrategy(hashingStrategy, size, loadFactor);
    }

    public static  UnifiedSetWithHashingStrategy newSetWith(HashingStrategy hashingStrategy, K... elements)
    {
        return UnifiedSetWithHashingStrategy.newSet(hashingStrategy, elements.length).with(elements);
    }

    public HashingStrategy hashingStrategy()
    {
        return this.hashingStrategy;
    }

    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(K 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 = this.hashingStrategy.computeHashCode(key);
        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(K 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(K key, int index)
    {
        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 = toSentinelIfNull(key);
                    if (++this.occupied > this.maxSize)
                    {
                        this.rehash();
                    }
                    return true;
                }
                if (this.nonNullTableObjectEquals(bucket.one, key))
                {
                    return false;
                }
                if (bucket.two == null)
                {
                    bucket.two = toSentinelIfNull(key);
                    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 = toSentinelIfNull(key);
                    if (++this.occupied > this.maxSize)
                    {
                        this.rehash();
                    }
                    return true;
                }
                if (this.nonNullTableObjectEquals(bucket.three, key))
                {
                    return false;
                }
                bucket.three = new ChainedBucket(bucket.three, toSentinelIfNull(key));
                if (++this.occupied > this.maxSize)
                {
                    this.rehash();
                }
                return true;
            }
            while (true);
        }
        ChainedBucket newBucket = new ChainedBucket(this.table[index], toSentinelIfNull(key));
        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((K) key);
        Object cur = this.table[index];
        if (cur == null)
        {
            return false;
        }
        if (cur instanceof ChainedBucket)
        {
            return this.chainContains((ChainedBucket) cur, (K) key);
        }
        return this.nonNullTableObjectEquals(cur, (K) key);
    }

    private boolean chainContains(ChainedBucket bucket, K 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 UnifiedSetWithHashingStrategy newEmpty() { return UnifiedSetWithHashingStrategy.newSet(this.hashingStrategy); } public K getFirst() { return this.isEmpty() ? null : this.iterator().next(); } public K getLast() { return this.getFirst(); } public UnifiedSetWithHashingStrategy 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

UnifiedSetWithHashingStrategy 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(K each, P parm) { if (predicate.accept(each, parm)) { targetCollection.add(each); } } }, parameter); return targetCollection; } public UnifiedSetWithHashingStrategy 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

UnifiedSetWithHashingStrategy 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(K 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(K each, P parm) { (predicate.accept(each, parm) ? positiveResult : negativeResult).add(each); } }, parameter); return Tuples.twin(positiveResult, negativeResult); } public PartitionMutableSet partition(Predicate predicate) { PartitionMutableSet partitionMutableSet = new PartitionUnifiedSetWithHashingStrategy(this.hashingStrategy); this.forEach(new PartitionProcedure(predicate, partitionMutableSet)); return partitionMutableSet; } public

PartitionMutableSet partitionWith(Predicate2 predicate, P parameter) { PartitionMutableSet partitionMutableSet = new PartitionUnifiedSetWithHashingStrategy(this.hashingStrategy); this.forEach(new PartitionPredicate2Procedure(predicate, parameter, partitionMutableSet)); return partitionMutableSet; } public UnifiedSetWithHashingStrategy selectInstancesOf(Class clazz) { UnifiedSetWithHashingStrategy result = (UnifiedSetWithHashingStrategy) this.newEmpty(); 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(K 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 K detect(Predicate predicate) { return IterableIterate.detect(this, predicate); } public K min(Comparator comparator) { return Iterate.min(this, comparator); } public K max(Comparator comparator) { return Iterate.max(this, comparator); } public K min() { return Iterate.min(this); } public K max() { return Iterate.max(this); } public > K minBy(Function function) { return IterableIterate.minBy(this, function); } public > K maxBy(Function function) { return IterableIterate.maxBy(this, function); } public K detectIfNone(Predicate predicate, Function0 function) { K result = this.detect(predicate); return result == null ? function.value() : result; } public

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

K detectWithIfNone( Predicate2 predicate, P parameter, Function0 function) { K 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(K each, P parm) { if (predicate.accept(each, parm)) { count.increment(); } } }, parameter); return count.getCount(); } public boolean anySatisfy(Predicate predicate) { return IterableIterate.anySatisfy(this, predicate); } public

boolean anySatisfyWith( Predicate2 predicate, P parameter) { return IterableIterate.anySatisfyWith(this, predicate, parameter); } public boolean allSatisfy(Predicate predicate) { return IterableIterate.allSatisfy(this, predicate); } 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); } /** * @deprecated since 3.0. Use {@link #asLazy()}.{@link #select(Predicate)} instead. */ @Deprecated public LazyIterable lazySelect(Predicate predicate) { return this.asLazy().select(predicate); } /** * @deprecated since 3.0. Use {@link #asLazy()}.{@link #reject(Predicate)} instead. */ @Deprecated public LazyIterable lazyReject(Predicate predicate) { return this.asLazy().reject(predicate); } /** * @deprecated since 3.0. Use {@link #asLazy()}.{@link #collect(Function)} instead. */ @Deprecated public LazyIterable lazyCollect(Function function) { return this.asLazy().collect(function); } 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 HashingStrategySets.immutable.ofAll(this.hashingStrategy, this); } public boolean notEmpty() { return !this.isEmpty(); } public boolean isEmpty() { return this.occupied == 0; } public UnifiedSetWithHashingStrategy with(K element) { this.add(element); return this; } public UnifiedSetWithHashingStrategy with(K element1, K element2) { this.add(element1); this.add(element2); return this; } public UnifiedSetWithHashingStrategy with(K element1, K element2, K element3) { this.add(element1); this.add(element2); this.add(element3); return this; } public UnifiedSetWithHashingStrategy with(K... elements) { this.addAll(Arrays.asList(elements)); return this; } public UnifiedSetWithHashingStrategy withAll(Iterable iterable) { this.addAllIterable(iterable); return this; } public UnifiedSetWithHashingStrategy without(K element) { this.remove(element); return this; } public UnifiedSetWithHashingStrategy 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 UnifiedSetWithHashingStrategy) { return this.copySet((UnifiedSetWithHashingStrategy) 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(UnifiedSetWithHashingStrategy 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((K) key); Object cur = this.table[index]; if (cur == null) { return false; } if (cur instanceof ChainedBucket) { return this.removeFromChain((ChainedBucket) cur, (K) key, index); } if (this.nonNullTableObjectEquals(cur, (K) key)) { this.table[index] = null; this.occupied--; return true; } return false; } private boolean removeFromChain(ChainedBucket bucket, K 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, K 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 += this.hashingStrategy.computeHashCode(this.nonSentinel(cur)); } } return hashCode; } private int chainedHashCode(ChainedBucket bucket) { int hashCode = 0; do { hashCode += this.hashingStrategy.computeHashCode(this.nonSentinel(bucket.zero)); if (bucket.one == null) { return hashCode; } hashCode += this.hashingStrategy.computeHashCode(this.nonSentinel(bucket.one)); if (bucket.two == null) { return hashCode; } hashCode += this.hashingStrategy.computeHashCode(this.nonSentinel(bucket.two)); if (bucket.three == null) { return hashCode; } if (bucket.three instanceof ChainedBucket) { bucket = (ChainedBucket) bucket.three; continue; } hashCode += this.hashingStrategy.computeHashCode(this.nonSentinel(bucket.three)); return hashCode; } while (true); } @Override public String toString() { return this.makeString("[", ", ", "]"); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { this.hashingStrategy = (HashingStrategy) in.readObject(); 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((K) in.readObject()); } } public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(this.hashingStrategy); 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(K key, UnifiedSetWithHashingStrategy 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, K key, UnifiedSetWithHashingStrategy 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); UnifiedSetWithHashingStrategy retainedCopy = new UnifiedSetWithHashingStrategy(this.hashingStrategy, retainedSize, this.loadFactor); for (Object key : iterable) { this.addIfFound((K) 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 UnifiedSetWithHashingStrategy clone() { return new UnifiedSetWithHashingStrategy(this.hashingStrategy, 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 < UnifiedSetWithHashingStrategy.this.size(); } public void remove() { if (!this.lastReturned) { throw new IllegalStateException("next() must be called as many times as remove()"); } this.count--; UnifiedSetWithHashingStrategy.this.occupied--; if (this.chainPosition != 0) { this.removeFromChain(); return; } int pos = this.position - 1; Object key = UnifiedSetWithHashingStrategy.this.table[pos]; if (key instanceof ChainedBucket) { this.removeLastFromChain((ChainedBucket) key, pos); return; } UnifiedSetWithHashingStrategy.this.table[pos] = null; this.position = pos; this.lastReturned = false; } protected void removeFromChain() { ChainedBucket chain = (ChainedBucket) UnifiedSetWithHashingStrategy.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) { UnifiedSetWithHashingStrategy.this.table[tableIndex] = null; } this.lastReturned = false; } protected K nextFromChain() { ChainedBucket bucket = (ChainedBucket) UnifiedSetWithHashingStrategy.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 UnifiedSetWithHashingStrategy.this.nonSentinel(cur); } public K next() { if (!this.hasNext()) { throw new NoSuchElementException("next() called, but the iterator is exhausted"); } this.count++; Object[] table = UnifiedSetWithHashingStrategy.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 UnifiedSetWithHashingStrategy.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 UnifiedSetWithHashingStrategyMultimap groupBy( Function function) { return this.groupBy(function, UnifiedSetWithHashingStrategyMultimap.newMultimap(this.hashingStrategy)); } 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 K get(K 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 (K) cur; } return null; } private K chainedGet(K 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 K put(K 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 K chainedPut(K 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 K removeFromPool(K 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 K removeFromChainForPool(ChainedBucket bucket, K 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 K removeDeepChainForPool(ChainedBucket oldBucket, K 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 K nonSentinel(Object key) { return key == NULL_KEY ? null : (K) key; } private static Object toSentinelIfNull(Object key) { if (key == null) { return NULL_KEY; } return key; } private boolean nonNullTableObjectEquals(Object cur, K key) { return cur == key || (cur == NULL_KEY ? key == null : this.hashingStrategy.equals(this.nonSentinel(cur), key)); } public MutableMap aggregateInPlaceBy( final Function groupBy, final Function0 zeroValueFactory, final Procedure2 mutatingAggregator) { final MutableMap map = UnifiedMap.newMap(); this.forEach(new Procedure() { public void value(K each) { K2 key = groupBy.valueOf(each); V value = map.getIfAbsentPut(key, zeroValueFactory); mutatingAggregator.value(value, each); } }); return map; } public MutableMap aggregateBy( final Function groupBy, final Function0 zeroValueFactory, final Function2 nonMutatingAggregator) { final MutableMap map = UnifiedMap.newMap(); this.forEach(new Procedure() { public void value(K each) { K2 key = groupBy.valueOf(each); V value = map.getIfAbsentPut(key, zeroValueFactory); map.put(key, nonMutatingAggregator.value(value, each)); } }); return map; } }