从Set中删除“第一个”对象

时间:2011-04-26 15:25:25

标签: java set guava

在某些情况下,我需要逐出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的解决方案都可以。


我完全清楚集合没有固有的顺序。我要求删除迭代顺序定义的第一个条目。

6 个答案:

答案 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的解决方案。 :)