簡體   English   中英

快速算法從ArrayList中刪除多個元素

[英]Fast algorithm to remove a number of elements from an ArrayList

假設ArrayList的大小為n。

在我的例子中,我經常需要從ArrayList中刪除具有不同索引的1到n個元素。

通過使用visualvm profiler,我發現ArrayList.remove()占用了大約90%的運行時間。

所以我想提高刪除的性能。 我想知道它是否可以加速。

這是一個最小的例子:

public void testArrayListRemove() {
        List list = new ArrayList();
        int[] indexes = new int[] { 1, 2, 4, 10, 100, 1000 };
        for (int i = 0; i < 100000; i++) {
            list.add(i);
        }
        for (int i = indexes.length - 1; i >= 0; i--) {
            list.remove(indexes[i]);
        }
    }

我能想到的想法是將要刪除的元素交換到最后並將其刪除,以便ArrayList.remove()不需要生成system.arraycopy。 我不確定這是否真的有效。

注意:ArrayList.remove(i)當我不是最后一個元素時,它將執行System.arraycopy來移動元素。

如果您能提供解決我的問題的想法,將非常感激。 您可以評論我最終交換元素的天真想法,或者甚至可以更好地提供除我的想法之外的更高級的算法。

謝謝。

你應該看看GapList - 一個閃電般快速的List實現

來自文章:


GapList簡介

為了解決問題,我們引入了GapList作為java.util.List接口的另一個實現。 作為主要功能,GapList提供

  • 通過索引有效訪問元素
  • 在列表的頭部和尾部插入恆定時間
  • 利用應用程序中常見的引用位置

讓我們看看如何實現GapList來提供這些功能。

如果我們比較ArrayList處理不同類型的插入的方式,我們可以快速提出一個解決方案,以保證在列表的開頭和結尾快速插入。

我們不是移動所有元素來獲得索引0處的空間,而是將現有元素保留在原位,並在剩余空間的情況下將元素寫入分配數組的末尾。 所以我們基本上使用數組作為一種旋轉緩沖區。

GapList1

為了以正確的順序訪問元素,我們必須記住第一個元素的起始位置,並使用模運算來計算邏輯元素的物理索引:

physIndex = (start + index) % capacity

為了利用引用的局部性,我們允許在列表元素的存儲中包含間隙。 由后備陣列中未使用的插槽形成的間隙可以是列表中的任何位置。 最多只有一個差距,但也可能沒有。

這個差距可以幫助您利用列表的引用位置,因此如果您在列表的中間添加一個元素,則中間的后續添加將很快。

中間

如果GapList沒有間隙,則根據需要創建一個間隙。 如果間隙位置錯誤,則移動。 但如果操作發生在彼此附近,則只需要復制少量數據。

GapList還允許在開始和結束時刪除元素而無需移動元素。

去掉

中間的移除處理類似於插入:如果不再需要,現有的間隙可能會移動或消失。


這是一個小示例代碼:

package rpax.stackoverflow.q24077045;

import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import org.magicwerk.brownies.collections.GapList;

public class Q24077045 {

    static int LIST_SIZE = 500000;

    public static void main(String[] args) {
        long a1, b1, c1 = 0, a2, b2, c2 = 0;
        int[] indexes = generateRandomIndexes(10000);

        a2 = System.currentTimeMillis();
        List<Integer> l2 = testArrayListRemove2(indexes);
        if (l2.size() < 1)
            return;
        b2 = System.currentTimeMillis();
        c2 = b2 - a2;

        a1 = System.currentTimeMillis();
        List<Integer> l = testArrayListRemove(indexes);
        if (l.size() < 1)
            return;
        b1 = System.currentTimeMillis();
        c1 = b1 - a1;

        System.out.println("1 : " + c1);
        System.out.println("2 : " + c2);

        System.out.println("Speedup : "+ c1 * 1.00 / c2+"x");

    }

    static int[] generateRandomIndexes(int number) {
        int[] indexes = new int[number];
        for (int i = 0; i < indexes.length; i++)
        {
            indexes[i] = ThreadLocalRandom.current().nextInt(0, LIST_SIZE);
        }
        Arrays.sort(indexes);
        return indexes;
    }

    public static List<Integer> testArrayListRemove(int[] indexes) {
        List<Integer> list = new ArrayList<Integer>(LIST_SIZE);

        for (int i = 0; i < LIST_SIZE; i++)
            list.add(i);

        for (int i = indexes.length - 1; i >= 0; i--)
            list.remove(indexes[i]);
        return list;
    }

    public static List<Integer> testArrayListRemove2(int[] indexes) {

        List<Integer> list = GapList.create(LIST_SIZE);

        for (int i = 0; i < LIST_SIZE; i++)
            list.add(i);

        for (int i = indexes.length - 1; i >= 0; i--)
            list.remove(indexes[i]);
        return list;
    }

}

我的筆記本電腦快了大約10倍。 它似乎是ArrayList一個很好的替代品。

免責聲明:這不是性能分析。 這只是一個說明性的例子。

您可以處理數組並迭代它:

Integer[] arr = list.toArray(new int[]{});

int[] newArr = new int[arr.length-indices.length];

現在你需要System.arrayCopy數組的每個連續塊:

for (int i=0;i<arr.length;i++) {
    for (int j : indexes) { // Should be 'indices' btw
        if (j == arr[i]) {
            // Array copy arr to newArr
            break;
        }
    }
}

在這里查看數據結構列表。 根據您的要求選擇一個。 像Guarev提到的那樣,HashMap可能是你最好的選擇。 Hashmaps具有插入,搜索和刪除的恆定時間的優點。

ArrayLists不是用於存儲大量數據的良好結構,因為內存使用很快就會出現,並且搜索/刪除時間非常快。

ArrayList實際上不是一個很好的數據結構來執行此操作。

我建議您使用HashMap來實現此目的,您可以將密鑰,值對與密鑰保持為索引。

暫無
暫無

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

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