web-dev-qa-db-ja.com

Guava-述語に基づいてリストから削除し、削除されたものを追跡する方法は?

フィルタリングするArrayListと、フィルタリングするさまざまなGuava Predicatesがあります。このリストには、50〜100個の要素のみが含まれます。

私は、各述語を順番に使用してIterables.removeIfを計画していました。おそらく最大限に効率的ではありませんが、気にしないでください(少なくともremoveIfにはRandomAccessリストの最適化があります)

デバッグのために、各述語が行ったことを簡潔にログに記録したいと思います。例えば.

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

明らかなハッキングソリューションがいくつかありますが、最もクリーンなものとして何を提案しますか?

ボーナスポイントについても、かなり効率的である必要があります。 ;)

17
Iain

削除された要素を述語コードでキャプチャします。

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

これは、ループの使用が最も簡単な場合です。

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;
       }
 }
5
Peter Lawrey

観察可能な述語で調査します。アイデア:述語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();
}

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
}

PredicateLoggerには、述語にリスナーとして自分自身を追加する1つのコンストラクターが必要です。通知を受信し、イベントを発生させた述語をキャッシュします(Eventクラスにはその情報に適切なフィールドが必要です)。 printはログメッセージを作成し、resetはロガーキャッシュをクリアします(次の実行のために)。

4
Andreas_D

私はピーターの答えに同意します。

ただし、楽しみたい場合は、各述語を、ラップされた述語に委任する述語内にラップし、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;
    }
}
1
JB Nizet