[英]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簡介
為了解決問題,我們引入了GapList作為java.util.List
接口的另一個實現。 作為主要功能,GapList提供
讓我們看看如何實現GapList來提供這些功能。
如果我們比較ArrayList處理不同類型的插入的方式,我們可以快速提出一個解決方案,以保證在列表的開頭和結尾快速插入。
我們不是移動所有元素來獲得索引0處的空間,而是將現有元素保留在原位,並在剩余空間的情況下將元素寫入分配數組的末尾。 所以我們基本上使用數組作為一種旋轉緩沖區。
為了以正確的順序訪問元素,我們必須記住第一個元素的起始位置,並使用模運算來計算邏輯元素的物理索引:
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.