【问题标题】:Removing the "first" object from a Set从集合中删除“第一个”对象
【发布时间】:2011-04-26 15:25:24
【问题描述】:

在某些情况下,我需要驱逐 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,我可以用 1 行代码完成 3 行(由于其他原因,SortedSet 不是这里的选项):

if (/*stuff*/)
{
    mySet.remove(mySet.first());
}

那么有没有更简洁的方法来做到这一点,没有:

  • 更改Set 实现,或
  • 编写静态实用方法?

任何利用Guava 的解决方案都可以。


我完全知道集合没有内在的顺序。我问的是删除迭代顺序定义的第一个条目。

【问题讨论】:

  • 您能评论一下为什么不能选择 SortedSet 吗?有了更多信息,有人可能会提供更好的解决方案。
  • 为什么迭代器返回的第一个元素应该是最旧的?
  • @RHSeeger,排序集不是一个选项,因为它比较对象并按其自然顺序排序。对象不会跟踪它们在其他 Collection 对象中的插入顺序,因此无法覆盖或更改自然顺序以模仿插入顺序。
  • @RHSeeger:因为我可以添加 1000 个具有相同时间戳的元素(以毫秒为单位)。
  • Do you have a good reason for not implementing your own version of Set/OrderedSet, even if the code involved is small? 是的……已经完成了;)

标签: java set guava


【解决方案1】:

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 表示添加对象时甚至不需要显式检查和删除操作的方式
  • @Thomas,您也不必担心addAll() 是否调用add(),因为这会为您解决。
  • 你每天都能学到新东西……这是一个很棒的技术! +1 当之无愧!
  • 嗯,我不知道removeEldestEntry。这似乎是做我想做的事的好方法。 唯一的挂断是Set&lt;Foo&gt; 来自番石榴SetMultimap。我实际上不是newing 集合;它来自SetMultimap&lt;String, Foo&gt; cache = LinkedHashMultimap.create(),后来调用cache.get("baz") 返回我正在使用的集合。
  • @Matt Ball:你可以创建一个Supplier&lt;Set&gt;,使用这种技术来制作集合,并与Multimaps.newMultimap一起使用。
【解决方案2】:
if (!mySet.isEmpty())
  mySet.remove(mySet.iterator.next());

似乎不到 3 行。

如果您的集合由多个线程共享,您当然必须围绕它进行同步。

【讨论】:

  • +1 使用 JDK-only 库的最短路径。
  • 比使用iterator.remove() 方法慢。
  • @LaurentPireyn,好点子。我将按原样保留此答案,并支持您的答案。
【解决方案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;
}

【讨论】:

    【解决方案4】:

    用番石榴:

    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 行,但它对使用它的代码是不可见的。而且由于这实际上是集合的特定行为,因此将其包含在集合中是合乎逻辑的。

    【讨论】:

      【解决方案5】:

      我认为你这样做的方式很好。这是您经常做的事情,是否值得寻找更短的方法?你可以像这样用 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;
                }
              });
            }
          });
      

      【讨论】:

        【解决方案6】:

        又快又脏的单线解决方案:mySet.remove(mySet.toArray(new Foo[mySet.size()])[0]) ;)

        但是,我仍然会选择迭代器解决方案,因为它更具可读性并且应该更快。

        编辑:我会选择 Mike Samuel 的解决方案。 :)

        【讨论】:

        • 呃。创建数组是一种浪费。如果您想要一个快速而肮脏的单行解决方案,只需将 OP 的代码放在一行中即可mySet.iterator().next().remove()
        • @wolfcastle 这不会编译,因为mySet.iterator().next() 会返回一个Foo 对象。如前所述,这非常丑陋且性能不佳,我赞成 Mike 的解决方案(我没有想到 ;)):mySet.remove(mySet.iterator().next()).
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-10-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多