在某些情况下,我需要逐出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();
}
这很难看:如果我使用的是SortedSet
,可以使用3行代替我可以做1行(由于其他原因,SortedSet
不是一个选项这里):
if (/*stuff*/)
{
mySet.remove(mySet.first());
}
这样做是否有更简洁的方法,没有:
Set
实施,或任何利用Guava的解决方案都可以。
我完全清楚集合没有固有的顺序。我要求删除迭代顺序定义的第一个条目。
答案 0 :(得分:31)
LinkedHashSet是LinkedHashMap的包装器,它支持简单的“删除最旧”策略。要将它用作Set,你可以
Set<String> set = Collections.newSetFromMap(new LinkedHashMap<String, Boolean>(){
protected boolean removeEldestEntry(Map.Entry<String, Boolean> eldest) {
return size() > MAX_ENTRIES;
}
});
答案 1 :(得分:24)
if (!mySet.isEmpty())
mySet.remove(mySet.iterator.next());
似乎少于3行。
如果您的集合由多个线程共享,您必须同步它。
答案 2 :(得分:7)
如果您确实需要在代码中的多个位置执行此操作,请编写静态方法。
提出的其他解决方案通常较慢,因为它们暗示调用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;
}
答案 3 :(得分:3)
用番石榴:
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行,但是使用它的代码是不可见的。由于这实际上是集合的特定行为,因此将其置于集合中是合乎逻辑的。
答案 4 :(得分:2)
我认为你这样做的方式很好。这是你经常做的事情值得找一个更短的方法吗?你可以像这样用Guava做同样的事情:
Iterables.removeIf(Iterables.limit(mySet, 1), Predicates.alwaysTrue());
这增加了包装集合及其迭代器以限制然后再调用alwaysTrue()
谓词的小开销......但对我来说似乎并不值得。
编辑:要在答案中添加我在评论中所说的内容,您可以创建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;
}
});
}
});
答案 5 :(得分:0)
快速而肮脏的单行解决方案:mySet.remove(mySet.toArray(new Foo[mySet.size()])[0])
;)
但是,我仍然会选择迭代器解决方案,因为这样会更具可读性,也应该更快。
编辑:我会选择Mike Samuel的解决方案。 :)