Подтвердить что ты не робот

Ключ Hashtable в целочисленном интервале

Я не знаю, возможно ли это, но я пытаюсь создать Hashtable, где Interval - это класс с 2 целыми/длинными значениями, начало и конец, и я хотел сделать что-то вроде этого:

Hashtable<Interval, WhateverObject> test = new Hashtable<Interval, WhateverObject>();
test.put(new Interval(100, 200), new WhateverObject());
test.get(new Interval(150, 150)) // returns the new WhateverObject i created above because 150 is betwwen 100 and 200
test.get(new Interval(250, 250)) // doesn't find the value because there is no key that contains 250 in it interval

Так что в основном я хочу, чтобы ключ между диапазоном значений в объекте Interval давал корреспондент WhateverObject. Я знаю, что мне нужно переопределить equals() и hashcode() в объекте интервала, основная проблема, я думаю, так или иначе иметь все значения от 100 до 200 (в этом конкретном примере), чтобы дать тот же хеш.

Любые идеи, если это возможно?

Спасибо

4b9b3361

Ответ 1

Не нужно изобретать колесо, используйте NavigableMap. Пример кода:

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());

Вывод:

Cry Baby
Школьное время
Есть еще автомобиль?

Ответ 2

Наивный HashTable - неправильное решение здесь. Переопределение метода equals() не делает вам ничего хорошего, потому что HashTable сначала сравнивает ключевую запись с помощью хеш-кода, а не методом equals(). Метод equals() проверяется только после того, как будет согласован хэш-код.

Легко сделать хэш-функцию на вашем объекте интервала, но гораздо сложнее сделать тот, который даст тот же хэш-код для всех возможных интервалов, которые будут в пределах другого интервала. Переопределение метода get() (например, здесь fooobar.com/questions/413479/...) для HashTable полностью отрицает преимущества HashTable, что очень быстрое время поиска. В тот момент, когда вы просматриваете каждый элемент HashTable, вы знаете, что неправильно используете HashTable.

Я бы сказал, что Использование java-карты для поиска диапазона и fooobar.com/questions/413479/... - лучшие решения, но HashTable просто не подходит для этого.

Ответ 3

Вы можете использовать IntervalTree. Здесь я сделал это раньше.

public class IntervalTree<T extends IntervalTree.Interval> {
  // My intervals.

  private final List<T> intervals;
  // My center value. All my intervals contain this center.
  private final long center;
  // My interval range.
  private final long lBound;
  private final long uBound;
  // My left tree. All intervals that end below my center.
  private final IntervalTree<T> left;
  // My right tree. All intervals that start above my center.
  private final IntervalTree<T> right;

  public IntervalTree(List<T> intervals) {
    if (intervals == null) {
      throw new NullPointerException();
    }

    // Initially, my root contains all intervals.
    this.intervals = intervals;

    // Find my center.
    center = findCenter();

    /*
     * Builds lefts out of all intervals that end below my center.
     * Builds rights out of all intervals that start above my center.
     * What remains contains all the intervals that contain my center.
     */

    // Lefts contains all intervals that end below my center point.
    final List<T> lefts = new ArrayList<T>();
    // Rights contains all intervals that start above my center point.
    final List<T> rights = new ArrayList<T>();

    long uB = Long.MIN_VALUE;
    long lB = Long.MAX_VALUE;
    for (T i : intervals) {
      long start = i.getStart();
      long end = i.getEnd();
      if (end < center) {
        lefts.add(i);
      } else if (start > center) {
        rights.add(i);
      } else {
        // One of mine.
        lB = Math.min(lB, start);
        uB = Math.max(uB, end);
      }
    }

    // Remove all those not mine.
    intervals.removeAll(lefts);
    intervals.removeAll(rights);
    uBound = uB;
    lBound = lB;

    // Build the subtrees.
    left = lefts.size() > 0 ? new IntervalTree<T>(lefts) : null;
    right = rights.size() > 0 ? new IntervalTree<T>(rights) : null;

    // Build my ascending and descending arrays.
    /** @todo Build my ascending and descending arrays. */
  }

  /*
   * Returns a list of all intervals containing the point.
   */
  List<T> query(long point) {
    // Check my range.
    if (point >= lBound) {
      if (point <= uBound) {
        // In my range but remember, there may also be contributors from left or right.
        List<T> found = new ArrayList<T>();
        // Gather all intersecting ones.
        // Could be made faster (perhaps) by holding two sorted lists by start and end.
        for (T i : intervals) {
          if (i.getStart() <= point && point <= i.getEnd()) {
            found.add(i);
          }
        }

        // Gather others.
        if (point < center && left != null) {
          found.addAll(left.query(point));
        }
        if (point > center && right != null) {
          found.addAll(right.query(point));
        }

        return found;
      } else {
        // To right.
        return right != null ? right.query(point) : Collections.<T>emptyList();
      }
    } else {
      // To left.
      return left != null ? left.query(point) : Collections.<T>emptyList();
    }

  }

  private long findCenter() {
    //return average();
    return median();
  }

  protected long median() {
    // Choose the median of all centers. Could choose just ends etc or anything.
    long[] points = new long[intervals.size()];
    int x = 0;
    for (T i : intervals) {
      // Take the mid point.
      points[x++] = (i.getStart() + i.getEnd()) / 2;
    }
    Arrays.sort(points);
    return points[points.length / 2];
  }

  /*
   * What an interval looks like.
   */
  public interface Interval {

    public long getStart();

    public long getEnd();
  }

  /*
   * A simple implemementation of an interval.
   */
  public static class SimpleInterval implements Interval {

    private final long start;
    private final long end;

    public SimpleInterval(long start, long end) {
      this.start = start;
      this.end = end;
    }

    public long getStart() {
      return start;
    }

    public long getEnd() {
      return end;
    }

    @Override
    public String toString() {
      return "{" + start + "," + end + "}";
    }
  }

}

Ответ 4

Я думаю, что реализация специализированного метода get будет намного проще.

Новый метод может быть частью класса-оболочки-оболочки.

Ключевой класс: (интервал [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;
    }
}

Класс карты:

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;
    }
}

Это просто пример и, безусловно, может быть оптимизирован, и есть несколько недостатков:

Для примера, если Intervals перекрываются, нет никакой гарантии, чтобы узнать, какой интервал будет использоваться для поиска, а интервалы не гарантируются, чтобы не перекрываться!

Ответ 5

Решение OldCurmudgeon отлично работает для меня, но очень медленно инициализирует (потребовалось 20 минут для записи 70 тыс.). Если вы знаете, что ваш входящий список элементов уже упорядочен (по возрастанию) и имеет только неперекрывающиеся интервалы, вы можете инициализировать его в миллисекундах, добавив и используя следующий конструктор:

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);
}

Ответ 6

Это зависит от вашей реализации hashCode. У вас могут быть два объекта с одинаковым значением hashCode.
Используйте eclipse для генерации метода hashCode для вашего класса (нет смысла повторно изобретать колесо

Ответ 7

Чтобы Hastable или HashMap работали, как ожидалось, это не только равный хэш-код, но и метод equals должен возвращать true. То, что вы запрашиваете, это интервал (x, y).equals(интервал (m, n)) для m, n в пределах x, y. Поскольку это должно быть верно для любого перекрывающегося живого экземпляра Interval, класс должен записать все из них и должен реализовать то, что вы пытаетесь достичь, действительно.

Короче говоря, нет.

Google guava-библиотека планирует предлагать RangeSet и Map: guava RangeSet

Для разумных небольших диапазонов простой подход состоял бы в том, чтобы специализировать HashMap, помещая и получая отдельные значения интервалов.