org.databene.commons.collection.CompressedIntSet Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of databene-commons Show documentation
Show all versions of databene-commons Show documentation
'databene commons' is an open source Java library by Volker Bergmann.
It provides extensions to the Java core library by utility classes, abstract concepts
and concrete implementations.
/*
* Copyright (C) 2004-2014 Volker Bergmann ([email protected]).
* All rights reserved.
*
* 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 org.databene.commons.collection;
import java.util.Iterator;
import java.util.TreeMap;
import java.util.Map.Entry;
/**
* Collects int values in a compressed way.
* Created: 05.10.2010 19:17:30
* @since 0.5.4
* @author Volker Bergmann
*/
public class CompressedIntSet {
protected TreeMap numbers;
protected long size;
public CompressedIntSet() {
this.numbers = new TreeMap();
this.size = 0;
}
public void clear() {
numbers.clear();
this.size = 0;
}
public void addAll(int... numbers) {
for (int number : numbers)
add(number);
}
public void add(int i) {
if (numbers.isEmpty()) {
// if the set is empty, insert the number
insertNumber(i);
size = 1;
} else {
// search the highest entry which is less or equals to i
Entry floorEntry = numbers.floorEntry(i);
IntRange rangeBelow;
if (floorEntry == null)
extendRangeAboveOrInsertNumber(i); // no range below found, check above
else {
// check found range
rangeBelow = floorEntry.getValue();
if (rangeBelow.contains(i))
return;
if (rangeBelow.getMax() + 1 == i) {
// extend found range if applicable
rangeBelow.setMax(i);
size++;
// check if two adjacent ranges can be merged
IntRange upperNeighbor = numbers.get(i + 1);
if (upperNeighbor != null) {
numbers.remove(i + 1);
rangeBelow.setMax(upperNeighbor.getMax());
}
} else
extendRangeAboveOrInsertNumber(i);
}
}
}
private void extendRangeAboveOrInsertNumber(int i) {
IntRange rangeAbove = numbers.get(i + 1);
if (rangeAbove != null) {
numbers.remove(i + 1);
rangeAbove.setMin(i);
numbers.put(i, rangeAbove);
} else
insertNumber(i);
size++;
}
private void insertNumber(int i) {
numbers.put(i, new IntRange(i, i));
}
public boolean contains(int i) {
Entry floorEntry = numbers.floorEntry(i);
return (floorEntry != null && floorEntry.getValue().contains(i));
}
public boolean remove(int i) {
Entry floorEntry = numbers.floorEntry(i);
if (floorEntry == null || !floorEntry.getValue().contains(i))
return false;
IntRange range = floorEntry.getValue();
if (i == range.getMax() && range.getMax() > range.getMin()) {
range.setMax(i - 1);
} else if (i == range.getMin()) {
numbers.remove(i);
if (range.getMax() > i) {
range.setMin(i + 1);
numbers.put(i + 1, range);
}
} else {
int max = range.getMax();
range.setMax(i - 1);
IntRange range2 = new IntRange(i + 1, max);
numbers.put(i + 1, range2);
}
return true;
}
public boolean isEmpty() {
return numbers.isEmpty();
}
public long size() {
return size;
}
public Iterator iterator() {
return new CompressedSetIterator();
}
// java.lang.Object overrides --------------------------------------------------------------------------------------
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
CompressedIntSet that = (CompressedIntSet) obj;
return this.equals(that.numbers);
}
@Override
public int hashCode() {
return numbers.hashCode();
}
@Override
public String toString() {
return numbers.values().toString();
}
// Iterator class --------------------------------------------------------------------------------------------------
public class CompressedSetIterator implements Iterator {
protected Iterator intRangeIterator;
protected IntRange currentIntRange;
protected Integer lastInt;
protected CompressedSetIterator() {
intRangeIterator = numbers.values().iterator();
currentIntRange = null;
lastInt = null;
}
@Override
public boolean hasNext() {
if (currentIntRange == null) {
if (intRangeIterator != null && intRangeIterator.hasNext()) {
currentIntRange = intRangeIterator.next();
lastInt = null;
return true;
} else {
intRangeIterator = null;
return false;
}
}
return (lastInt == null || lastInt < currentIntRange.max);
}
@Override
public Integer next() {
if (intRangeIterator != null && currentIntRange == null) {
if (intRangeIterator.hasNext()) {
currentIntRange = intRangeIterator.next();
} else {
intRangeIterator = null;
currentIntRange = null;
}
}
if (intRangeIterator == null || currentIntRange == null)
throw new IllegalStateException("No 'next' value available. Check hasNext() before calling next().");
lastInt = (lastInt != null ? ++lastInt : currentIntRange.min);
if (lastInt == currentIntRange.max)
currentIntRange = null;
return lastInt;
}
@Override
public void remove() {
CompressedIntSet.this.remove(lastInt);
}
}
}