TreeSetのコンパレータは、いくつかのケースで重複を削除することができませんでしたか?

user1589188:

私は私のTreeSetのために、以下のコンパレータがあります。

public class Obj {
    public int id;
    public String value;
    public Obj(int id, String value) {
        this.id = id;
        this.value = value;
    }
    public String toString() {
        return "(" + id + value + ")";
    }
}

Obj obja = new Obj(1, "a");
Obj objb = new Obj(1, "b");
Obj objc = new Obj(2, "c");
Obj objd = new Obj(2, "a");
Set<Obj> set = new TreeSet<>((a, b) -> {
    System.out.println("Comparing " + a + " and " + b);
    int result = a.value.compareTo(b.value);
    if (a.id == b.id) {
        return 0;
    }
    return result == 0 ? Integer.compare(a.id, b.id) : result;
});
set.addAll(Arrays.asList(obja, objb, objc, objd));
System.out.println(set);

これは、重複を除去[(1A)、(2C)]を、プリントアウト。

私が最後に変更されたときしかしInteger.compareするInteger.compare(b.id, a.id)(すなわち、Bの位置を切り換え)、これは、[(2A)、(1A)、(2C)]プリントアウト。明らかに同じid 2回登場しました。

あなたはいつもにコンパレータをどのように修正すればよいのIDに基づいて重複を削除し、ソート値(昇順)そして、ID(降順)に基づく順序集合?

ルシオ:

あなたはしていaskimg:
どのようにあなたが常にIDに基づいて重複を削除し、ソート値(昇順)そして、ID(降順)に基づく順序セットにコンパレータを修正するのですか?

あなたは、比較がしたいです

  1. 削除重複に基づきます Obj.id
  2. でセットを並べ替えるObj.alueと、Obj.id

要件1)の結果で

Function<Obj, Integer> byId = o -> o.id;
Set<Obj> setById = new TreeSet<>(Comparator.comparing(byId));

要件2)の結果で

Function<Obj, String> byValue = o -> o.value;
Comparator<Obj> sortingComparator =  Comparator.comparing(byValue).thenComparing(Comparator.comparing(byId).reversed());
Set<Obj> setByValueAndId = new TreeSet<>(sortingComparator);

のは、上を見てみましょうのJavaDocのをTreeSetそれは言います:

順序が設定することによって維持することを注意[...]と一致している必要がありequals、それは正しく実装している場合はSetインターフェイスを。これがそうであるSetインタフェースがに関して定義されたequals動作が、TreeSetインスタンスを行う全ての素子の比較は、その使用compareTo(または比較)方法を、この方法によって等しいと見なされるので、2つの要素がセットの観点から、ある等しいです。

セットには、コンパレータに従って順序付けされますが、その要素もコンパレータを使用して等しいかどうかを比較しています。

私の知る限り見ることができるように定義する方法がありませんComparatorその両方を満たす要件を。以来、TreeSet最初の場所にあるSet必要1)と一致しなければなりません。要件2を達成するために)あなたは、第二を作成することができますTreeSet

Set<Obj> setByValueAndId = new TreeSet<>(sortingComparator);
setByValueAndId.addAll(setById);

それとも、セット自体は必要ありませんが、あなたが使用することができ所望の順序で要素を処理する場合Stream

Consumer<Obj> consumer = <your consumer>;
setById.stream().sorted(sortingComparator).forEach(consumer);

ところで:
それはの要素をソートすることも可能ですがStream与えられたに応じてComparator何が無いdistinct服用方法Comparator、それに応じて重複を削除します。


EDIT:
1.重複除去、2ソート:次の2つの異なるタスクを持っています。一つは、Comparator両方のタスクを解決することはできません。だから、選択肢があり何ですか?

あなたは無効にすることができますequalsし、hashCodeObjその後、HashSetまたはStream重複を除去するために使用することができます。
並べ替えのためにあなたはまだ必要ですComparator(上記のように)。実装Comparableだけによる「equalsと一貫性」ではありません発注につながるソートするためComparable のJavaDoc

以来Stream、両方のタスクを解決することができ、それは私の選択です。まず、上書きhashCodeequalsで重複を識別するためにid

public int hashCode() {
    return Integer.hashCode(id);
}

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    Obj other = (Obj) obj;
    if (id != other.id)
        return false;
    return true;
}

今、私たちは使用することができますStream

// instantiating one additional Obj and reusing those from the question
Obj obj3a = new Obj(3, "a");

// reusing sortingComparator from the code above
Set<Obj> set = Stream.of(obja, objb, objc, objd, obj3a)
        .distinct()
        .sorted(sortingComparator)
        .collect(Collectors.toCollection(LinkedHashSet::new));

System.out.println(set); // [(3a), (1a), (2c)]

返されたLinkedHashSetの意味を持っているSetが、それはまたの順序を保存しますsortingComparator


EDIT(コメントからの質問に答えます)

Q:なぜ、それは正しく仕事を完了しませんでしたか?
自分自身でそれを参照してください。あなたの最後の行に変更しComparator、以下のように

int r = result == 0 ? Integer.compare(a.id, b.id) : result;
System.out.println(String.format("a: %s / b: %s / result: %s -> %s", a.id, b.id, result, r));
return r;

一度コードを実行し、その後のオペランドを切り替えますInteger.compareスイッチは、別の比較経路をもたらします。とき違いがある(2a)(1a)比較されます。

最初の実行では(2a)より大きく、(1a)それは次のエントリと比較していますので(2c)平等でこの結果-重複が発見されました。

セカンドランでは(2a)より小さくなります(1a)したがって(2a)、以前のエントリと次のように比較されます。しかし、(1a)すでに最小のエントリで、以前の1はありません。したがって、重複が見つかりません(2a)、それがセットに追加されます。

Q:あなたは、1個のコンパレータを2つのタスクを完了することはできません、実際には私の第一のコンパレータが正しく両方のタスクをしたと述べました。
はい-だけ与えられた例。追加Obj obj3a私が行ったようにセットにし、あなたのコードを実行します。返されるソートセットは次のとおりです。

[(1a), (3a), (2c)]

これは同じのためにソートするためにあなたの条件に違反したvalueことにより、降順秒id今ではで昇順ですid私のコードを実行し、上記のように、それは、正しい順序を返します。

苦しんでComparator、私は次のコメントを得た前の時間:「...それは手動のコンパレータの実装がいかに難しい実証し、偉大な運動だ...」(ソース

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=206452&siteId=1