RangedMap.java
package handist.collections;
import java.io.Serializable;
import java.util.Collection;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
/**
*
*
* TODO : handle not for LongRange but for object range.
*
* @author yoshikikawanishi
*/
public class RangedMap<T> extends ParallelMap<Long, T> implements Serializable {
private static final long serialVersionUID = -977394469515519577L;
protected final transient ConcurrentNavigableMap<Long, T> data;
protected final transient LongRangeSet ranges;
/**
* Construct a RangedMap with given arguments.
*/
public RangedMap() {
this(new ConcurrentSkipListMap<>(), new LongRangeSet());
}
/**
* Construct a RangedMap with given arguments.
*/
RangedMap(ConcurrentNavigableMap<Long, T> data, Collection<LongRange> ranges) {
super(data);
this.data = (ConcurrentNavigableMap<Long, T>) super.data;
this.ranges = new LongRangeSet(ranges);
}
/**
* Add a range to this instance. You can {@link #put} element within the range
* added by this method.
*
* @param range a range to add to this instance.
*/
public void addRange(LongRange range) {
ranges.add(range);
}
/**
* Add a range to this instance. You can {@link #put} element within the range
* added by this method.
*
* @param range a range to add to this instance.
* @param initializer generates the initial value of the element for each index.
*/
public void addRange(LongRange range, Function<Long, T> initializer) {
ranges.add(range);
for (final long i : range) {
put(i, initializer.apply(i));
}
}
/**
* Remove all ranges and the elements set there.
*/
@Override
public void clear() {
super.clear();
ranges.clear();
}
@Override
public Object clone() {
final RangedMap<T> clone = new RangedMap<>();
for (final LongRange r : ranges) {
clone.addRange(r);
}
for (final Entry<Long, T> e : entrySet()) {
clone.put(e.getKey(), e.getValue());
}
return clone;
}
public boolean containsRange(LongRange range) {
return ranges.contains(range);
}
public int count() {
return data.size();
}
public void forEach(LongRange range, BiConsumer<Long, T> func) {
data.subMap(range.from, range.to).forEach((Long i, T t) -> {
func.accept(i, t);
});
}
public void forEach(LongRange range, Consumer<T> func) {
data.subMap(range.from, range.to).forEach((Long i, T t) -> {
func.accept(t);
});
}
@Override
public T put(Long key, T value) {
if (!ranges.containsIndex(key)) {
throw new IndexOutOfBoundsException("RangedMap: index " + key + " is not included ");
}
return super.put(key, value);
}
@Override
public void putAll(java.util.Map<? extends Long, ? extends T> m) {
m.forEach((key, value) -> {
this.put(key, value);
});
}
public Collection<LongRange> ranges() {
return ranges;
}
public Map<Long, T> remove(LongRange range) {
if (ranges.remove(range)) {
final TreeMap<Long, T> mapToRet = new TreeMap<>();
data.subMap(range.from, range.to).forEach((Long i, T t) -> {
data.remove(i);
mapToRet.put(i, t);
});
return mapToRet;
}
return null;
}
protected boolean removeRangeTemporary(LongRange range) {
return ranges.remove(range);
}
@Override
public int size() {
return (int) ranges.totalSize();
}
/**
* Splits this instance by two points of a given range and returns a subMap of
* the given range.
*
* @param range to split
* @return subMap within a given range.
*/
public RangedMap<T> split(LongRange range) {
final LongRange low = ranges.getOverlap(range.from);
if (low != null && low.from != range.from && low.to != range.from) {
removeRangeTemporary(low);
addRange(new LongRange(low.from, range.from));
addRange(new LongRange(range.from, low.to));
}
final LongRange high = ranges.getOverlap(range.to);
if (high != null && high.from != range.to && high.to != range.to) {
removeRangeTemporary(high);
addRange(new LongRange(high.from, range.to));
addRange(new LongRange(range.to, high.to));
}
return new RangedMap<>(data.subMap(range.from, range.to),
ranges.subSet(new LongRange(range.from), new LongRange(range.to)));
}
@Override
public String toString() {
// TODO
return data.toString();
}
}