2

注意:コードはスレッドセーフ、つまりシングルスレッドです。リスナーのリストから自分を削除できるのは私だけです。

以下のようなコードで通知する必要があるリスナーの 1000 の異なる ArrayLists があります。

protected void onFlushed() {
    int size = listeners.size();
    for (int i = 0; i < size; i++) {
        listeners.get(i).onFlushed();
        int newSize = listeners.size();
        if (newSize == size - 1) {
            size--;
            i--;
        } else if (newSize != size) {
            throw new ConcurrentModificationException("List was altered while iterating! oldSize=" + size + " newSize=" + newSize);
        }
    }
}

リスナーの ArrayList とリスナーメソッドを取り、以下のロジックを実行できる Notifier クラスを作成するスマートな方法はありますか、またはリスナーメソッドごとに上記のコードをどこでも複製する必要がありますか?

私は Java に多くを求めすぎていませんか? :(

4

2 に答える 2

3

まず第一に、このコードのほとんどが、あまり安全ではない方法で並行性の問題を処理しているように見えるという事実が少し心配です。同じリスナのリストにアクセスするすべてのコードをロックすることを検討してください。そうすれば、このコードを次のように単純化できます。

protected void onFlushed() {
    synchronized(listeners) {
        for (FlushListener listener : listeners) {
            listener.onFlushed();
        }
    }
}

コードの周りで繰り返したのはそれほど悪くありません。そうは言っても、そこにあるロジックが必要な理由がある場合は、次のようなクラスを作成できます。

import java.util.ArrayList;
import java.util.ConcurrentModificationException;

public class Notifier<T> {
    public static interface NotificationRunnable<T> {
        public void notify(T t);
    }

    private ArrayList<T> listeners;
    public Notifier() {
        listeners = new ArrayList<T>();
    }

    public void addListener(T t) {
        listeners.add(t);
    }

    public void notifyAll(NotificationRunnable<T> nr) {
        int size = listeners.size();
        for (int i = 0; i < size; i++) {
            nr.notify(listeners.get(i));
            int newSize = listeners.size();
            if (newSize == size - 1) {
                size--;
                i--;
            } else if (newSize != size) {
                throw new ConcurrentModificationException("List was altered while iterating! oldSize=" + size + " newSize=" + newSize);
            }
        }
    }
}

そして、それを呼び出すコードは次のようになります。

Notifier<FlushListener> notifier = new Notifier();
notifier.addListener(new FlushListener());

public void onFlushed() {
    notifier.notifyAll(new NotificationRunnable<FlushListener>(){
        public void notify(FlushListener t) {
            t.onFlushed();
        }
    });
}

または、実際のメソッドで 1 行にまとめることもできます。

NotificationRunnable<FlushListener> flushRunnable = new NotificationRunnable<FlushListener>(){
    public void notify(FlushListener t) {
        t.onFlushed();
    }
}

public void onFlushed() {
    notifier.notifyAll(flushRunnable);
}
于 2012-08-24T23:18:59.440 に答える
2

現在の Java では、関数型言語のようにメソッドを渡す方法はありませんが、インターフェイスを作成し、Function<Domain, Range>そのインターフェイスを実装するオブジェクトを渡すことができます。

関数イディオムとリスト内包表記に似たものを提供するGuavaの使用を検討することもできます。

于 2012-08-24T23:21:23.800 に答える