web-dev-qa-db-ja.com

LinkedHashSet-挿入順序と複製-最新の「先頭」を維持

挿入順序を保持し、一意の値を持つコレクションが必要です。 LinkedHashSetは進むべき道のように見えますが、1つの問題があります。2つのアイテムが等しい場合、最新のものを削除する(これは理にかなっています)、例を次に示します。

set.add("one");
set.add("two");
set.add("three");
set.add("two");

LinkedHashSetは次を印刷します。

onetwothree

しかし、私が必要なのは:

onethreetwo

ここで最善の解決策は何でしょうか?これを行うことができるコレクション/コレクションメソッドはありますか、それを手動で実装する必要がありますか?

32
rafakob

Java Collections のほとんどは、微調整のために拡張できます。

サブクラス LinkedHashSetadd メソッドをオーバーライドします。

class TweakedHashSet<T> extends LinkedHashSet<T> {

    @Override
    public boolean add(T e) {
        // Get rid of old one.
        boolean wasThere = remove(e);
        // Add it.
        super.add(e);
        // Contract is "true if this set did not already contain the specified element"
        return !wasThere;
    }

}
34
OldCurmudgeon

単にLinkedHashMapの特別な機能を使用できます:

Set<String> set = Collections.newSetFromMap(new LinkedHashMap<>(16, 0.75f, true));
set.add("one");
set.add("two");
set.add("three");
set.add("two");
System.out.println(set); // prints [one, three, two]

OracleのJREではLinkedHashSetLinkedHashMapに裏打ちされているため、機能的な違いはあまりありませんが、ここで使用される特別なコンストラクターはLinkedHashMapを構成して、 access挿入だけではありません。これは多すぎるように聞こえるかもしれませんが、実際には既に含まれているキー(Setの意味での値)の挿入にのみ影響します。影響を受ける他のMap操作(つまりget)は、返されるSetでは使用されません。

Java 8を使用していない場合は、型の推論が制限されているため、コンパイラを少し支援する必要があります。

Set<String> set
    = Collections.newSetFromMap(new LinkedHashMap<String, Boolean>(16, 0.75f, true));

しかし、機能は同じです。

18
Holger

LinkedHashSetを初期化するときに、addメソッドをオーバーライドできます。

Set<String> set = new LinkedHashSet<String>(){
    @Override
    public boolean add(String s) {
        if(contains(s))
            remove(s);
        return super.add(s);
    }
};

今、あなたに与えます:

set.add("1");
set.add("2");
set.add("3");
set.add("1");
set.addAll(Collections.singleton("2"));

// [3, 1 ,2]

addAllメソッドも機能しています。

6
Toonijn

上記のソリューションはすべて優れていますが、すでに実装されているコレクションをオーバーライドしたくない場合。 ArrayListをちょっとしたトリックで使用するだけで、この問題を解決できます。

リストにデータを挿入するために使用するメソッドを作成できます

_public static <T> void addToList(List<T> list, T element) {
    list.remove(element); // Will remove element from list, if list contains it
    list.add(element); // Will add element again to the list 
}
_

そして、このメソッドを呼び出してリストに要素を追加できます

_List<String> list = new ArrayList<>();

addToList(list, "one");
addToList(list, "two");
addToList(list, "three");
addToList(list, "two");
_

ここでの唯一の欠点は、addToList()の代わりに毎回カスタムlist.add()メソッドを呼び出す必要があることです。

1
Naresh Joshi