简体   繁体   English

番石榴 - 如何从列表中删除,基于谓词,跟踪删除的内容?

[英]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 Predicate s to filter it with. 我有一个要过滤的ArrayList ,以及各种过滤它的Guava Predicate This list will have only 50-100 elements. 此列表只有50-100个元素。

I was planning on Iterables.removeIf using each predicate in turn. 我正在计划Iterables.removeIf依次使用每个谓词。 It is perhaps not maximally efficient but never mind (at least removeIf has some optimization for RandomAccess lists) 它可能不是最大效率但从不介意(至少removeIf对RandomAccess列表有一些优化)

For debugging, I want to concisely log what each predicate did. 对于调试,我想简明地记录每个谓词的作用。 eg 例如

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. 对于奖励积分,它也应该是合理有效的。 ;) ;)

I would capture the removed elements in your Predicate code. 我会捕获您的Predicate代码中删除的元素。

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

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

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: 这个想法:每次,谓词apply方法即将返回true,它会向听众发出通知:

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 : ObservableLoggerPredicate的装饰器:

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. PredicateLogger需要一个构造函数,它将自己添加为谓词的侦听器。 It will receive the notifications and cache the predicates that fired the events (the Event class needs an appropriate field for that information). 它将接收通知并缓存触发事件的谓词( Event类需要该信息的适当字段)。 print will create the log message, reset will clear the loggers cache (for the next run). print将创建日志消息, reset将清除loggers缓存(用于下次运行)。

I agree with Peter's anwser. 我同意彼得的回复。

But if you want to have fun, you could also wrap each of your predicate inside a predicate which would delegate to the wrapped predicate, and store the values for which true is returned inside a Map<Predicate, List<Foo>> removedByPredicate : 但是如果你想获得乐趣,你也可以将每个谓词包装在一个谓词中,该谓词将委托给包装的谓词,并存储在Map<Predicate, List<Foo>> removedByPredicate返回true的值:

class PredicateWrapper implements Predicate<Foo> {
    private Predicate<Foo> delegate;

    public PredicateWrapper(Predicate<Foo> delegate) {
        this.delegate = delegate;
    }

    @Override
    public boolean apply(Foo foo) {
        boolean result = delegate.apply(foo);
        if (result) {
            List<Foo> objectsRemoved = removedByPredicate.get(delegate);
            if (objectsRemoved == null) {
                objectsRemoved = Lists.newArrayList();
                removedByPredicate.put(delegate, objectsRemoved);
            }
            objectsRemoved.add(foo);
        }
        return result;
    }
}

I guess you need: 我想你需要:

Predicate<XXX> predicate1 = new Predicate<XXX>(){  
    @Override  
    public boolean apply(XXX input) {  
        if(...) //satisfy your filter
            return true;  
        else  
            return false;  
}};  

Predicate<XXX> predicate2 = new Predicate<XXX>(){  
    @Override  
    public boolean apply(XXX input) {  
        if(...) //satisfy your filter
            return true;  
        else  
            return false;  
}};
Predicate allPredicates = Predicates.and(predicate1, predicate2);
//or Predicates.or(predicate1, predicate2);

Collection<XXX> list2 = Collections2.filter(list, allPredicates); 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM