TreeSet содержит метод, который у меня не работает

Я хочу поместить пользовательские данные в файл 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]

Я попробовал метод Бен Сюй, и вот код: Мой новый метод 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. Кто-то по имени Генри сказал мне ответ. Теперь я думаю, что когда мы используем метод contains() в TreeSet, он не ищет все в этом наборе, он ищет только отсортированное значение.

Новый класс 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.


person nhr    schedule 10.07.2011    source источник
comment
Вы имели в виду, что хотите, чтобы это было: [B] [1010] [A] [100]?   -  person Ben Xu    schedule 10.07.2011
comment
Пожалуйста, посмотрите мой ответ, используйте карту для суммирования, не используйте коллекцию (список или набор)   -  person Ben Xu    schedule 10.07.2011


Ответы (4)


Результат запуска , вы можете проверить, чтобы запустить программу снова.

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

результат связан с тем, что набор деревьев использует ваш реализованный компаратор

Я не знаю, что ты хочешь сделать.

но есть по крайней мере одна очевидная плохая практика:

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 , вместо этого используйте карту.

например, используйте хэш-карту, ключ — это имя или (TreeNode, поскольку он равен, а хэш-код использует только cstm ), значение — 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());
    }

}
}
person Ben Xu    schedule 10.07.2011
comment
да, ваш результат правильный, я его отредактирую. Вы предлагаете мне использовать HashMap вместо TreeSet, но я хочу отсортированную коллекцию. По вашему мнению, я могу поместить TradeNode в HashMap и отсортировать позже, я прав? - person nhr; 10.07.2011
comment
см. выше добавленный пример кода с использованием карты. при необходимости вы можете изменить значение на TreeNode. Если вам нужна отсортированная версия, измените использование TreeMap и попробуйте. - person Ben Xu; 10.07.2011
comment
Сортировка TreeMap по ключу. Я думаю, что сортировать позже лучше. - person Ben Xu; 10.07.2011

TreeSet.add делает не то, что вы думаете.

Если он обнаруживает, что значение уже существует, он не пытается «добавить» новое значение к существующему — он просто возвращается без изменения набора. Это просто операция на основе множества.

(Кроме того, тот факт, что ваше сравнение не синхронизировано с вашим методом equals, немного странен, а сравнение this.mon == o.mon не подходит для Integer.)

person Jon Skeet    schedule 10.07.2011

Вам действительно не следует изменять аргумент в TradeNode#compareTo(...). Нет никакой гарантии, будет ли TreeSet вызывать newItem.compareTo(existingItem) или existingItem.compareTo(newItem) при сравнении.

Вероятно, вам следует исправить свой TradeNode#compareTo(...), чтобы он выполнял контракт Comparator без мутаций.

Я не уверен, что Set (TreeSet или что-то другое) действительно правильная структура данных, если вы хотите изменить содержащиеся в ней объекты. Возможно, Map от String до TradeNode будет лучшим выбором?

person clstrfsck    schedule 10.07.2011

Ваш метод compareTo содержит код, который изменяет состояние o.mon += this.mon;, что очень плохо, и, что еще хуже, это состояние используется для определения результата compareTo if (this.mon == o.mon). Так как эта токсичная связь существует, ваша реализация почти наверняка нарушает контракт compareTo: см. его 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);  
    }  
}  
person Bohemian♦    schedule 10.07.2011
comment
если я изменю свой код таким образом, правильно ли это? 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); } else { return o.mon.compareTo(this.mon); } } - person nhr; 10.07.2011
comment
@nhr: Вы не должны сравнивать объекты (в вашем случае Integer) на ==, если хотите знать, равны ли они. Вместо этого используйте this.mon.equals(o.mon). - person Paŭlo Ebermann; 10.07.2011