Гуава - Как удалить из списка, основываясь на предикате, отслеживая, что было удалено? - PullRequest
17 голосов
/ 27 июня 2011

У меня есть ArrayList для фильтрации и различные Guava Predicate для фильтрации.Этот список будет содержать только 50-100 элементов.

Я планировал Iterables.removeIf, используя каждый предикат по очереди.Возможно, он не максимально эффективен, но не имеет значения (по крайней мере, removeIf имеет некоторую оптимизацию для списков RandomAccess)

Для отладки я хочу кратко записать, что делал каждый предикат.например,

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

Есть несколько очевидных хакерских решений, но что бы вы посоветовали в качестве наиболее чистых?

Для бонусных баллов он также должен быть достаточно эффективным.;)

Ответы [ 5 ]

32 голосов
/ 27 июня 2011

Я бы зафиксировал удаленные элементы в вашем коде предиката.

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;
    }
}); 
5 голосов
/ 27 июня 2011

Это может быть случай, когда использование цикла является самым простым.

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;
       }
 }
4 голосов
/ 27 июня 2011

Я бы исследовал в наблюдаемых предикатах . Идея: каждый раз, когда метод предикатов 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();
}

ObservableLogger - это декоратор для 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
}

PredicateLogger нужен один конструктор, который добавляет себя в качестве прослушивателя к предикатам. Он будет получать уведомления и кэшировать предикаты, которые инициировали события (классу Event необходимо соответствующее поле для этой информации). print создаст сообщение журнала, reset очистит кэш логгеров (для следующего запуска).

1 голос
/ 27 июня 2011

Я согласен с ответом Петра.

Но если вы хотите повеселиться, вы также можете обернуть каждый свой предикат в предикат, который будет делегирован обернутому предикату, и сохранить значения, для которых true возвращается в Map<Predicate, List<Foo>> removedByPredicate:

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;
    }
}
0 голосов
/ 27 июня 2011

Я думаю, вам нужно:

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