簡體   English   中英

如何有效(性能)從Java中的List中刪除許多項?

[英]How to efficiently (performance) remove many items from List in Java?

我有很大的List命名項(> = 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;
}

如您所見,我使用項目索引模2 == 0作為刪除條件(<cond>) - 僅用於演示目的。

可以提供什么更好的removeMany版本以及為什么這個更好的版本實際上更好?

好的,現在是提出方法測試結果的時候了。 這里我測試了哪些方法(每個方法的名稱在我的源代碼中也是類名):

  1. NaiveRemoveManyPerformer - 帶有迭代器和刪除的ArrayList - 在我的問題中給出的第一個和天真的實現。
  2. BetterNaiveRemoveManyPerformer - 具有向后迭代並從端到端移除的ArrayList
  3. LinkedRemoveManyPerformer - 天真的迭代器和刪除但在LinkedList工作。 Disadventage:僅適用於LinkedList
  4. CreateNewRemoveManyPerformer - 將ArrayList作為副本(僅添加保留元素),迭代器用於遍歷輸入ArrayList
  5. SmartCreateNewRemoveManyPerformer - 更好的CreateNewRemoveManyPerformer - 結果ArrayList初始大小(容量)設置為最終列表大小。 缺點:啟動時必須知道列表的最終大小。
  6. FasterSmartCreateNewRemoveManyPerformer - 甚至更好(?) SmartCreateNewRemoveManyPerformer - 使用項索引( items.get(idx) )而不是迭代器。
  7. MagicRemoveManyPerformer - 為ArrayList工作(沒有列表副本),並從列表末尾的項目開始壓縮孔(已刪除的項目)。 Disadventage:此方法更改列表中項目的順序。
  8. ForwardInPlaceRemoveManyPerformer - 為ArrayList - 移動保留項以填充空洞,最后返回subList(無最終刪除或清除)。
  9. GuavaArrayListRemoveManyPerformer - Google Guava Iterables.removeIf用於ArrayList - 幾乎與ForwardInPlaceRemoveManyPerformer相同,但最終刪除列表末尾的項目。

完整的源代碼在本答案的最后給出。

測試使用不同列表大小(從10,000個項目到10,000,000個項目)執行的測試和不同的刪除因子(指定必須從列表中刪除多少項目)。

正如我在評論中發布的其他答案 - 我認為將項目從ArrayList復制到第二個ArrayList將比迭代LinkedList更快,只是刪除項目。 Sun的Java文檔說,與LinkedList實現相比, ArrayList常量因子較低,但令人驚訝的是,在我的問題中並非如此。

實際上,在大多數情況下,具有簡單迭代和刪除功能的LinkedList具有最佳性能(此方法在LinkedRemoveManyPerformer實現)。 通常只有MagicRemoveManyPerformer性能可與LinkedRemoveManyPerformer相媲美,其他方法都明顯變慢。 Google Guava GuavaArrayListRemoveManyPerformer比手工制作的類似代碼慢(因為我的代碼不會刪除列表末尾的不必要的項目)。

從1,000,000個源項目中刪除500,000個項目的示例結果:

  1. NaiveRemoveManyPerformer :測試未執行 - 我不是那么耐心,但它的表現比BetterNaiveRemoveManyPerformer更差。
  2. BetterNaiveRemoveManyPerformer :226080毫(s)
  3. LinkedRemoveManyPerformer :69毫(s)
  4. CreateNewRemoveManyPerformer :246毫(s)
  5. SmartCreateNewRemoveManyPerformer :112毫(s)
  6. FasterSmartCreateNewRemoveManyPerformer :202 milli(s)
  7. MagicRemoveManyPerformer :74毫(s)
  8. ForwardInPlaceRemoveManyPerformer :69毫(s)
  9. GuavaArrayListRemoveManyPerformer :118 milli(s)

從1,000,000個源項目中刪除1個項目的示例結果(第一個項目已刪除):

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

從1,000,000個源項目中刪除333,334個項目的示例結果:

  1. BetterNaiveRemoveManyPerformer:253206毫(s)
  2. LinkedRemoveManyPerformer:69毫(s)
  3. CreateNewRemoveManyPerformer:245毫(s)
  4. SmartCreateNewRemoveManyPerformer:111毫(s)
  5. FasterSmartCreateNewRemoveManyPerformer:203 milli(s)
  6. MagicRemoveManyPerformer:69毫(s)
  7. ForwardInPlaceRemoveManyPerformer:72 milli(s)
  8. GuavaArrayListRemoveManyPerformer:102毫(s)

從1,000,000個源項目中刪除1,000,000(全部)項目的示例結果(所有項目都被刪除但是通過逐個處理,如果您事先知道要刪除所有項目,則應該簡單地清除列表):

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

我的最終結論是:使用混合方法 - 如果處理LinkedList - 簡單的迭代和刪除是最好的,如果處理ArrayList - 它取決於項目順序是否重要 - 然后使用ForwardInPlaceRemoveManyPerformer,如果項目順序可能被更改 - 最佳選擇是MagicRemoveManyPerformer。 如果刪除因子是先驗已知的(您知道將刪除多少項目與保留),那么可以選擇更多條件來選擇在特定情況下表現更好的方法。 但是已知的刪除因素不是常見的情況......谷歌Guava 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);
    }
}

正如其他人所說,你的第一個傾向應該是建立第二個清單。

但是,如果您還想嘗試就地編輯列表,那么有效的方法是使用Guava的Iterables.removeIf() 如果它的參數是一個列表,它會將保留的元素合並到前面,然后簡單地切掉末尾 - 比逐個刪除()內部元素要快得多。

ArrayList刪除大量元素是O(n^2)操作。 我建議只使用一個更適合插入和刪除的LinkedList (但不是隨機訪問)。 LinkedList有一點內存開銷。

如果確實需要保留ArrayList ,那么最好創建一個新列表。

更新:與創建新列表相比:

重用相同的列表,主要成本來自刪除節點和更新LinkedList中的相應指針。 這是任何節點的常量操作。

構建新列表時,主要成本來自創建列表和初始化數組條目。 兩者都是廉價的操作。 您可能還要承擔調整新列表后端陣列大小的成本; 假設最終數組大於傳入數組的一半。

因此,如果您只刪除一個元素,那么LinkedList方法可能更快。 如果要刪除除1之外的所有節點,則新列表方法可能更快。

引入內存管理和GC時會出現更多復雜情況。 我想把它們留下來。

最佳選擇是自己實施替代方案,並在運行典型負載時對結果進行基准測試。

我會創建一個新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;
}

編輯:我沒有測試過這個,所以可能會有很小的語法錯誤。

第二次編輯:當您不需要隨機訪問但快速添加時,使用LinkedList會更好。

但...

ArrayList的常量因子小於LinkedListRef )的常量因子。 既然您可以合理地猜測將刪除多少元素(在您的問題中說“大約一半”),只要您不必重新添加元素到ArrayList的末尾就是O(1) - 分配它。 所以,如果你做出合理的猜測,我希望在大多數情況下, ArrayListLinkedList略快。 (這適用於我發布的代碼。在你天真的實現中,我認為LinkedList會更快)。

我認為構建一個新列表而不是修改現有列表會更有效 - 特別是當項目數量與您指示的一樣大時。 這假定,您的列表是ArrayList ,而不是LinkedList 對於非圓形LinkedList ,插入是O(n),但是在現有迭代器位置的移除是O(1); 在這種情況下,你的天真算法應該足夠高效。

除非列表是LinkedList ,否則每次調用remove()時移動列表的成本可能是實現中最昂貴的部分之一。 對於數組列表,我會考慮使用:

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

對不起,但所有這些答案都沒有提到,我想:你可能沒有,也可能不應該使用List。

如果這種“查詢”很常見,為什么不構建一個有序的數據結構,消除了遍歷所有數據節點的需要? 你沒有告訴我們關於這個問題的充分信息,但考慮到你提供一個簡單樹的例子可以解決問題。 每個項目都有一個插入開銷,但您可以非常快速地找到包含匹配節點的子樹,因此您可以避免現在正在進行的大部分比較。

此外:

  • 根據確切的問題以及您設置的確切數據結構,您可以加快刪除速度 - 如果要殺死的節點確實減少到子樹或某種類型,您只需刪除該子樹,而不是更新整個列表節點。

  • 每次刪除一個列表項時,你都在更新指針 - 例如lastNode.nextnextNode.prev或者其他東西 - 但是如果事實證明你也想要刪除nextNode ,那么你剛才引起的指針更新會被拋棄一個新的更新。)

您可以嘗試的一件事是使用LinkedList而不是ArrayList ,與ArrayList一樣,如果從列表中刪除元素,則需要復制所有其他元素。

使用Apache Commons Collections 特別是這個功能 這實現方式與人們建議您實現它的方式基本相同(即創建一個新列表然后添加到它中)。

由於速度是最重要的指標,因此可以使用更多內存並減少列表重放(如我的評論中所述)。 但實際的性能影響完全取決於功能的使用方式。

該算法假定至少滿足下列條件之一:

  • 原始列表的所有元素都不需要測試。 如果我們真的在尋找符合我們條件的前N個元素,而不是符合我們條件的所有元素,就會發生這種情況。
  • 將列表復制到新內存中的成本更高。 如果原始列表使用超過50%的已分配內存,則可能會發生這種情況,因此就地工作可能會更好,或者內存操作變得更慢(這將是意外結果)。
  • 從列表中刪除元素的速度懲罰太大而不能立即接受,但是在多個操作中傳播該懲罰是可以接受的,即使總懲罰大於一次取得所有懲罰。 這就像拿出20萬美元的抵押貸款一樣:30美元每月支付1000美元,每月可以負擔得起,並擁有擁有住房和股權的好處,即使在貸款期限內總支付額為360K。

免責聲明:有很多語法錯誤 - 我沒有嘗試編譯任何東西。

首先,繼承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("You must specify a condition for the iterator"); 
  }
}

然后我們需要輔助類:

public class ConditionalArrayListIterator implements ListIterator
{
  private ListIterator listIterator;
  Condition condition;

  // the two following flags are used as a quick optimization so that 
  // we don't repeat tests on known-good elements unnecessarially.
  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); }

  /**
   * Note that this it is extremely inefficient to 
   * call hasNext() and hasPrev() alternatively when
   * there's a bunch of non-matching elements between
   * two matching elements.
   */
  public boolean hasNext()
  { 
     if( nextKnownGood ) return true;

     /* find the next object in the list that 
      * matches our condition, if any.
      */
     while( ! listIterator.hasNext() )
     {
       Object next = listIterator.next();
       if( condition.matches(next) ) {
         listIterator.set(next);
         nextKnownGood = true;
         return true;
       }
     }

     nextKnownGood = false;
     // no matching element was found.
     return false;
  }

  /**
   *  See hasPrevious for efficiency notes.
   *  Copy & paste of hasNext().
   */
  public boolean hasPrevious()
  { 
     if( prevKnownGood ) return true;

     /* find the next object in the list that 
      * matches our condition, if any.
      */
     while( ! listIterator.hasPrevious() )
     {
       Object prev = listIterator.next();
       if( condition.matches(prev) ) {
         prevKnownGood = true;
         listIterator.set(prev);
         return true;
       }
     }

     // no matching element was found.
     prevKnwonGood = false;
     return false;
  }

  /** see hasNext() for efficiency note **/
  public Object next()
  {
     if( nextKnownGood || hasNext() ) 
     { 
       prevKnownGood = nextKnownGood;
       nextKnownGood = false;
       return listIterator.next();
     }

     throw NoSuchElementException("No more matching elements");
  }

  /** see hasNext() for efficiency note; copy & paste of next() **/
  public Object previous()
  {
     if( prevKnownGood || hasPrevious() ) 
     { 
       nextKnownGood = prevKnownGood;
       prevKnownGood = false;
       return listIterator.previous();                        
     }
     throw NoSuchElementException("No more matching elements");
  }

  /** 
   * Note that nextIndex() and previousIndex() return the array index
   * of the value, not the number of results that this class has returned.
   * if this isn't good for you, just maintain your own current index and
   * increment or decriment in next() and previous()
   */
  public int nextIndex(){ return listIterator.previousIndex(); }
  public int previousIndex(){ return listIterator.previousIndex(); }

  public remove(){ listIterator.remove(); }
  public set(Object o) { listIterator.set(o); }
}

當然,我們需要條件接口:

/** much like a comparator... **/
public interface Condition
{
  public boolean matches(Object obj);
}

以及測試的條件

public class IsEvenCondition {
{
  public boolean matches(Object obj){ return (Number(obj)).intValue() % 2 == 0;
}

我們終於准備好了一些測試代碼

Condition condition = new IsEvenCondition();

    System.out.println("preparing items");
    startMillis = System.currentTimeMillis();
    List<Integer> items = new ArrayList<Integer>(); // Integer is for demo
    for (int i = 0; i < 1000000; i++) {
        items.add(i * 3); // just for demo
    }
    endMillis = System.currentTimeMillis();
    System.out.println("It took " + (endmillis-startmillis) + " to prepare the list. ");

    System.out.println("deleting items");
    startMillis = System.currentTimeMillis();
    // we don't actually ever remove from this list, so 
    // removeMany is effectively "instantaneous"
    // items = removeMany(items);
    endMillis = System.currentTimeMillis();
    System.out.println("after remove: items.size=" + items.size() + 
            " and it took " + (endMillis - startMillis) + " milli(s)");
    System.out.println("--> NOTE: Nothing is actually removed.  This algorithm uses extra"
                       + " memory to avoid modifying or duplicating the original list.");

    System.out.println("About to iterate through the list");
    startMillis = System.currentTimeMillis();
    int count = iterate(items, condition);
    endMillis = System.currentTimeMillis();
    System.out.println("after iteration: items.size=" + items.size() + 
            " count=" + count + " and it took " + (endMillis - startMillis) + " milli(s)");
    System.out.println("--> NOTE: this should be somewhat inefficient."
                       + " mostly due to overhead of multiple classes."
                       + " This algorithm is designed (hoped) to be faster than "
                       + " an algorithm where all elements of the list are used.");

    System.out.println("About to iterate through the list");
    startMillis = System.currentTimeMillis();
    int total = addFirst(30, items, condition);
    endMillis = System.currentTimeMillis();
    System.out.println("after totalling first 30 elements: total=" + total + 
            " and it took " + (endMillis - startMillis) + " milli(s)");

...

private int iterate(List<Integer> items, Condition condition)
{
  // the i++ and return value are really to prevent JVM optimization
  // - just to be safe.
  Iterator 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++)
  {
    total += ((Integer)iter.next()).intValue();
  }
}

也許List不是您的最佳數據結構? 你能改變嗎? 也許您可以使用一個樹,其中項目的排序方式是刪除一個節點刪除滿足條件的所有項目? 或者至少可以加快您的運營速度?

在你的簡單示例中,使用兩個列表(一個包含i%2!= 0的項為真,另一個包含i%2!= 0的項為false)可以很好地服務。 但這當然非常依賴於域名。

而不是混淆我的第一個答案,這已經很長了,這是第二個相關的選項:你可以創建自己的ArrayList,並將事物標記為“已刪除”。 這個算法做出了以下假設:

  • 在施工期間浪費時間(較低的速度)比在拆除操作期間做同樣的事情更好。 換句話說,它將速度懲罰從一個位置移動到另一個位置。
  • 現在最好浪費內存,並在計算結果后計算垃圾,而不是花時間預先計算(你總是被時間垃圾收集困住......)。
  • 一旦刪除開始,元素永遠不會被添加到列表中(否則重新分配flags對象會出現問題)

此外,這再次沒有經過測試,因此存在prlolly語法錯誤。

public class FlaggedList extends ArrayList {
  private Vector<Boolean> flags = new ArrayList();
  private static final String IN = Boolean.TRUE;  // not removed
  private static final String OUT = Boolean.FALSE; // removed
  private int removed = 0;

  public MyArrayList(){ this(1000000); }
  public MyArrayList(int estimate){
    super(estimate);
    flags = new ArrayList(estimate);
  }

  public void remove(int idx){
    flags.set(idx, OUT);
    removed++;
  }

  public boolean isRemoved(int idx){ return flags.get(idx); }
}

和迭代器 - 可能需要更多工作來保持同步,並且省略了許多方法,這次:

public class FlaggedListIterator implements 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();
  }
}

嘗試在您的算法中實現遞歸。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM