【问题标题】:TreeSet contains method doesn't work for meTreeSet contains 方法对我不起作用
【发布时间】:2011-10-02 05:19:44
【问题描述】:

我想将自定义数据放入TreeSet。当自定义数字相同时,我添加交易量。

这是我的 TradeNode 类,它实现了 Comparable 交互器。

import java.util.Comparator;  

public class TradeNode implements Comparable<TradeNode> {  

    private String cstm; // custom number  

    private Integer mon = 0; // Trade  

    public TradeNode() {}  

    public TradeNode(String cstm, int mon) {  
        this.mon = mon;  
        this.cstm = cstm;  
    }  

    public int compareTo(TradeNode o) {  
        if (o.cstm.equals(this.cstm)) {  
            o.mon += this.mon;  
            return 0;  
        } else if (this.mon == o.mon) {  
            return this.cstm.compareTo(o.cstm);  
        } else {  
            //return (o.mon - this.mon);  
            return o.mon.compareTo(this.mon);  
        }  
    }  

    @Override  
    public boolean equals(Object obj) {  
        if (this == obj) {  
            return true;  
        }  
        if (obj == null) {  
            return false;  
        }  
        if (!(obj instanceof TradeNode)) {  
            return false;  
        }  
        TradeNode other = (TradeNode) obj;  
        if (cstm == null) {  
            if (other.cstm != null) {  
                return false;  
            }  
        } else if (!cstm.equals(other.cstm)) {  
            return false;  
        }  
        return true;  
    }  

    @Override  
    public int hashCode() {  
        final int prime = 31;  
        int result = 1;  
        result = prime * result + ((cstm == null) ? 0 : cstm.hashCode());  
        return result;  
    }  

    @Override  
    public String toString() {  
        return "[" + cstm + "] [" + mon + "]";  
    }  

    public int getMon() {  
        return mon;  
    }  

    public void setMon(Integer mon) {  
        this.mon = mon;  
    }  

    public String getCstm() {  
        return cstm;  
    }  

} 

测试类是:

public class Testtree {  
    public static void main(String[] args) {  
    TradeNode nd1 = new TradeNode("A", 100);  
        TradeNode nd2 = new TradeNode("B", 10);  
        TradeNode nd3 = new TradeNode("B", 1000);  
        TreeSet<TradeNode> tree = new TreeSet<TradeNode>();  
        tree.add(nd1);  
        tree.add(nd2);  
        tree.add(nd3);  
        for (TradeNode node : tree) {  
            System.out.println(node);  
        }  
    } 

我认为输出应该是这样的:

[B] [1010]  
[A] [100]

但输出是

[B] [1000]  
[A] [100] 
[B] [10]

谁能帮我指出我的错在哪里?

如果我像这样更改 compareTo() 方法,它仍然不起作用。

public int compareTo(TradeNode o) {
        if (o.cstm.equals(this.cstm)) {
            return 0;
        } else {
            return o.mon.compareTo(this.mon);
        }
    }

结果是:

[B] [1000]
[A] [100]
[B] [10]

我尝试了Ben Xu的方法,代码如下: 我的新 compareTo() 方法:

public int compareTo(TradeNode o) {
        if (o.cstm.equals(this.cstm)) {
            return 0;
        } else {
            return this.mon.compareTo(o.mon);
        }
    }

我的新 Testtree 类:

public class Testtree {

    public static void main(String[] args) {
        TradeNode nd1 = new TradeNode("44010358010481", 150354);
        TradeNode nd2 = new TradeNode("44010358010481", 150641);
        TradeNode nd3 = new TradeNode("44010358010481", 270000);
        TradeNode nd4 = new TradeNode("44010039275685", 10000);
        TradeNode nd5 = new TradeNode("44010039275685", 980000);
        TradeNode nd6 = new TradeNode("44010039275685", 5000);
        TradeNode nd7 = new TradeNode("44010234235687", 10000);
        TradeNode nd8 = new TradeNode("44010234235687", 360000);
        TradeNode nd9 = new TradeNode("44010234235687", 53400);
        Map<String, Integer> map = new HashMap<String, Integer>(); 
        addTradeNode(map, nd1);
        addTradeNode(map, nd2);
        addTradeNode(map, nd3);
        addTradeNode(map, nd4);
        addTradeNode(map, nd5);
        addTradeNode(map, nd6);
        addTradeNode(map, nd7);
        addTradeNode(map, nd8);
        addTradeNode(map, nd9);

        Iterator<Entry<String, Integer>> iterator = map.entrySet().iterator();
        TradeNode t;
        List<TradeNode> list = new ArrayList<TradeNode>();
        while(iterator.hasNext()) {
            Map.Entry<String, Integer> m = iterator.next();
            t = new TradeNode(m.getKey(),m.getValue());
            list.add(t);
        }
        Collections.sort(list);
        for(TradeNode tn : list) {
            System.out.println(tn);
        }
    }

    private static void addTradeNode(Map<String, Integer> map, TradeNode node) {

        Integer integer = map.get(node.getCstm());
        if (integer == null) {
            map.put(node.getCstm(), node.getMon());
        } else {
            map.remove(node.getCstm());
            map.put(node.getCstm(), integer.intValue() + node.getMon());
        }

    }

}

结果是:

[44010234235687] [423400]
[44010358010481] [570995]
[44010039275685] [995000]

终于满足了我的要求。但是我仍然不知道为什么这个新的 compareTo() 方法在下面的测试方法中不起作用:

public class Testtree2 {

    public static void main(String[] args) {
        TradeNode nd1 = new TradeNode("A", 100);
        TradeNode nd2 = new TradeNode("B", 10);
        TradeNode nd3 = new TradeNode("B", 1000);
        TreeSet<TradeNode> tree = new TreeSet<TradeNode>();
        tree.add(nd1);
        tree.add(nd2);
        tree.add(nd3);
        for (TradeNode node : tree) {
            System.out.println(node);
        }       
    }
}

结果是:

[B] [10]
[A] [100]
[B] [1000]

我认为它是:

[B] [10]
[A] [100]

有人能告诉我新的 compareTo() 方法的问题出在哪里吗?非常感谢,感谢任何帮助我的人。

哈哈哈,我从 JavaRanch 得到了答案。有个叫 Henry 的人告诉了我答案。现在我认为当我们在 TreeSet 中使用 contains() 方法时,它不会搜索此 Set 中的所有内容,它只搜索排序后的值。

新的 Testtree3 类是:

public class Testtree3 {

    public static void main(String[] args) {
    TradeNode nd1 = new TradeNode("A", 100);
        TradeNode nd2 = new TradeNode("B", 200);
        TradeNode nd3 = new TradeNode("B", 1000);
        TreeSet<TradeNode> tree = new TreeSet<TradeNode>();
        tree.add(nd1);
        tree.add(nd2);
        tree.add(nd3);
        for (TradeNode node : tree) {
            System.out.println(node);
        }
    }

}

结果是:

[A] [100]
[B] [200]

哈哈。现在我去寻找 TreeSet 背后的代码。

【问题讨论】:

  • 您的意思是您希望它是:[B] [1010] [A] [100]?
  • 请看我的回答,使用地图求和,不要使用集合(列表或集合)

标签: java contains treeset


【解决方案1】:

TreeSet.add 并没有按照你的想法去做。

如果它检测到一个值已经存在,它不会尝试将新值“添加”到现有值 - 它只是返回而不更改集合。这只是一个基于集合的操作。

(此外,您的比较与equals 方法不同步这一事实有点奇怪,this.mon == o.mon 的比较不适用于Integer。)

【讨论】:

    【解决方案2】:

    你真的不应该改变TradeNode#compareTo(...) 中的参数。无法保证TreeSet 在进行比较时是否会调用newItem.compareTo(existingItem)existingItem.compareTo(newItem)

    您可能应该修复您的TradeNode#compareTo(...),以便它在没有突变的情况下履行Comparator 合同。

    如果您想改变它包含的对象,我不确定SetTreeSet 或其他)是否真的是正确的数据结构。也许从StringTradeNodeMap 会是更好的选择?

    【讨论】:

      【解决方案3】:

      运行结果为 ,可以勾选再次运行程序。

      [B] [1000]
      [A] [100]
      [B] [10]
      

      结果是由于 treeset 使用了您实现的比较器

      我不知道你想做什么。

      但至少有一种明显的坏习惯:

      public int compareTo(TradeNode o) {  
          if (o.cstm.equals(this.cstm)) {  
              o.mon += this.mon;  
              return 0;  
          } else if (this.mon == o.mon) {  
              return this.cstm.compareTo(o.cstm);  
          } else {  
              //return (o.mon - this.mon);  
              return o.mon.compareTo(this.mon);  
          }  
      }  
      

      您不应在 compareTo 方法中更改其值“ o.mon += this.mon; ”,这非常令人困惑。

      如果你想对所有同名的 TreeNode 求和, 不要使用 Collection ,而是使用 map 。

      例如,使用 hashmap,key 是 name 或(TreeNode,因为它的 equals 和 hashcode 只使用 cstm ),value 是 num。每次添加 TreeNode 时,检查是否存在相同的名称,如果不存在,则添加到地图,否则添加值。

      以下是一个使用地图的示例代码:

      public class Testtree {
      public static void main(String[] args) {
          TradeNode nd1 = new TradeNode("A", 100);
          TradeNode nd2 = new TradeNode("B", 10);
          TradeNode nd3 = new TradeNode("B", 1000);
          Map<String, Integer> map = new HashMap<String, Integer>();
          addTreeNode(map, nd1);
          addTreeNode(map, nd2);
          addTreeNode(map, nd3);
          System.out.println(map);
      }
      
      private static void addTreeNode(Map<String, Integer> map, TradeNode node) {
      
          Integer integer = map.get(node.getCstm());
          if (integer == null) {
              map.put(node.getCstm(), node.getMon());
          } else {
              map.remove(node.getCstm());
              map.put(node.getCstm(), integer.intValue() + node.getMon());
          }
      
      }
      }
      

      【讨论】:

      • 是的,你的结果是对的,我会编辑它。你建议我使用 HashMap 而不是 TreeSet,但我想要一个排序的集合。根据你的意见,我可以把 TradeNode 放在一个 HashMap 中,然后再排序,对吗?
      • 查看上面添加的使用 map 的示例代码。如果需要,您可以将值更改为 TreeNode。如果想要排序版本,请更改为使用 TreeMap 并尝试。
      • TreeMap 按键排序。我认为稍后排序更好。
      【解决方案4】:

      您的 compareTo 方法包含更改状态 o.mon += this.mon; 的代码,这是非常糟糕的设计,更糟糕的是,该状态用于确定 compareTo if (this.mon == o.mon) 的结果。由于存在这种有害关系,因此您的实现几乎肯定违反了 compareTo 的约定:请参阅its javadoc

      这太可怕了。消除 compareTo 方法中的副作用样式状态更改。

      public int compareTo(TradeNode o) {  
          if (o.cstm.equals(this.cstm)) {  
              o.mon += this.mon;    // ALARM BELLS!!! SIDE EFFECT!! ARRGGGHHH!
              return 0;  
          } else if (this.mon == o.mon) {  // AND THE SIDE EFFECT IS ALSO USED TO COMPARE! AVERT YOUR EYES! 
              return this.cstm.compareTo(o.cstm);  
          } else {  
              //return (o.mon - this.mon);  
              return o.mon.compareTo(this.mon);  
          }  
      }  
      

      【讨论】:

      • 如果我这样更改代码,对吗?public int compareTo(TradeNode o) { if (o.cstm.equals(this.cstm)) { return 0; } else if (this.mon == o.mon) { return this.cstm.compareTo(o.cstm); } 其他 { 返回 o.mon.compareTo(this.mon); } }
      • @nhr:如果你想知道它们是否相等,你不应该通过==比较对象(在你的情况下是Integer)。请改用this.mon.equals(o.mon)
      猜你喜欢
      • 2017-10-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-21
      相关资源
      最近更新 更多