TreeSet的CompareTo方法未能产生期望的结果

huangapple go评论81阅读模式
英文:

TreeSet CompareTo not giving desirable result

问题

我正在尝试创建一个包含字典中所有单词中所有字母的集合。

为此,我使用了TreeSet,因为我需要执行大量的比较操作。

以下是我的代码类:

public class main {

    public static void main(String[] args) {

        Set<String> lines = new TreeSet<>();
        lines.add("ba");
        DictAwareSolver myGuesser = new DictAwareSolver(lines);
        myGuesser.makeGuess();
    }
}

这是我在集合上操作的类:

package solver;

import sun.reflect.generics.tree.Tree;

import java.util.*;
import java.lang.System;

public class DictAwareSolver extends HangmanSolver
{

    private Set<String> dict;

    TreeSet<Node> myTree = new TreeSet<>();

    // 获取器方法

    public Set<String> getDict() {
        return dict;
    }

    // 方法

    public DictAwareSolver(Set<String> dictionary) {
        this.dict = dictionary;
        // 在此实现!
    } 

    @Override
    public void newGame(int[] wordLengths, int maxIncorrectGuesses)
    {
        // 在此实现!
    } 

    @Override
    public char makeGuess() {
        Set<String> guessDict = getDict();
        Iterator dictItr = guessDict.iterator();

        while (dictItr.hasNext())
        {
            String word = (String) dictItr.next();
            for (int i = 0; i<word.length(); i++)
            {
                Node temp = new Node(word.charAt(i));
                myTree.add(temp);
            }
        }

        Iterator treeItr = myTree.iterator();

        while (treeItr.hasNext())
        {
            Node n = (Node) treeItr.next();
            System.out.println(n.getLetter() + "->"+n.getFrequency());
        }
        return '\0';
    } 

    @Override
    public void guessFeedback(char c, Boolean bGuess, ArrayList< ArrayList<Integer> > lPositions)
    {
        // 在此实现!
    } 

}

class Node implements Comparable<Node>{
    private char letter;
    private int frequency;

    public Node(char letter)
    {
        this.letter = letter;
        this.frequency = 1;
    }

    public void countIncrementer()
    {
        int newCount = getFrequency()+1;
        setFrequency(newCount);
    }

    // 获取器方法

    public char getLetter() {
        return letter;
    }

    public int getFrequency() {
        return frequency;
    }

    // 设置器方法

    public void setFrequency(int frequency) {
        this.frequency = frequency;
    }

    @Override
    public int compareTo(Node o) {
        if (getLetter() == o.letter)
        {
            o.countIncrementer();
            return 0;
        }
        else if (getLetter() > o.getLetter())
        {
            return 1;
        }
        else
        {
            return -1;
        }
    }
}

当我运行这个代码时,无论我添加什么,第一个字母的计数总是为2。例如,在此情况下的输出为:

a->1
b->2

但我期望的是:

a->1
b->1

如果您能指出问题所在,那将非常有帮助。根据我所想,问题应该出现在我的o.countIncrementer();语句,位于compareTo方法中。我刚开始学习Java。

英文:

I am trying to create a set of all letters in all the words in a dictionary.

I am using a TreeSet for that as I have to do lot's of compare operations.


public class main {
public static void main(String[] args) {
Set&lt;String&gt; lines = new TreeSet&lt;&gt;();
lines.add(&quot;ba&quot;);
DictAwareSolver myGuesser = new DictAwareSolver(lines);
myGuesser.makeGuess();
}
}

This is my class which is operating on the set

package solver;
import sun.reflect.generics.tree.Tree;
import java.util.*;
import java.lang.System;
public class DictAwareSolver extends HangmanSolver
{
private Set&lt;String&gt; dict;
TreeSet&lt;Node&gt; myTree = new TreeSet&lt;&gt;();
//getters
public Set&lt;String&gt; getDict() {
return dict;
}
// methods
public DictAwareSolver(Set&lt;String&gt; dictionary) {
this.dict = dictionary;
// Implement me!
} // end of DictAwareSolver()
@Override
public void newGame(int[] wordLengths, int maxIncorrectGuesses)
{
// Implement me!
} // end of newGame()
@Override
public char makeGuess() {
Set&lt;String&gt; guessDict = getDict();
Iterator dictItr = guessDict.iterator();
while (dictItr.hasNext())
{
String word = (String) dictItr.next();
for (int i = 0; i&lt;word.length(); i++)
{
Node temp = new Node(word.charAt(i));
myTree.add(temp);
}
}
Iterator treeItr = myTree.iterator();
while (treeItr.hasNext())
{
Node n = (Node) treeItr.next();
System.out.println(n.getLetter() + &quot;--&gt;&quot;+n.getFrequency());
}
// TODO: This is a placeholder, replace with appropriate return value.
return &#39;\0&#39;;
} // end of makeGuess()
@Override
public void guessFeedback(char c, Boolean bGuess, ArrayList&lt; ArrayList&lt;Integer&gt; &gt; lPositions)
{
// Implement me!
} // end of guessFeedback()
} // end of class DictAwareSolver
class Node implements Comparable&lt;Node&gt;{
private char letter;
private int frequency;
public Node(char letter)
{
this.letter = letter;
this.frequency = 1;
}
public void countIncrementer()
{
int newCount = getFrequency()+1;
setFrequency(newCount);
}
// getters
public char getLetter() {
return letter;
}
public int getFrequency() {
return frequency;
}
// setters
public void setFrequency(int frequency) {
this.frequency = frequency;
}
@Override
public int compareTo(Node o) {
if (getLetter() == o.letter)
{
o.countIncrementer();
return 0;
}
else if (getLetter() &gt; o.getLetter())
{
return 1;
}
else
{
return -1;
}
}
}

When I am running this whatever I am adding 1st is giving a count of 2. As in this case output is

a-->1
b-->2

but I am expecting

a-->1
b-->1

It will be really helpful if you can point out what is the problem. From what I can think of it should be something in my o.countIncrementer(); in my compareTo method. I am new to java.

答案1

得分: 1

这段代码假设 TreeSet 只会在集合中已存在相等元素时才调用比较器,并且如果进行了此类比较,它只会执行一次。然而,这并不是 TreeSet 的实现方式。查看 API 文档 可以发现,关于 TreeSet 如何进行比较以及比较的频率并没有任何保证。由于这不是 API 文档中记录的一部分,TreeSet 的作者可以自由地以任何合理的方式来实现这个功能,只要符合文档中的 API 规定。实际上,他们还可以在不同版本之间(例如 Java 6 和 Java 7)或不同实现之间(例如 Oracle vs. IBM)更改实现方式。

简而言之,如果文档没有保证某种行为,你的代码就不应该依赖于该行为。

具体到你所看到的行为,首次添加到 TreeSet 中的元素(在你使用的 Java 版本中)会与其自身进行比较。虽然这可能令人惊讶,但这在 API 中并没有被禁止。这可能有也可能没有一个合理的原因(我相信在 Java 7 中添加了这个检查,是为了在将 null 作为第一个元素添加到不允许包含 null 的 TreeSet 时引发 NullPointerException,根据此 bug)。然而,最终这个检查的原因对于 API 的用户来说并不重要,因为它在 API 中并没有被禁止。

以下是你提供的代码部分:

public static void main(String[] args) {
    System.out.printf("Java vendor & version: %s %s\n", System.getProperty("java.vendor"), Runtime.version());

    TreeSet<Character> set = new TreeSet<>(new LoggingComparator<>());
    set.add('a');
}

private static class LoggingComparator<T extends Comparable<? super T>> implements Comparator<T> {
    @Override
    public int compare(T o1, T o2) {
        System.out.println(o1 + " <=> " + o2);
        return o1.compareTo(o2);
    }
}
Java vendor & version: Oracle Corporation 11.0.4+10-LTS
a <=> a
英文:

The code is making the assumption that the TreeSet will only call the comparator against an equal element if one already exists in the set, and if it does such a comparison, it will only do it exactly once. However, this is not how TreeSet is implemented. Looking at the API documentation for TreeSet, there are no guarantees as to how the comparisons will occur or with what frequency. Since this is not a documented part of the API, the authors of TreeSet are free to implement this functionality in any reasonable manner they wish, so long as it meets the documented API. In fact, they are also allowed to change how it's implemented between versions (e.g. Java 6 & Java 7), or between different implementations (e.g. Oracle vs. IBM).

In short, if the documentation does not guarantee a behavior, your code should not rely on that behavior.

To go into the specific behavior you're seeing, the first element added to a TreeSet (in the versions of Java you're using) is compared against itself. While this is perhaps surprising, it is not disallowed by the API. There may or may not be a good reason for this (I believe the check was added in Java 7 to force a NullPointerException to be thrown when a null is added as the first element to a TreeSet that disallows nulls per this bug). However, in the end, the reason for the check shouldn't matter to users of the API, since it's not disallowed in the API.

public static void main(String[] args) {
    System.out.printf(&quot;Java vendor &amp; version: %s %s\n&quot;, System.getProperty(&quot;java.vendor&quot;), Runtime.version());

    TreeSet&lt;Character&gt; set = new TreeSet&lt;&gt;(new LoggingComparator&lt;&gt;());
    set.add(&#39;a&#39;);
}

private static class LoggingComparator&lt;T extends Comparable&lt;? super T&gt;&gt; implements Comparator&lt;T&gt; {
    @Override
    public int compare(T o1, T o2) {
        System.out.println(o1 + &quot; &lt;=&gt; &quot; + o2);
        return o1.compareTo(o2);
    }
}
Java vendor &amp; version: Oracle Corporation 11.0.4+10-LTS
a &lt;=&gt; a

huangapple
  • 本文由 发表于 2020年10月2日 15:19:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/64167556.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定