مفتاح Hashtable ضمن فاصل زمني صحيح

لا أعرف ما إذا كان هذا ممكنًا ولكني أحاول إنشاء Hashtable حيث يكون Interval عبارة عن فئة بها قيمتين صحيحتين/طويلتين ، بداية ونهاية وأردت تقديم شيء كالتالي:

Hashtable test = new Hashtable();
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's interval

حتى ما أريده هو أن المفتاح بين مجموعة من القيم في كائن الفاصل الزمني يعطي المراسل WhateverObject. وأنا أعلم أنني يجب أن يتجاوز equals() و hashcode() في كائن الفاصل الزمني ، المشكلة الرئيسية أعتقد أن بطريقة ما يكون كل القيم بين 100 و 200 (في هذا المثال المحدد) لإعطاء نفس التجزئة.

اي ايدى اذا كان هذا ممكن؟

شكر

11
لذا ، فالمفتاح Hashtable هو عدد صحيح أو رقمان صحيحان يمثلان فترة زمنية؟
وأضاف المؤلف Tudor, مصدر
وأضاف المؤلف Jigar Joshi, مصدر
أنا في الأساس أحاول القيام بنظام ترجمة حيث سيكون النطاق بداية ونهاية كل نقطة جديلة ، لذلك أريد أن تحصل القيم الوسطى على نفس الكائن الفرعي. الفواصل لن تتغير ولكن لديهم ثغرات بين الترجمات المصاحبة.
وأضاف المؤلف xlar8or, مصدر
نعم ، سوف تتطابق بداية ونهاية الفاصل الزمني مع بداية ونهاية نقطة الإشارة في العنوان الفرعي. لا يوجد تصادم فواصل ، فقط ثقوب بينها.
وأضاف المؤلف xlar8or, مصدر
أعلم أنه سؤال قديم ، ولكن لا يزال يتم العثور عليه كثيرًا على Google عند البحث عن IntervalTree ، يمكنك إلقاء نظرة على github.com/Breinify/brein-time-utilities ، التي من المفترض أن تغطي احتياجاتك ، كما أنها تتيح لك فرصة استمرار/تخزين/تخزين بيانات الفاصل الزمني.
وأضاف المؤلف Philipp, مصدر
هل ستكون الفواصل الزمنية نفسها دائمًا في خريطة واحدة؟
وأضاف المؤلف AlexS, مصدر

7 إجابة

14
وأضاف
aaronburro ستعمل حتى ذلك الحين ، طالما قمت بإدراج قيم فارغة "الثقوب" (وإضافة المزيد من رمز العميل)
وأضاف المؤلف Sean Patrick Floyd, مصدر
نعم. سيعمل هذا ، طالما لا توجد "ثقوب" في الفواصل الزمنية.
وأضاف المؤلف aaronburro, مصدر

السذاجة HashTable هو الحل الخطأ هنا. لا يؤدي تجاوز طريقة equals() إلى أي نجاح لأن HashTable يقارن إدخال مفتاح بواسطة رمز التجزئة أولاً ، وليس بطريقة equals (). يتم تحديد طريقة equals() فقط بعد مطابقة شفرة التجزئة.

من السهل إنشاء دالة هاش على كائن الفاصل الزمني الخاص بك ، ولكن من الصعب جدًا إنشاء دالة يمكنها الحصول على نفس hashcode لكافة الفواصل الزمنية المحتملة التي ستكون ضمن فاصل زمني آخر. تجاوز طريقة get() مثل https://stackoverflow.com/a/11189075/1261844 ) للحصول على HashTable ينفي بشكل كامل مزايا HashTable ، وهو مرات البحث سريع جداً. عند النقطة التي يتم فيها فحص كل عضو في HashTable ، فإنك تعرف أنك تستخدم HashTable بشكل غير صحيح.

أود أن أقول إن استخدام خريطة جافا لعمليات البحث على النطاق و https://stackoverflow.com/a/11189080/1261844 هي حلول أفضل ، ولكن HashTable هو ببساطة ليس طريقة للذهاب حول هذا الموضوع.

3
وأضاف

يمكنك استخدام IntervalTree . هذا واحد قدمته في وقت سابق.

public class IntervalTree {
 //My intervals.

  private final List 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 left;
 //My right tree. All intervals that start above my center.
  private final IntervalTree right;

  public IntervalTree(List 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 lefts = new ArrayList();
   //Rights contains all intervals that start above my center point.
    final List rights = new ArrayList();

    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(lefts) : null;
    right = rights.size() > 0 ? new IntervalTree(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 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 found = new ArrayList();
       //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.emptyList();
      }
    } else {
     //To left.
      return left != null ? left.query(point) : Collections.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 + "}";
    }
  }

}
2
وأضاف
شكرا جزيلا. ومع ذلك انها بطيئة بشكل لا يصدق في التهيئة. استغرق 20 دقيقة بالنسبة لي لبناء شجرة من إدخالات 70K تقريبا. إذا كنت تعرف أن قائمتك مرتبة وغير متداخلة ، فيمكنك جعلها أسرع. لقد أضفت إجابة أدناه مع مُنشئ إضافي أعتقد أنه يجب عليك إضافته إلى شفرتك. ثم يتم تشغيله بالمللي ثانية بدلاً من ذلك.
وأضاف المؤلف user467257, مصدر

أعتقد أن تنفيذ طريقة الحصول المتخصصة سيكون أسهل بكثير.

يمكن أن تكون الطريقة الجديدة جزءًا من فئة مجمدة خريطة.

فئة المفتاح: (الفاصل الزمني هو [السفلي ؛ العلوي [)

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 extends HashMap {

    public T get(int key) {
        for (Interval iv : keySet()) {
            if (iv.contains(key)) {
                return super.get(iv);
            }
        }
        return null;
    }
}

هذا هو مجرد مثال ويمكن بالتأكيد أن يكون الأمثل ، وهناك بعض العيوب أيضا:

على سبيل المثال ، إذا تداخلت الفترات الزمنية ، لا يوجد ضمان ليعرف الفاصل الزمني الذي سيتم استخدامه للبحث ولا يكون الفترات مضمنة لعدم التداخل!

1
وأضاف
هل تستطيع أن تعطيني مثالا؟
وأضاف المؤلف xlar8or, مصدر
هذا مشابه لما كان يدور في ذهني. لم أفهم طريقة hashCode الخاصة بك. لكي يعمل هذا النوع من التنفيذ ، يجب على hashCode أن يعطي نفس التشويش بين كل قيمة للفاصل الزمني ، ولكن يختلف عن الفواصل الزمنية الأخرى للحصول على الحد الأقصى للانتشار في الهاشتابل.
وأضاف المؤلف xlar8or, مصدر
إذن ما الفرق بين ذلك وبين وجود الفاصل الزمني داخل العنوان الفرعي وجعل قائمة <�عنوان فرعي> وتكرار كل عنصر من القائمة للتحقق مما إذا كان في الفاصل الزمني؟
وأضاف المؤلف xlar8or, مصدر
هذا هو مجرد طريقة hashcode- عادية. في هذا المثال ، يمكنك تكرار كل الفواصل الزمنية والتحقق من وجود عدد صحيح معين بداخله.
وأضاف المؤلف AlexS, مصدر
في الأساس لا شيء.
وأضاف المؤلف AlexS, مصدر

يعمل حل OldCurmudgeon بشكل مثالي بالنسبة لي ، ولكنه بطيء جدًا في التهيئة (استغرق 20 دقيقة لمدخلات 70 ألفًا). إذا كنت تعلم أن القائمة الواردة من العناصر مرتبة بالفعل (تصاعديًا) ولديها فترات زمنية غير متداخلة فقط ، فيمكنك جعلها تبدأ بالمللي ثانية عن طريق إضافة واستخدام منشئ البيانات التالي:

public IntervalTree(List intervals, boolean constructorFlagToIndicateOrderedNonOverlappingIntervals) {
    if (intervals == null) throw new NullPointerException();

    int centerPoint = intervals.size()/2;
    T centerInterval = intervals.get(centerPoint);
    this.intervals = new ArrayList();
    this.intervals.add(centerInterval);
    this.uBound = centerInterval.getEnd();
    this.lBound = centerInterval.getStart();
    this.center = (this.uBound + this.lBound)/2;
    List toTheLeft = centerPoint < 1 ? Collections.emptyList() : intervals.subList(0, centerPoint);
    this.left = toTheLeft.isEmpty() ? null : new IntervalTree(toTheLeft, true);
    List toTheRight = centerPoint >= intervals.size() ? Collections.emptyList() : intervals.subList(centerPoint+1, intervals.size());
    this.right = toTheRight.isEmpty() ? null : new IntervalTree(toTheRight, true);
}
1
وأضاف

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
وأضاف
سينشئ Eclipse hashCode ولا يبدأ حتى في حل مشكلة OP. في جميع الاحتمالات لا يمكن حلها نظريًا من خلال خريطة التجزئة.
وأضاف المؤلف Marko Topolnik, مصدر

بالنسبة لـ Hastable أو HashMap للعمل كما هو متوقع ، فهو ليس فقط رمز hashcode مساويًا ، ولكن يجب أيضًا إرجاع طريقة equals. ما تطلبه هو أن Interval (x، y) .equals (Interval (m، n)) لـ m، n ضمن x، y. وحيث أن هذا يجب أن يكون صحيحًا لأي مثيل حي للمراحل الفاصلة المتداخلة ، يجب أن يسجل الفصل جميعهم ويحتاج إلى تنفيذ ما تحاول تحقيقه بالفعل.

باختصار ، الجواب هو لا.

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

بالنسبة إلى النطاقات الصغيرة المعقولة ، يتمثل أسلوب سهل في تخصيص HashMap بوضع القيم الفردية للفواصل الزمنية والحصول عليها.

0
وأضاف