web-dev-qa-db-ja.com

Javaのリストから多くのアイテムを効率的に(パフォーマンスで)削除するにはどうすればよいですか?

非常に大きなリスト名のアイテム(> = 1,000,000アイテム)があり、削除するアイテムを選択する<cond>で示される条件があり、<cond>はリストのアイテムの多く(おそらく半分)に当てはまります。

私の目標は、<cond>によって選択されたアイテムを効率的に削除し、他のすべてのアイテムを保持することです。ソースリストを変更したり、新しいリストを作成したりできます。パフォーマンスを考慮して、それを行うための最良の方法を選択する必要があります。

これが私のテストコードです:

    System.out.println("preparing items");
    List<Integer> items = new ArrayList<Integer>(); // Integer is for demo
    for (int i = 0; i < 1000000; i++) {
        items.add(i * 3); // just for demo
    }

    System.out.println("deleting items");
    long startMillis = System.currentTimeMillis();
    items = removeMany(items);
    long endMillis = System.currentTimeMillis();

    System.out.println("after remove: items.size=" + items.size() + 
            " and it took " + (endMillis - startMillis) + " milli(s)");

素朴な実装:

public static <T> List<T> removeMany(List<T> items) {
    int i = 0;
    Iterator<T> iter = items.iterator();
    while (iter.hasNext()) {
        T item = iter.next();
        // <cond> goes here
        if (/*<cond>: */i % 2 == 0) {
            iter.remove();
        }
        i++;
    }
    return items;
}

ご覧のとおり、削除条件(<cond>)として2 == 0を法とするアイテムインデックスを使用しました。これは、デモンストレーションの目的でのみ使用します。

removeManyのどのようなより良いバージョンが提供される可能性があり、なぜこのより良いバージョンが実際に優れているのですか?

30
WildWezyr

さて、提案されたアプローチのテスト結果です。ここで私がテストしたアプローチ(各アプローチの名前は私のソースのクラス名でもあります):

  1. NaiveRemoveManyPerformer --ArrayListイテレータと削除-私の質問で与えられた最初の素朴な実装。
  2. BetterNaiveRemoveManyPerformer-ArrayListを逆方向に繰り返し、最後から最後まで削除します。
  3. LinkedRemoveManyPerformer-単純なイテレータと削除、ただしLinkedListの作業。欠点:LinkedListでのみ機能します。
  4. CreateNewRemoveManyPerformer-ArrayListはコピーとして作成され(保持されている要素のみが追加されます)、入力ArrayListをトラバースするためにイテレーターが使用されます。
  5. SmartCreateNewRemoveManyPerformer-より良いCreateNewRemoveManyPerformer-結果ArrayListの初期サイズ(容量)は最終リストサイズに設定されます。短所:開始時にリストの最終的なサイズを知っている必要があります。
  6. FasterSmartCreateNewRemoveManyPerformer-さらに良い(?)SmartCreateNewRemoveManyPerformer-イテレータの代わりにアイテムインデックス(items.get(idx))を使用します。
  7. MagicRemoveManyPerformer-ArrayListの所定の場所で機能し(リストのコピーなし)、リストの最後のアイテムから穴(削除されたアイテム)を圧縮します。欠点:このアプローチでは、リスト内のアイテムの順序が変更されます。
  8. ForwardInPlaceRemoveManyPerformer-ArrayListの代わりに機能します-保持アイテムを移動して穴を埋め、最後にsubListが返されます(最終的な削除やクリアはありません)。
  9. GuavaArrayListRemoveManyPerformer-GoogleグアバIterables.removeIfArrayListに使用されます-ForwardInPlaceRemoveManyPerformerとほぼ同じですが、リストの最後にあるアイテムを最終的に削除します。

完全なソースコードは、この回答の最後に記載されています。

さまざまなリストサイズ(10,000アイテムから10,000,000アイテム)およびさまざまな削除係数(リストから削除する必要のあるアイテムの数を指定)を使用して実行されたテスト。

他の回答に対するコメントでここに投稿したように、ArrayListから2番目のArrayListへのアイテムのコピーは、LinkedListを繰り返してアイテムを削除するよりも速いと思いました。 SunのJavaドキュメンテーションには、ArrayListの定数係数はLinkedList実装の定数係数と比較して低いと記載されていますが、驚いたことに、これは私の問題には当てはまりません。

実際には、単純な反復と削除を行うLinkedListがほとんどの場合に最高のパフォーマンスを発揮します(このアプローチはLinkedRemoveManyPerformerに実装されています)。通常、MagicRemoveManyPerformerのパフォーマンスのみがLinkedRemoveManyPerformerに匹敵しますが、他のアプローチは大幅に遅くなります。 Google Guava GuavaArrayListRemoveManyPerformerは、手作りの同様のコードよりも低速です(私のコードはリストの最後にある不要なアイテムを削除しないため)。

1,000,000個のソースアイテムから500,000個のアイテムを削除した結果の例:

  1. NaiveRemoveManyPerformer:テストは実行されません-私はその患者ではありませんが、BetterNaiveRemoveManyPerformerよりもパフォーマンスが劣ります。
  2. BetterNaiveRemoveManyPerformer:226080ミリ(秒)
  3. LinkedRemoveManyPerformer:69ミリ秒
  4. CreateNewRemoveManyPerformer:246ミリ秒
  5. SmartCreateNewRemoveManyPerformer:112ミリ秒
  6. FasterSmartCreateNewRemoveManyPerformer:202ミリ秒
  7. MagicRemoveManyPerformer:74ミリ(秒)
  8. ForwardInPlaceRemoveManyPerformer:69ミリ(秒)
  9. GuavaArrayListRemoveManyPerformer:118ミリ(秒)

1,000,000のソースアイテムから1つのアイテムを削除した結果の例(最初のアイテムが削除されます):

  1. BetterNaiveRemoveManyPerformer:34ミリ秒
  2. LinkedRemoveManyPerformer:41ミリ(秒)
  3. CreateNewRemoveManyPerformer:253ミリ秒
  4. SmartCreateNewRemoveManyPerformer:108ミリ秒
  5. FasterSmartCreateNewRemoveManyPerformer:71ミリ
  6. MagicRemoveManyPerformer:43ミリ秒
  7. ForwardInPlaceRemoveManyPerformer:73ミリ(秒)
  8. GuavaArrayListRemoveManyPerformer:78ミリ

1,000,000個のソースアイテムから333,334個のアイテムを削除した結果の例:

  1. BetterNaiveRemoveManyPerformer:253206ミリ秒
  2. LinkedRemoveManyPerformer:69ミリ
  3. CreateNewRemoveManyPerformer:245ミリ秒
  4. SmartCreateNewRemoveManyPerformer:111ミリ
  5. FasterSmartCreateNewRemoveManyPerformer:203ミリ秒
  6. MagicRemoveManyPerformer:69ミリ
  7. ForwardInPlaceRemoveManyPerformer:72ミリ秒
  8. GuavaArrayListRemoveManyPerformer:102ミリ

1,000,000のソースアイテムから1,000,000(すべて)のアイテムを削除した結果の例(すべてのアイテムは削除されますが、1つずつ処理され、すべてのアイテムが削除されることが事前にわかっている場合は、リストを単純にクリアする必要があります):

  1. BetterNaiveRemoveManyPerformer:58ミリ
  2. LinkedRemoveManyPerformer:88ミリ(秒)
  3. CreateNewRemoveManyPerformer:95ミリ秒
  4. SmartCreateNewRemoveManyPerformer:91ミリ
  5. FasterSmartCreateNewRemoveManyPerformer:48ミリ
  6. MagicRemoveManyPerformer:61ミリ秒
  7. ForwardInPlaceRemoveManyPerformer:49ミリ(秒)
  8. GuavaArrayListRemoveManyPerformer:133ミリ秒

私の最終的な結論:ハイブリッドアプローチを使用する-LinkedListを扱う場合-単純な反復と削除が最適であり、ArrayListを扱う場合-アイテムの順序が重要かどうかによって異なります-ForwardInPlaceRemoveManyPerformerを使用し、アイテムの順序が変更される可能性がある場合-最良の選択はMagicRemoveManyPerformerです。削除係数が事前にわかっている場合(削除されるアイテムと保持されるアイテムの数がわかっている場合)、特定の状況でさらに優れたパフォーマンスを発揮するアプローチを選択するために、さらにいくつかの条件を設定できます。しかし、既知の削除要素は通常のケースではありません... GoogleグアバIterables.removeIfはそのようなハイブリッドソリューションですが、仮定が少し異なります(元のリストを変更する必要があり、新しいリストを作成できず、アイテムの順序が常に重要です)-これらは最も一般的な仮定であるため、removeIfが最も現実的な最良の選択です-ライフケース。

また、すべての優れたアプローチ(ナイーブは良くありません!)で十分であることに注意してください。実際のアプリケーションでは、いずれも問題なく機能しますが、ナイーブなアプローチは避ける必要があります。

ついに-テスト用の私のソースコード。

package WildWezyrListRemovalTesting;

import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import Java.util.ArrayList;
import Java.util.Iterator;
import Java.util.LinkedList;
import Java.util.List;

public class RemoveManyFromList {

    public static abstract class BaseRemoveManyPerformer {

        protected String performerName() {
            return getClass().getSimpleName();
        }

        protected void info(String msg) {
            System.out.println(performerName() + ": " + msg);
        }

        protected void populateList(List<Integer> items, int itemCnt) {
            for (int i = 0; i < itemCnt; i++) {
                items.add(i);
            }
        }

        protected boolean mustRemoveItem(Integer itemVal, int itemIdx, int removeFactor) {
            if (removeFactor == 0) {
                return false;
            }
            return itemIdx % removeFactor == 0;
        }

        protected abstract List<Integer> removeItems(List<Integer> items, int removeFactor);

        protected abstract List<Integer> createInitialList();

        public void testMe(int itemCnt, int removeFactor) {
            List<Integer> items = createInitialList();
            populateList(items, itemCnt);
            long startMillis = System.currentTimeMillis();
            items = removeItems(items, removeFactor);
            long endMillis = System.currentTimeMillis();
            int chksum = 0;
            for (Integer item : items) {
                chksum += item;
            }
            info("removing took " + (endMillis - startMillis)
                    + " milli(s), itemCnt=" + itemCnt
                    + ", removed items: " + (itemCnt - items.size())
                    + ", remaining items: " + items.size()
                    + ", checksum: " + chksum);
        }
    }
    private List<BaseRemoveManyPerformer> rmps =
            new ArrayList<BaseRemoveManyPerformer>();

    public void addPerformer(BaseRemoveManyPerformer rmp) {
        rmps.add(rmp);
    }
    private Runtime runtime = Runtime.getRuntime();

    private void runGc() {
        for (int i = 0; i < 5; i++) {
            runtime.gc();
        }
    }

    public void testAll(int itemCnt, int removeFactor) {
        runGc();
        for (BaseRemoveManyPerformer rmp : rmps) {
            rmp.testMe(itemCnt, removeFactor);
        }
        runGc();
        System.out.println("\n--------------------------\n");
    }

    public static class NaiveRemoveManyPerformer
            extends BaseRemoveManyPerformer {

        @Override
        public List<Integer> removeItems(List<Integer> items, int removeFactor) {
            if (items.size() > 300000 && items instanceof ArrayList) {
                info("this removeItems is too slow, returning without processing");
                return items;
            }
            int i = 0;
            Iterator<Integer> iter = items.iterator();
            while (iter.hasNext()) {
                Integer item = iter.next();
                if (mustRemoveItem(item, i, removeFactor)) {
                    iter.remove();
                }
                i++;
            }
            return items;
        }

        @Override
        public List<Integer> createInitialList() {
            return new ArrayList<Integer>();
        }
    }

    public static class BetterNaiveRemoveManyPerformer
            extends NaiveRemoveManyPerformer {

        @Override
        public List<Integer> removeItems(List<Integer> items, int removeFactor) {
//            if (items.size() > 300000 && items instanceof ArrayList) {
//                info("this removeItems is too slow, returning without processing");
//                return items;
//            }

            for (int i = items.size(); --i >= 0;) {
                Integer item = items.get(i);
                if (mustRemoveItem(item, i, removeFactor)) {
                    items.remove(i);
                }
            }
            return items;
        }
    }

    public static class LinkedRemoveManyPerformer
            extends NaiveRemoveManyPerformer {

        @Override
        public List<Integer> createInitialList() {
            return new LinkedList<Integer>();
        }
    }

    public static class CreateNewRemoveManyPerformer
            extends NaiveRemoveManyPerformer {

        @Override
        public List<Integer> removeItems(List<Integer> items, int removeFactor) {
            List<Integer> res = createResultList(items, removeFactor);
            int i = 0;

            for (Integer item : items) {
                if (mustRemoveItem(item, i, removeFactor)) {
                    // no-op
                } else {
                    res.add(item);
                }
                i++;
            }

            return res;
        }

        protected List<Integer> createResultList(List<Integer> items, int removeFactor) {
            return new ArrayList<Integer>();
        }
    }

    public static class SmartCreateNewRemoveManyPerformer
            extends CreateNewRemoveManyPerformer {

        @Override
        protected List<Integer> createResultList(List<Integer> items, int removeFactor) {
            int newCapacity = removeFactor == 0 ? items.size()
                    : (int) (items.size() * (removeFactor - 1L) / removeFactor + 1);
            //System.out.println("newCapacity=" + newCapacity);
            return new ArrayList<Integer>(newCapacity);
        }
    }

    public static class FasterSmartCreateNewRemoveManyPerformer
            extends SmartCreateNewRemoveManyPerformer {

        @Override
        public List<Integer> removeItems(List<Integer> items, int removeFactor) {
            List<Integer> res = createResultList(items, removeFactor);

            for (int i = 0; i < items.size(); i++) {
                Integer item = items.get(i);
                if (mustRemoveItem(item, i, removeFactor)) {
                    // no-op
                } else {
                    res.add(item);
                }
            }

            return res;
        }
    }

    public static class ForwardInPlaceRemoveManyPerformer
            extends NaiveRemoveManyPerformer {

        @Override
        public List<Integer> removeItems(List<Integer> items, int removeFactor) {
            int j = 0; // destination idx
            for (int i = 0; i < items.size(); i++) {
                Integer item = items.get(i);
                if (mustRemoveItem(item, i, removeFactor)) {
                    // no-op
                } else {
                    if (j < i) {
                        items.set(j, item);
                    }
                    j++;
                }
            }

            return items.subList(0, j);
        }
    }

    public static class MagicRemoveManyPerformer
            extends NaiveRemoveManyPerformer {

        @Override
        public List<Integer> removeItems(List<Integer> items, int removeFactor) {
            for (int i = 0; i < items.size(); i++) {
                if (mustRemoveItem(items.get(i), i, removeFactor)) {
                    Integer retainedItem = removeSomeFromEnd(items, removeFactor, i);
                    if (retainedItem == null) {
                        items.remove(i);
                        break;
                    }
                    items.set(i, retainedItem);
                }
            }

            return items;
        }

        private Integer removeSomeFromEnd(List<Integer> items, int removeFactor, int lowerBound) {
            for (int i = items.size(); --i > lowerBound;) {
                Integer item = items.get(i);
                items.remove(i);
                if (!mustRemoveItem(item, i, removeFactor)) {
                    return item;
                }
            }
            return null;
        }
    }

    public static class GuavaArrayListRemoveManyPerformer
            extends BaseRemoveManyPerformer {

        @Override
        protected List<Integer> removeItems(List<Integer> items, final int removeFactor) {
            Iterables.removeIf(items, new Predicate<Integer>() {

                public boolean apply(Integer input) {
                    return mustRemoveItem(input, input, removeFactor);
                }
            });

            return items;
        }

        @Override
        protected List<Integer> createInitialList() {
            return new ArrayList<Integer>();
        }
    }

    public void testForOneItemCnt(int itemCnt) {
        testAll(itemCnt, 0);
        testAll(itemCnt, itemCnt);
        testAll(itemCnt, itemCnt - 1);
        testAll(itemCnt, 3);
        testAll(itemCnt, 2);
        testAll(itemCnt, 1);
    }

    public static void main(String[] args) {
        RemoveManyFromList t = new RemoveManyFromList();
        t.addPerformer(new NaiveRemoveManyPerformer());
        t.addPerformer(new BetterNaiveRemoveManyPerformer());
        t.addPerformer(new LinkedRemoveManyPerformer());
        t.addPerformer(new CreateNewRemoveManyPerformer());
        t.addPerformer(new SmartCreateNewRemoveManyPerformer());
        t.addPerformer(new FasterSmartCreateNewRemoveManyPerformer());
        t.addPerformer(new MagicRemoveManyPerformer());
        t.addPerformer(new ForwardInPlaceRemoveManyPerformer());
        t.addPerformer(new GuavaArrayListRemoveManyPerformer());

        t.testForOneItemCnt(1000);
        t.testForOneItemCnt(10000);
        t.testForOneItemCnt(100000);
        t.testForOneItemCnt(200000);
        t.testForOneItemCnt(300000);
        t.testForOneItemCnt(500000);
        t.testForOneItemCnt(1000000);
        t.testForOneItemCnt(10000000);
    }
}
37
WildWezyr

他の人が言っているように、あなたの最初の傾向は、2番目のリストを作成することです。

ただし、リストのインプレース編集も試してみたい場合、効率的な方法は、Guavaの Iterables.removeIf() を使用することです。その引数がリストの場合、保持されている要素を前に向かって合体させてから、単に最後を切り落とします。これは、内部要素を1つずつ削除するよりもはるかに高速です。

11

ArrayListから多くの要素を削除することは、O(n^2)操作です。挿入と削除に最適化されたLinkedListを使用することをお勧めします(ただし、ランダムアクセスには最適化されていません)。 LinkedListには、メモリのオーバーヘッドが少しあります。

ArrayListを保持する必要がある場合は、新しいリストを作成することをお勧めします。

更新:新しいリストの作成との比較:

同じリストを再利用する場合、主なコストは、ノードを削除し、LinkedList内の適切なポインターを更新することです。これは、任意のノードの定数操作です。

新しいリストを作成する場合の主なコストは、リストの作成と配列エントリの初期化です。どちらも安価な操作です。新しいリストバックエンド配列のサイズを変更するコストも発生する可能性があります。最終的な配列が入力配列の半分より大きいと仮定します。

したがって、要素を1つだけ削除する場合は、LinkedListアプローチの方がおそらく高速です。 1つを除くすべてのノードを削除する場合は、おそらく新しいリストアプローチの方が高速です。

メモリ管理とGCを導入すると、さらに複雑になります。これらは省略したいと思います。

最良のオプションは、代替案を自分で実装し、通常の負荷を実行するときに結果をベンチマークすることです。

6
notnoop

リストの途中からアイテムを削除するのは非常にコストがかかるため、アイテムを追加するために新しいListを作成します。

public static List<T> removeMany(List<T> items) {
    List<T> tempList = new ArrayList<T>(items.size()/2); //if about half the elements are going to be removed
    Iterator<T> iter = items.iterator();
    while (item : items) {
        // <cond> goes here
        if (/*<cond>: */i % 2 != 0) {
            tempList.add(item);
        }
    }
    return tempList;
}

編集:私はこれをテストしていないので、小さな構文エラーがある可能性があります。

2番目の編集:ランダムアクセスは必要ないが追加時間が速い場合は、LinkedListを使用する方が適切です。

しかし...

ArrayListの定数係数はLinkedListの定数係数よりも小さい( 参照 )。削除される要素の数を合理的に推測できるため(質問で「約半分」と言った)、ArrayListの末尾に要素を追加するとO(1)再割り当てする必要がない限り、それで、can合理的な推測をすると、ArrayListはわずかに速いと思います。ほとんどの場合、LinkedList(これは私が投稿したコードに適用されます。あなたの素朴な実装では、LinkedListの方が速いと思います)。

5
Chinmay Kanchi

申し訳ありませんが、これらの答えはすべて要点を欠いていると思います。おそらく、リストを使用する必要はなく、使用すべきではないでしょう。

この種の「クエリ」が一般的である場合は、すべてのデータノードをトラバースする必要がない順序付けられたデータ構造を構築してみませんか?問題については十分に説明していませんが、単純なツリーを提供する例を考えると、うまくいく可能性があります。アイテムごとに挿入のオーバーヘッドがありますが、一致するノードを含むサブツリーをすばやく見つけることができるため、現在行っている比較のほとんどを回避できます。

さらに:

  • 正確な問題と設定した正確なデータ構造に応じて、削除を高速化できます。強制終了するノードがサブツリーなどに縮小された場合は、drop多数のリストノード全体を更新するのではなく、そのサブツリー。

  • リストアイテムを削除するたびに、ポインタを更新しています-例:lastNode.nextおよびnextNode.prevなど-しかし、それが判明した場合は、削除する必要もありますnextNodeの場合、発生したポインタの更新は、新しい更新によって破棄されます。)

2
fullreset

特にアイテムの数が指定した数だけ多い場合は、既存のリストを変更するのではなく、新しいリストを作成する方がパフォーマンスが高いと思います。これは、リストがArrayListではなくLinkedListであることを前提としています。非円形のLinkedListの場合、挿入はO(n)ですが、既存のイテレータ位置での削除はO(1)です。その場合、ナイーブなアルゴリズムは十分に機能するはずです。

リストがLinkedListでない限り、remove()を呼び出すたびにリストをシフトするコストは、実装の中で最もコストのかかる部分の1つである可能性があります。配列リストの場合、次の使用を検討します。

public static <T> List<T> removeMany(List<T> items) {
    List<T> newList = new ArrayList<T>(items.size());
    Iterator<T> iter = items.iterator();
    while (iter.hasNext()) {
        T item = iter.next();
        // <cond> goes here
        if (/*<cond>: */i++ % 2 != 0) {
            newList.add(item);
        }
    }
    return newList;
}
2
LBushkin

速度が最も重要なメトリックであるため、より多くのメモリを使用し、リストの再作成を少なくする可能性があります(私のコメントで述べたように)。ただし、実際のパフォーマンスへの影響は、機能の使用方法に完全に依存します。

アルゴリズムは、次の少なくとも1つが当てはまると想定しています。

  • 元のリストのすべての要素をテストする必要はありません。これは、条件に一致するすべての要素ではなく、条件に一致する最初のN個の要素を実際に探している場合に発生する可能性があります。
  • リストを新しいメモリにコピーする方がコストがかかります。これは、元のリストが割り当てられたメモリの50%以上を使用している場合に発生する可能性があるため、インプレースでの作業が改善されるか、メモリ操作が遅くなることが判明した場合(予期しない結果になります)。
  • リストから要素を削除する速度のペナルティは大きすぎて一度にすべてを受け入れることはできませんが、全体のペナルティが一度にすべてを取得するよりも大きい場合でも、そのペナルティを複数の操作に分散することは許容されます。これは、20万ドルの住宅ローンを借りるようなものです。30年間月額1000ドルを支払うことは、月額ベースで手頃な価格であり、ローンの全期間にわたって全体の支払いが36万であるにもかかわらず、住宅と株式を所有するという利点があります。

免責事項:prolly構文エラーがあります-私は何もコンパイルしようとしませんでした。

まず、ArrayListをサブクラス化します

 public class ConditionalArrayList extends ArrayList {
 
 public Iterator iterator(Condition condition)
 {
 return listIterator(condition); 
 } 
 
 public ListIterator listIterator(Condition condition)
 {
 return new ConditionalArrayListIterator(this.iterator()、condition); 
} 
 
 public ListIterator listIterator(){return iterator(); } 
 public iterator(){
 throw new InvalidArgumentException( "イテレータの条件を指定する必要があります"); 
} 
} 

次に、ヘルパークラスが必要です。

 public classConditionalArrayListIteratorはListIteratorを実装します
 {
 private ListIterator listIterator; 
条件条件; 
 
 //次の2つのフラグは次のとおりです。 
 //既知の良好な要素に対するテストを不必要に繰り返さないように、迅速な最適化として使用されます。
 boolean nextKnownGood = false; 
 boolean prevKnownGood = false; 
 
 public ConditionalArrayListIterator(ListIterator listIterator、Condition condition)
 {
 this.listIterator = listIterator; 
 this.condition = condition; 
} 
 
 public void add(Object o){listIterator.add(o); } 
 
 /**
 *これは、
を呼び出すのは非常に非効率的であることに注意してください。* 
 * 
 * 2つの一致する要素の間に一致しない要素がたくさんあります。
 */
 public boolean hasNext()
 {
 if [nextKnownGood )return true; 
 
/* 
 *がある場合、条件に一致するリスト内の次のオブジェクトを検索します。
 */
 while (!listIterator.hasNext())
 {
 Object next = listIterator.next(); 
 if(condition.matches(next)){
 listIterator。 set(next); 
 nextKnownGood = true; 
 return true; 
} 
} 
 
 nextKnownGood = false; 
 //一致する要素が見つかりませんでした。
 return false; 
} 
 
/** 
 
 * hasNext()のコピーと貼り付け。[.___ _。] */
 public boolean hasPrevious()
 {
 if(prevKnownGood)return true; 
 
/*次のオブジェクトを検索
 *条件に一致するリスト(ある場合)。
 * /
 while(! listIterator.hasPrevious())
 {
 Object prev = listIterator.next(); 
 if(condition.matches(prev)){
 prevKnownGood = true; 
 listIterator.set(prev); 
 return true; 
} 
} 
 
 //一致する要素が見つかりませんでした。 
 prevKnwonGood = false; 
 return false; 
} 
 
/** hasNext()を参照して効率に関するメモ**/
 public Object next()
 {
 if(nextKnownGood || hasNext())
 {
 prevKnownGood = nextKnownGood; 
 nextKnownGood = false; 
 return listIterator.next(); 
} 
 
 throw NoSuchElementException( "No moreマッチング要素"); 
} 
 
/**効率に関する注意事項についてはhasNext()を参照してください。 next()のコピー&ペースト**/
publicObject previous()
 {
 if(prevKnownGood || hasPrevious())
 {
 nextKnownGood = prevKnownGood; 
 prevKnownGood = false; 
 return listIterator.previous(); 
} 
 throw NoSuchElementException( "No more matching elements"); 
} 
 
/** 
 * nextIndex ()およびpreviousIndex()は、このクラスが返した結果の数ではなく、値の配列インデックス
 *を返します。
 *これが適切でない場合は、独自の値を維持してください現在のインデックスと
 * next()およびprevious()の増分または減分
 */
 public int nextIndex(){return listIterator.previousIndex(); } 
 public int previousIndex(){return listIterator.previousIndex(); } 
 
 public remove(){listIterator.remove(); } 
 public set(Object o){listIterator.set(o); } 
} 

そしてもちろん、条件インターフェースが必要です。

/**コンパレータによく似ています... **/
publicinterface Condition 
 {
 public boolean matches(Object obj); 
 } 

そして、テストする条件

パブリッククラスIsEvenCondition {
 {
 public booleanmatches(Object obj){return(Number(obj))。intValue()%2 == 0; 
 } 

そして、最終的にいくつかのテストコードの準備が整いました

 
条件条件= new IsEvenCondition(); 
 
 System.out.println( "preparing items"); 
 startMillis = System.currentTimeMillis (); 
 List <Integer> items = new ArrayList <Integer>(); //整数はdemo 
 for(int i = 0; i <1000000; i ++){
 items.add(i * 3); //デモ用
} 
 endMillis = System.currentTimeMillis(); 
 System.out.println( "準備に" +(endmillis-startmillis)+ "かかりましたlist。 "); 
 
 System.out.println(" deleting items "); 
 startMillis = System.currentTimeMillis(); 
 //しませんtこのリストから実際に削除するため、
 // removeManyは事実上「瞬時」
 // items = removeMany(items); 
 endMillis = System.currentTimeMillis(); 
 System.out.println( "after remove:items.size =" + items.size()+ 
 "そしてそれは" +(endMillis-startMillis)+ "milli(s)")かかりました; 
 System.out.println( "->注:実際には何も削除されません。このアルゴリズムは、元のリストの変更や複製を避けるために、余分な" 
 + "メモリを使用します。"); 
 
 System.out.println( "リストを反復処理しようとしています"); 
 startMillis = System.currentTimeMillis(); 
 int count = iterate(items、condition); 
 endMillis = System.currentTimeMillis(); 
 System.out.println( "反復後:items.size =" + items.size()+ 
 "count =" + count + "そしてそれは" +(endMillis-startMillis)+ "milli(s)"); 
 System.out.println( "->注:これは多少必要です非効率的です。 "
 +"主に複数のクラスのオーバーヘッドが原因です。 "
 +"このアルゴリズムは、 "
 +"すべての要素が含まれるアルゴリズムよりも高速になるように設計(期待)されています。 "); 
 
 System.out.println("リストの反復処理について "); 
 startMillis = System.currentTimeMillis(); 
 int total = addFirst(30、items、condition); 
 endMillis = System.currentTimeMillis(); 
 System.out.println( "最初の30要素を合計した後:total =" +合計+ 
 "そして、" +(endMillis-startMillis)+ "ミリ秒"); 
 
 ... 
 
 private int iterate(List <Integer> items、Condition condition)
 {
 // i ++と戻り値JVMの最適化を実際に防ぐためのものです。
 //-安全のためだけに使用します。
イテレータiter = items.listIterator(condition); 
 for(int i = 0; iter.hasNext()); i ++){iter.next(); } 
 return i; 
} 
 
 private int addFirst(int n、List <Integer> items、Condition condition)
 {
 int total = 0; 
 Iterator iter = items.listIterator(condition); 
 for(int i = 0; i <n; i ++)
 {
合計+ =((整数)iter.next())。intValue(); 
} 
} 
 
1
atk

Apache Commons Collections を使用します。具体的には この関数 。これは、基本的に、実装を提案しているのと同じ方法で実装されます(つまり、新しいリストを作成してから追加します)。

1
Jherico

LinkedListの代わりに、ArrayListを使用することもできます。これは、ArrayListの場合と同様に、リスト内から要素を削除する場合は、他のすべての要素をコピーする必要があります。

1
Fabian Steeg

すでにかなり長い私の最初の答えを濁らせるのではなく、2番目の関連オプションを示します。独自のArrayListを作成し、「削除済み」としてフラグを付けることができます。このアルゴリズムは、次のことを前提としています。

  • 撤去作業中に同じことをするよりも、建設中に時間を無駄にする(低速にする)方が良いです。つまり、速度ペナルティをある場所から別の場所に移動します。
  • 今すぐメモリを浪費し、結果の計算後にガベージコレクションに時間を費やすよりも、前もって時間を費やす方がよいでしょう(ガベージコレクションの時間に常に悩まされています...)。
  • 削除が開始されると、要素がリストに追加されることはありません(そうしないと、flagsオブジェクトの再割り当てに問題があります)

また、これもテストされていないため、構文エラーがあります。

 public class FlaggedList extends ArrayList {
 private Vector <Boolean> flags = new ArrayList(); 
 private static final String IN = Boolean.TRUE; //削除されません
 private static final String OUT = Boolean.FALSE; //削除
 privateint削除= 0; 
 
 public MyArrayList(){this(1000000); } 
 public MyArrayList(intestimate){
 super(estimate); 
 flags = new ArrayList(estimate); 
} 
 
 public void remove(int idx){
 flags.set(idx、OUT); 
 deleted ++; 
} 
 
 public boolean isRemoved (int idx){return flags.get(idx); } 
} 

そしてイテレータ-同期を維持するためにさらに作業が必要になる可能性があり、今回は多くのメソッドが省略されています。

 public classFlaggedListIteratorはListIteratorを実装します
 {
 int idx = 0; 
 
 public FlaggedList list; 
 public FlaggedListIterator(FlaggedList list )
 {
 this.list = list; 
} 
 public boolean hasNext(){
 while(idx <list.size()&& list.isRemoved(idx ++)); 
 return idx <list.size(); 
} 
} 
0
atk

たぶん、リストはあなたにとって最適なデータ構造ではありませんか?変更できますか?おそらく、1つのノードを削除すると、条件を満たすすべてのアイテムが削除されるようにアイテムが並べ替えられたツリーを使用できますか?それとも、少なくとも操作をスピードアップしますか?

2つのリスト(1つはi%2!= 0が真である項目、もう1つはi%2!= 0が偽である項目)を使用する単純な例では、うまく機能する可能性があります。しかし、これはもちろん非常にドメインに依存しています。

0
Arne Deutsch