blob: 549ec7bfd30c5dfc3d0939cfb1e949fd7078335c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.lucene.search;
import java.util.AbstractCollection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* A {@link Multiset} is a set that allows for duplicate elements. Two
* {@link Multiset}s are equal if they contain the same unique elements and if
* each unique element has as many occurrences in both multisets.
* Iteration order is not specified.
* @lucene.internal
*/
final class Multiset<T> extends AbstractCollection<T> {
private final Map<T, Integer> map = new HashMap<>();
private int size;
/** Create an empty {@link Multiset}. */
Multiset() {
super();
}
@Override
public Iterator<T> iterator() {
final Iterator<Map.Entry<T, Integer>> mapIterator = map.entrySet().iterator();
return new Iterator<T>() {
T current;
int remaining;
@Override
public boolean hasNext() {
return remaining > 0 || mapIterator.hasNext();
}
@Override
public T next() {
if (remaining == 0) {
Map.Entry<T, Integer> next = mapIterator.next();
current = next.getKey();
remaining = next.getValue();
}
assert remaining > 0;
remaining -= 1;
return current;
}
};
}
@Override
public int size() {
return size;
}
@Override
public void clear() {
map.clear();
size = 0;
}
@Override
public boolean add(T e) {
map.put(e, map.getOrDefault(e, 0) + 1);
size += 1;
return true;
}
@Override
@SuppressWarnings("unchecked")
public boolean remove(Object o) {
final Integer count = map.get(o);
if (count == null) {
return false;
} else if (1 == count.intValue()) {
map.remove(o);
} else {
map.put((T) o, count - 1);
}
size -= 1;
return true;
}
@Override
public boolean contains(Object o) {
return map.containsKey(o);
}
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass()) {
return false;
}
Multiset<?> that = (Multiset<?>) obj;
return size == that.size // not necessary but helps escaping early
&& map.equals(that.map);
}
@Override
public int hashCode() {
return 31 * getClass().hashCode() + map.hashCode();
}
}