Data structures that can map a range of keys to a value
Are your ranges non-overlapping? If so you could use a TreeMap:
TreeMap<Double, Character> m = new TreeMap<Double, Character>();
m.put(1.0, 'A');
m.put(2.9, null);
m.put(4.0, 'B');
m.put(6.0, null);
m.put(6.5, 'C');
m.put(10.0, null);
The lookup logic is a bit complicated by the fact that you probably want an inclusive lookup (i.e. 2.9 maps to 'A', and not undefined):
private static <K, V> V mappedValue(TreeMap<K, V> map, K key) {
Entry<K, V> e = map.floorEntry(key);
if (e != null && e.getValue() == null) {
e = map.lowerEntry(key);
}
return e == null ? null : e.getValue();
}
Example:
mappedValue(m, 5) == 'B'
More results include:
0.9 null
1.0 A
1.1 A
2.8 A
2.9 A
3.0 null
6.4 null
6.5 C
6.6 C
9.9 C
10.0 C
10.1 null
Guava RangeMap provides specialized solution out of the box:
RangeMap<Integer, String> rangeMap = TreeRangeMap.create();
rangeMap.put(Range.closed(1, 100), "foo"); // {[1, 100] => "foo"}
rangeMap.put(Range.open(3, 6), "bar"); // {[1, 3] => "foo", (3, 6) => "bar", [6, 100] => "foo"}
rangeMap.get(42); // returns "foo"
A HashMap
will not work for mapping ranges to values unless you find a way to generate a hashcode for ranges and single values in there that matches. But below approach could be what you are looking for
public class RangeMap {
static class RangeEntry {
private final double lower;
private final double upper;
private final Object value;
public RangeEntry(double lower, double upper, Object mappedValue) {
this.lower = lower;
this.upper = upper;
this.value = mappedValue;
}
public boolean matches(double value) {
return value >= lower && value <= upper;
}
public Object getValue() { return value; }
}
private final List<RangeEntry> entries = new ArrayList<RangeEntry>();
public void put(double lower, double upper, Object mappedValue) {
entries.add(new RangeEntry(lower, upper, mappedValue));
}
public Object getValueFor(double key) {
for (RangeEntry entry : entries) {
if (entry.matches(key))
return entry.getValue();
}
return null;
}
}
You could do
RangeMap map = new RangeMap();
map.put(1, 2.9, "A");
map.put(4, 6, "B");
map.getValueFor(1.5); // = "A"
map.getValueFor(3.5); // = null
It's not very efficient since it's just iterating over a list and it will in that state not complain if you put conflicting ranges in there. Will just return the first it finds.
P.S.: mapping like this would be mapping a range of keys to a value