Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guava - How to remove from a list, based on a predicate, keeping track of what was removed?

I have an ArrayList to be filtered, and various Guava Predicates to filter it with. This list will have only 50-100 elements.

I was planning on Iterables.removeIf using each predicate in turn. It is perhaps not maximally efficient but never mind (at least removeIf has some optimization for RandomAccess lists)

For debugging, I want to concisely log what each predicate did. e.g.

Pred0 removed [a, c, g]
Pred1 removed []
Pred2 removed [b, f]

There are some obvious hack solutions but what would you suggest as the cleanest?

For bonus points, it should be reasonably efficient too. ;)

like image 714
Iain Avatar asked Jun 27 '11 14:06

Iain


People also ask

How do you remove an element from a list based on condition in Java?

To remove elements from ArrayList based on a condition or predicate or filter, use removeIf() method. You can call removeIf() method on the ArrayList, with the predicate (filter) passed as argument. All the elements that satisfy the filter (predicate) will be removed from the ArrayList.

Which method is used to eliminate elements based on criteria?

Using removeIf() As the name suggests, it is a direct method to remove all elements that satisfy the given predicate.

How do I remove a collection element?

An element can be removed from a Collection using the Iterator method remove(). This method removes the current element in the Collection.


3 Answers

I would capture the removed elements in your Predicate code.

List<String> removedElements = Lists.newArrayList();
final Iterables.removeIf(list, new Predicate<String>() {
    @Override
    public boolean apply(String input) {
        if ("a".equals(input)) {
            removedElements.add(input);
            return true;
        }
        return false;
    }
}); 
like image 116
JanRavn Avatar answered Oct 16 '22 14:10

JanRavn


This may be an occasion when using a loop is simplest.

List<MyType> list =
Predicate<MyType>[] predicates =
Map<Predicate, List<MyType>> removed = 
      new LinkedHashMap<Predicate, List<MyType>>();
for(Iterator<MyType> iter=list.iterator();list.hasNext();) {
   MyType mt = iter.next();
   for(Predicate<MyType> pred: predicates) 
       if(pred.apply(mt)) {
          List<MyType> mts = removed.get(pred);
          if(mts == null)
              removed.put(pred, mts = new ArrayList<MyType>());
          mts.add(mt);
          iter.remove();
          break;
       }
 }
like image 26
Peter Lawrey Avatar answered Oct 16 '22 16:10

Peter Lawrey


I'd investigate in observable predicates. The idea: everytime, a predicates apply method is about to return true, it will fire a notification to listeners:

Iterable<?> iterable = getIterable();
Collection<ObservablePredicate> predicates = getPredicates();
PredicatesLogger log = new PredicatesLogger(predicates);  // listens to all predicates
for (ObservablePredicate pred : predicates) {
  Iterables.removeIf(iterable, pred);
  log.print();
  log.reset();
}

The ObservableLogger is a decorator for Predicate :

public class ObservableLogger implements Predicate {
  private Predicate predicate;

  private List<Listener> listeners = new ArrayList<Listener>();
  // usual stuff for observer pattern

  @Override
  public boolean apply(Object input) {
    boolean result = predicate.apply(input);
    fire(result);
    return result;
  }

  // a fire method
}

The PredicateLogger needs one constructor that adds itself as a listener to the predicates. It will receive the notifications and cache the predicates that fired the events (the Event class needs an appropriate field for that information). print will create the log message, reset will clear the loggers cache (for the next run).

like image 4
Andreas Dolk Avatar answered Oct 16 '22 16:10

Andreas Dolk