Removing the “first” object from a Set

前端 未结 6 1881
北恋
北恋 2021-02-05 06:42

Under certain situations, I need to evict the oldest element in a Java Set. The set is implemented using a LinkedHashSet, which makes this simple: just get rid of t

相关标签:
6条回答
  • 2021-02-05 06:57
    if (!mySet.isEmpty())
      mySet.remove(mySet.iterator.next());
    

    seems to be less than 3 lines.

    You have to synchronize around it of course if your set is shared by multiple threads.

    0 讨论(0)
  • 2021-02-05 06:57

    With guava:

    if (!set.isEmpty() && set.size() >= MAX_SET_SIZE) {
        set.remove(Iterables.get(set, 0));
    }
    

    I will also suggest an alternative approach. Yes, it it changing the implementation, but not drastically: extend LinkedHashSet and have that condition in the add method:

    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
        }
    }
    

    It's still 5 line, but it is invisible to the code that's using it. And since this is actually a specific behaviour of the set, it is logical to have it within the set.

    0 讨论(0)
  • 2021-02-05 06:58

    Quick and dirty one-line solution: mySet.remove(mySet.toArray(new Foo[mySet.size()])[0]) ;)

    However, I'd still go for the iterator solution, since this would be more readable and should also be faster.

    Edit: I'd go for Mike Samuel's solution. :)

    0 讨论(0)
  • 2021-02-05 07:16

    LinkedHashSet is a wrapper for LinkedHashMap which supports a simple "remove oldest" policy. To use it as a Set you can do

    Set<String> set = Collections.newSetFromMap(new LinkedHashMap<String, Boolean>(){
        protected boolean removeEldestEntry(Map.Entry<String, Boolean> eldest) {
            return size() > MAX_ENTRIES;
        }
    });
    
    0 讨论(0)
  • 2021-02-05 07:19

    If you really need to do this at several places in your code, just write a static method.

    The other solutions proposed are often slower since they imply calling the Set.remove(Object) method instead of the Iterator.remove() method.

    @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;
    }
    
    0 讨论(0)
  • 2021-02-05 07:19

    I think the way you're doing it is fine. Is this something you do often enough to be worth finding a shorter way? You could do basically the same thing with Guava like this:

    Iterables.removeIf(Iterables.limit(mySet, 1), Predicates.alwaysTrue());
    

    That adds the small overhead of wrapping the set and its iterator for limiting and then calling the alwaysTrue() predicate once... doesn't seem especially worth it to me though.

    Edit: To put what I said in a comment in an answer, you could create a SetMultimap that automatically restricts the number of values it can have per key like this:

    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;
              }
            });
          }
        });
    
    0 讨论(0)
提交回复
热议问题