特定の状況下では、Java Set
の最も古い要素を削除する必要があります。セットは LinkedHashSet
を使用して実装されます。これにより、これが簡単になります。セットのイテレータによって返される最初の要素を削除するだけです。
Set<Foo> mySet = new LinkedHashSet<Foo>();
// do stuff...
if (mySet.size() >= MAX_SET_SIZE)
{
Iterator<Foo> iter = mySet.iterator();
iter.next();
iter.remove();
}
これは醜いです:私が何かをするための3行couldSortedSet
を使用していた場合、1行で行います(他の理由で、ここではSortedSet
はオプションではありません)::
if (/*stuff*/)
{
mySet.remove(mySet.first());
}
それで、これを行うためのよりクリーンな方法があります、なし:
Set
実装の変更、またはGuava を活用するソリューションは問題ありません。
セットには固有の順序がないことを十分に認識しています。反復順序で定義されている最初のエントリを削除することについて質問しています。
LinkedHashSetは、単純な「最も古いものを削除する」ポリシーをサポートするLinkedHashMapのラッパーです。セットとして使用するには、
Set<String> set = Collections.newSetFromMap(new LinkedHashMap<String, Boolean>(){
protected boolean removeEldestEntry(Map.Entry<String, Boolean> eldest) {
return size() > MAX_ENTRIES;
}
});
if (!mySet.isEmpty())
mySet.remove(mySet.iterator.next());
3行未満のようです。
もちろん、セットが複数のスレッドで共有されている場合は、その周りで同期する必要があります。
コードのいくつかの場所でこれを本当に行う必要がある場合は、静的メソッドを作成するだけです。
提案されている他のソリューションは、Set.remove(Object)
メソッドの代わりにIterator.remove()
メソッドを呼び出すことを意味するため、多くの場合遅くなります。
@Nullable
public static <T> T removeFirst(Collection<? extends T> c) {
Iterator<? extends T> it = c.iterator();
if (!it.hasNext()) { return null; }
T removed = it.next();
it.remove();
return removed;
}
グアバ付き:
if (!set.isEmpty() && set.size() >= MAX_SET_SIZE) {
set.remove(Iterables.get(set, 0));
}
別のアプローチも提案します。はい、それは実装を変更しますが、大幅ではありません:LinkedHashSet
を拡張し、add
メソッドにその条件を設定します。
public LimitedLinkedHashSet<E> extends LinkedHashSet<E> {
public void add(E element) {
super.add(element);
// your 5-line logic from above or my solution with guava
}
}
まだ5行ですが、それを使用しているコードからは見えません。また、これは実際にはセットの特定の動作であるため、セット内に含めるのが論理的です。
私はあなたがそれをしている方法は良いと思います。これは、もっと短い方法を見つける価値があるほど頻繁に行うことですか?あなたはこのようにグアバで基本的に同じことをすることができます:
_Iterables.removeIf(Iterables.limit(mySet, 1), Predicates.alwaysTrue());
_
これにより、セットとそのイテレータをラップして制限し、alwaysTrue()
述語を1回呼び出すという小さなオーバーヘッドが追加されます...しかし、私には特に価値がないようです。
編集:回答のコメントに私が言ったことを入れるには、次のようにキーごとに持つことができる値の数を自動的に制限するSetMultimap
を作成できます。
_SetMultimap<K, V> multimap = Multimaps.newSetMultimap(map,
new Supplier<Set<V>>() {
public Set<V> get() {
return Sets.newSetFromMap(new LinkedHashMap<V, Boolean>() {
@Override protected boolean removeEldestEntry(Entry<K, V> eldestEntry) {
return size() > MAX_SIZE;
}
});
}
});
_
迅速で汚い1行の解決策:mySet.remove(mySet.toArray(new Foo[mySet.size()])[0])
;)
ただし、これは読みやすく、高速である必要があるため、イテレータソリューションを使用します。
編集:私はマイクサミュエルの解決策に行きます。 :)