Подтвердить что ты не робот

Guava - Как удалить из списка на основе предиката, отслеживая, что было удалено?

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

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

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

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

Есть некоторые очевидные решения для взлома, но что вы предложите как самые чистые?

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

4b9b3361

Ответ 1

Я бы захватил удаленные элементы в вашем коде 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;
    }
}); 

Ответ 2

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

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

Ответ 3

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

Ответ 4

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

Но если вы хотите повеселиться, вы можете также обернуть каждый из своих предикатов внутри предиката, который делегирует завернутый предикат, и сохраните значения, для которых 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;
    }
}

Ответ 5

Думаю, вам нужно:

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