【问题标题】:Debugging Challenge in regards to TreeSet关于 TreeSet 的调试挑战
【发布时间】:2014-10-18 06:45:04
【问题描述】:

所以这将是我的问题,但实际上我在写它的时候就发现了问题。也许这对其他人有用(如果问题重复或被认为不适合本网站,我将删除该问题)。我知道我的问题有两种可能的解决方案,但也许有人会想出一个比我想象的更好的解决方案。

我不明白为什么TreeSet 没有在这里删除第一个元素。我的TreeSet 的大小应该保持有界,但似乎会无限增长。

这是我认为相关的代码:

此代码位于双 for 循环内。 NUM_GROUPs 是一个 static final int,它被设置为 100newGroups 是一个 TreeSet<TeamGroup> 对象,在双 for 循环之前初始化(没有元素)(变量 groupteam 来自两个 for-each 循环)。

final TeamGroup newGroup = new TeamGroup(group, team);
newGroups.add(newGroup);
System.err.println("size of newGroups: " + newGroups.size());
if (newGroups.size() > NUM_GROUPS) {
    System.err.println("removing first from newGroups");
    newGroups.remove(newGroups.first());
    System.err.println("new size of newGroups: "
        + newGroups.size());
}

我包含了我的调试语句,以表明问题确实出现了。我得到以下类型的输出:

size of newGroups: 44011
removing first from newGroups
new size of newGroups: 44011

您会看到,虽然if 语句显然已输入,但TreeSet<TeamGroup> teamGroups 的大小并没有减少。在我看来,发生这种情况的唯一方法是,如果 remove 调用没有删除任何内容 - 但它如何从对 first() 的调用中删除一些内容? 肯定TreeSet中的一个元素?

这是我的TeamGroup 类中的compareTo 方法(score 是一个int,对于许多不同的TeamGroup 对象可能是相同的,因此我使用R_ID 字段作为决胜局):

public int compareTo(TeamGroup o) {
    // sorts low to high so that when you pop off of the TreeSet object, the
    // lowest value gets popped off (and keeps the highest values).
    if (o.score == this.score)
        return this.R_ID - o.R_ID;
    return this.score - o.score;
}

这是我的TeamGroup 类的equals 方法:

@Override
public boolean equals(final Object o) {
    return this.R_ID == ((TeamGroup) o).R_ID;
}

...我不担心这里的ClassCastException,因为这与我的上述问题特别相关,我从不尝试将TeamGroup 对象与另一个TeamGroup 对象进行比较——这是绝对不是问题(至少不是ClassCastException问题)。

R_ID 应该是唯一的,我通过以下方式保证这一点:

private static final double WIDTH = (double) Integer.MAX_VALUE
        - (double) Integer.MIN_VALUE;
private static final Map<Integer, Integer> MAPPED_IDS = 
    new HashMap<Integer, Integer>(50000);
...
public final int R_ID = TeamGroup.getNewID();
...
private static int getNewID() {
    int randID = randID();
    while (MAPPED_IDS.get(randID) != null) {
        randID = randID();
    }

    MAPPED_IDS.put(randID, randID);

    return randID;
}

private static int randID() {
    return (int) (Integer.MIN_VALUE + Math.random() * WIDTH);
}

【问题讨论】:

  • 这是实施compareTo 的一个众所周知的问题......但我找不到合适的问答作为最终副本。 (如果其他人可以,请这样做。)
  • @StephenC 你提到的众所周知的问题是什么?就像我说的,我知道问题出在哪里,它compareTo 方法中,但它与compareTo 方法本身没有任何关系。真正的问题在于both compareTo 方法我正在使用排序集这一事实。
  • 没有。问题是您的compareTo 实施不正确。众所周知,那样实现它是不正确的。
  • @StephenC 好吧,我显然不同意你的观点(compareTo 方法 is 绝对错误地实现了),但是为什么这会导致我遇到的问题(我意识到你的回答触及了原因)。

标签: java twos-complement


【解决方案1】:

问题出在这里:

        return this.R_ID - o.R_ID;

应该是:

        return Integer.compare(this.R_ID, o.R_ID);

如果两个值都保证为非负数,则取两个 intInteger 值的差值是可行的。但是,在您的示例中,您在整个int / Integer 范围内使用 ID 值,这意味着减法可能导致溢出......以及compareTo 的错误结果。

不正确的实现会导致compareTo 方法不是自反的;即整数I1I2I3,其中compareTo 方法表示I1 &lt; I2I2 &lt; I3,还有I3 &lt; I1。当您将其插入TreeSet 时,元素会被插入到错误位置的树中,并且会发生奇怪的行为。究竟发生了什么很难预测 - 这将取决于插入的对象,以及它们插入的顺序

TreeSet.first() 肯定会返回一个属于集合的对象,对吧?

大概……

那为什么不能移除这个对象呢?

可能是因为它找不到它……因为 compareTo 坏了。

要了解到底发生了什么,您需要单步执行 TreeSet 代码等。

【讨论】:

  • 这就是我想到的解决方案之一(尽管我想自己将其与if-else 声明进行比较......但你的方式更干净)。很高兴解释为什么我的(原始)方法不能正常工作。
  • 你肯定发现了这个错误,但是为什么这个错误会导致这个行为呢?这实际上是我无法解释的事情。 TreeSet.first() 绝对应该返回一个属于该集合的对象,对吧?那么为什么它不能删除这个对象呢? (我怀疑这与TreeSet 对象搜索元素的方式有关,但我不确定)。
  • 感谢您提供更多详细信息。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多