/*
* Copyright (C) 2011 The Guava Authors
*
* 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.google.common.collect;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.annotations.GwtCompatible;
import com.google.common.base.Objects;
import com.google.common.collect.Multisets.ImmutableEntry;
import com.google.common.primitives.Ints;
import com.google.errorprone.annotations.concurrent.LazyInit;
import com.google.j2objc.annotations.WeakOuter;
import java.util.Collection;
import javax.annotation.Nullable;
/**
* Implementation of {@link ImmutableMultiset} with zero or more elements.
*
* @author Jared Levy
* @author Louis Wasserman
*/
@GwtCompatible(serializable = true)
@SuppressWarnings("serial") // uses writeReplace(), not default serialization
class RegularImmutableMultiset<E> extends ImmutableMultiset<E> {
static final RegularImmutableMultiset<Object> EMPTY =
new RegularImmutableMultiset<Object>(ImmutableList.<Entry<Object>>of());
private final transient Multisets.ImmutableEntry<E>[] entries;
private final transient Multisets.ImmutableEntry<E>[] hashTable;
private final transient int size;
private final transient int hashCode;
@LazyInit
private transient ImmutableSet<E> elementSet;
RegularImmutableMultiset(Collection<? extends Entry<? extends E>> entries) {
int distinct = entries.size();
@SuppressWarnings("unchecked")
Multisets.ImmutableEntry<E>[] entryArray = new Multisets.ImmutableEntry[distinct];
if (distinct == 0) {
this.entries = entryArray;
this.hashTable = null;
this.size = 0;
this.hashCode = 0;
this.elementSet = ImmutableSet.of();
} else {
int tableSize = Hashing.closedTableSize(distinct, 1.0);
int mask = tableSize - 1;
@SuppressWarnings("unchecked")
Multisets.ImmutableEntry<E>[] hashTable = new Multisets.ImmutableEntry[tableSize];
int index = 0;
int hashCode = 0;
long size = 0;
for (Entry<? extends E> entry : entries) {
E element = checkNotNull(entry.getElement());
int count = entry.getCount();
int hash = element.hashCode();
int bucket = Hashing.smear(hash) & mask;
Multisets.ImmutableEntry<E> bucketHead = hashTable[bucket];
Multisets.ImmutableEntry<E> newEntry;
if (bucketHead == null) {
boolean canReuseEntry =
entry instanceof Multisets.ImmutableEntry && !(entry instanceof NonTerminalEntry);
newEntry =
canReuseEntry
? (Multisets.ImmutableEntry<E>) entry
: new Multisets.ImmutableEntry<E>(element, count);
} else {
newEntry = new NonTerminalEntry<E>(element, count, bucketHead);
}
hashCode += hash ^ count;
entryArray[index++] = newEntry;
hashTable[bucket] = newEntry;
size += count;
}
this.entries = entryArray;
this.hashTable = hashTable;
this.size = Ints.saturatedCast(size);
this.hashCode = hashCode;
}
}
private static final class NonTerminalEntry<E> extends Multisets.ImmutableEntry<E> {
private final Multisets.ImmutableEntry<E> nextInBucket;
NonTerminalEntry(E element, int count, ImmutableEntry<E> nextInBucket) {
super(element, count);
this.nextInBucket = nextInBucket;
}
@Override
public ImmutableEntry<E> nextInBucket() {
return nextInBucket;
}
}
@Override
boolean isPartialView() {
return false;
}
@Override
public int count(@Nullable Object element) {
Multisets.ImmutableEntry<E>[] hashTable = this.hashTable;
if (element == null || hashTable == null) {
return 0;
}
int hash = Hashing.smearedHash(element);
int mask = hashTable.length - 1;
for (Multisets.ImmutableEntry<E> entry = hashTable[hash & mask];
entry != null;
entry = entry.nextInBucket()) {
if (Objects.equal(element, entry.getElement())) {
return entry.getCount();
}
}
return 0;
}
@Override
public int size() {
return size;
}
@Override
public ImmutableSet<E> elementSet() {
ImmutableSet<E> result = elementSet;
return (result == null) ? elementSet = new ElementSet() : result;
}
@WeakOuter
private final class ElementSet extends ImmutableSet.Indexed<E> {
@Override
E get(int index) {
return entries[index].getElement();
}
@Override
public boolean contains(@Nullable Object object) {
return RegularImmutableMultiset.this.contains(object);
}
@Override
boolean isPartialView() {
return true;
}
@Override
public int size() {
return entries.length;
}
}
@Override
Entry<E> getEntry(int index) {
return entries[index];
}
@Override
public int hashCode() {
return hashCode;
}
}