Data structures that can map a range of keys to a value

后端 未结 7 1923
死守一世寂寞
死守一世寂寞 2020-11-27 18:02

I am trying to find a data structure that takes in a particular value from a range of values and map it to a key.

For example, I have the following conditions:

相关标签:
7条回答
  • 2020-11-27 18:30

    just have an List as a value in your Map.

    Map<String, List<Double>> map = new HashMap<String, List<Double>>();
    

    and also one Suggustion:

    do not use Hashtable unless you want synchronized access.

    EDIT:

    Hashtable methods are synchronized, i.e., no two threads can access those methods at a single point of time. HashMap menthods are not Synchronized. If you use Hashtable there will be a performance hit. use HashMap for better performance.

    0 讨论(0)
  • 2020-11-27 18:33

    This type of data structure is called an Interval Tree. (The Wikipedia page only presents the case where intervals may overlap, but one can imagine a case where you want to remove mappings for any overlapped intervals when you add a new interval. Do a Google search for implementations and see if any fit your needs.

    0 讨论(0)
  • 2020-11-27 18:40

    This seems like a natural situation to use a tree structure.

    Unfortunately it won't be practical to implement the java.util.Map interface because it specifies a method to return all of the keys, and in your situation you theoretically have an impractically large number of keys.

    Each node of your tree should have a minimum key, a maximum key, and a value associated with that range. You can then have links to the nodes representing the next higher and next lower range (if they exist). Something like:

    public class RangeMap<K extends Comparable<K>, V> {
        protected boolean empty;
        protected K lower, upper;
        protected V value;
        protected RangeMap<K, V> left, right;
    
        public V get(K key) {
            if (empty) {
                return null;
            }
    
            if (key.compareTo(lower) < 0) {
                return left.get(key);
            }
    
            if (key.compareTo(upper) > 0) {
                return right.get(key);
            }
    
            /* if we get here it is in the range */
            return value;
        }
    
        public void put(K from, K to, V val) {
            if (empty) {
                lower = from;
                upper = to;
                value = val;
                empty = false;
                left = new RangeMap<K,V>();
                right = new RangeMap<K,V>();
                return;
            }
    
            if (from.compareTo(lower) < 0) {
                left.put(from, to, val);
                return;
            }
    
            if (to.compareTo(upper) > 0) {
                right.put(from, to, val);
                return;
            }
    
            /* here you'd have to put the code to deal with adding an overlapping range,
               however you want to handle that. */
        }
    
        public RangeMap() {
            empty = true;
        }
    }
    

    If you need faster lookups than the tree can provide, you may want to look into something like a skip list or developing your own hash function.

    0 讨论(0)
  • 2020-11-27 18:42

    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
    
    0 讨论(0)
  • 2020-11-27 18:42

    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

    0 讨论(0)
  • 2020-11-27 18:47

    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"
    
    0 讨论(0)
提交回复
热议问题