Hashtable key within integer interval

后端 未结 7 1847
北恋
北恋 2020-11-30 11:17

I don\'t know if this is possible but i\'m trying to make an Hashtable of where Interval is a class with 2 integer / long values, a start and an end and i wanted to make so

相关标签:
7条回答
  • 2020-11-30 12:01

    No need to reinvent the wheel, use a NavigableMap. Example Code:

    final NavigableMap<Integer, String> map = new TreeMap<Integer, String>();
    map.put(0, "Cry Baby");
    map.put(6, "School Time");
    map.put(16, "Got a car yet?");
    map.put(21, "Tequila anyone?");
    map.put(45, "Time to buy a corvette");
    
    System.out.println(map.floorEntry(3).getValue());
    System.out.println(map.floorEntry(10).getValue());
    System.out.println(map.floorEntry(18).getValue());
    

    Output:

    Cry Baby
    School Time
    Got a car yet?

    0 讨论(0)
  • 2020-11-30 12:01

    For Hastable or HashMap to work as expected it's not only a equal hashcode, but also the equals method must return true. What you are requesting is that Interval(x, y).equals(Interval(m, n)) for m, n within x,y. As this must be true for any overlapping living instance of Interval, the class has to record all of them and needs to implement what you are trying to achieve, indeed.

    So in short the answer is no.

    The Google guava library is planning to offer a RangeSet and Map: guava RangeSet

    For reasonable small ranges an easy approach would be to specialize HashMap by putting and getting the indivual values of the intervals.

    0 讨论(0)
  • 2020-11-30 12:03

    I think implementing a specialized get-method would be much easier.

    The new method can be part of a map-wrapper-class.

    The key-class: (interval is [lower;upper[ )

    public class Interval {
        private int upper;
        private int lower;
    
        public Interval(int upper, int lower) {
            this.upper = upper;
            this.lower = lower;
        }
    
        public boolean contains(int i) {
            return i < upper && i >= lower;
        }
    
        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final Interval other = (Interval) obj;
            if (this.upper != other.upper) {
                return false;
            }
            if (this.lower != other.lower) {
                return false;
            }
            return true;
        }
    
        @Override
        public int hashCode() {
            int hash = 5;
            hash = 61 * hash + this.upper;
            hash = 61 * hash + this.lower;
            return hash;
        }
    }
    

    The Map-class:

    public class IntervalMap<T> extends HashMap<Interval, T> {
    
        public T get(int key) {
            for (Interval iv : keySet()) {
                if (iv.contains(key)) {
                    return super.get(iv);
                }
            }
            return null;
        }
    }
    

    This is just an example and can surely be optimized, and there are a few flaws as well:

    For Example if Intervals overlap, there's no garantee to know which Interval will be used for lookup and Intervals are not garanteed to not overlap!

    0 讨论(0)
  • 2020-11-30 12:07

    This depends on your hashCode implementation. You may have two Objects with the same hashCode value.
    Please use eclipse to generate a hashCode method for your class (no point to re-invent the wheel

    0 讨论(0)
  • 2020-11-30 12:10

    OldCurmudgeon's solution works perfectly for me, but is very slow to initialise (took 20 mins for 70k entries). If you know your incoming list of items is already ordered (ascending) and has only non overlapping intervals, you can make it initialise in milliseconds by adding and using the following constructor:

    public IntervalTree(List<T> intervals, boolean constructorFlagToIndicateOrderedNonOverlappingIntervals) {
        if (intervals == null) throw new NullPointerException();
    
        int centerPoint = intervals.size() / 2;
        T centerInterval = intervals.get(centerPoint);
        this.intervals = new ArrayList<T>();
        this.intervals.add(centerInterval);
        this.uBound = centerInterval.getEnd();
        this.lBound = centerInterval.getStart();
        this.center = (this.uBound + this.lBound) / 2;
        List<T> toTheLeft = centerPoint < 1 ? Collections.<T>emptyList() : intervals.subList(0, centerPoint);
        this.left = toTheLeft.isEmpty() ? null : new IntervalTree<T>(toTheLeft, true);
        List<T> toTheRight = centerPoint >= intervals.size() ? Collections.<T>emptyList() : intervals.subList(centerPoint+1, intervals.size());
        this.right = toTheRight.isEmpty() ? null : new IntervalTree<T>(toTheRight, true);
    }
    
    0 讨论(0)
  • 2020-11-30 12:14

    A naive HashTable is the wrong solution here. Overriding the equals() method doesn't do you any good because the HashTable compares a key entry by the hash code first, NOT the equals() method. The equals() method is only checked AFTER the hash code is matched.

    It's easy to make a hash function on your interval object, but it's much more difficult to make one that would yield the same hashcode for all possible intervals that would be within another interval. Overriding the get() method (such as here https://stackoverflow.com/a/11189075/1261844) for a HashTable completely negates the advantages of a HashTable, which is very fast lookup times. At the point where you are scanning through each member of a HashTable, then you know you are using the HashTable incorrectly.

    I'd say that Using java map for range searches and https://stackoverflow.com/a/11189080/1261844 are better solutions, but a HashTable is simply not the way to go about this.

    0 讨论(0)
提交回复
热议问题