【问题标题】:removing irrelevant items from a List vs building up new List with relevant items从列表中删除不相关的项目与使用相关项目建立新列表
【发布时间】:2013-10-02 12:02:15
【问题描述】:

我正在逐个像素地检测位图中的数字。每个图形都以一个像素的大小开始。大多数数字都在增长,但有些数字仍然很小。

我在ArrayList 中按顺序收集这些数字。在下一步中,我想减少删除小数字的列表。

第一步收集了大约 1500 个图形。第二步删除了大约 1/3。

什么表现更好:

  1. 从列表中删除项目
    由于使用 LinkedList 删除项目的效果更好:
    • 我可以在第一步中使用LinkedList,但随后构建此列表的性能比使用ArrayList 更差(因为创建LinkedList 项比将对象添加到@ 987654326@)
      因此
    • 我仍然使用ArrayList,每次都必须将删除项目之后的项目复制到已删除项目的位置。为了减少要复制的项目数量,我可以从最后一个索引开始遍历列表。
  2. 建立一个只有更大数字的新列表
    • 我创建了一个新的ArrayList,使用原始数字列表的 3/4 大小对其进行初始化,然后仅将较大的数字添加到新列表中。

我想,使用这么几个项目,没有一个选项会带来可衡量的性能提升。但是对于更多的项目,什么表现更好?

【问题讨论】:

  • 有什么阻止你自己测试这个吗?
  • 在需要的时候改变你的实现真的很难吗?我不这么认为。过早的优化往往会导致代码太难......
  • 你为什么不只是将过滤器传递给你用来删除数字的方法并只接受/添加到列表中足够大的数字?
  • 列表的顺序重要吗?如果不是,你可能根本不想要一个列表,而是一个集合
  • @Jon Skeet:没有理由,为什么我不能测试它。除了这种经验方法之外,我还对您的考虑感兴趣。

标签: java list arraylist


【解决方案1】:

使用下面的测试程序,我发现

数组列表

1000个对象引用,25%要从arrayList中移除;

  • 从现有的数组列表中删除; 0.565ms
  • 重新创建一个新的arrayList; 0.573ms

100000个对象引用,25%要从arrayList中移除;

  • 从现有的数组列表中删除; 1028 毫秒
  • 重新创建一个新的数组列表; 8.66 毫秒
  • 重新创建一个新的数组列表(预先调整大小); 2毫秒

在小范围内这并不重要,在大范围内创建一个新列表是关键,最好是预先调整大小

哈希集

1000个对象引用,25%要从arrayList中移除;

  • 从现有哈希集中删除; 0.602ms
  • 重新创建一个新的哈希集; 2.64 毫秒

100000个对象引用,25%要从arrayList中移除;

  • 从现有哈希集中删除; 28 毫秒
  • 重新创建一个新的哈希集; 37 毫秒

在小尺度上,从现有集合中移除似乎更快,但在重要的尺度上两者都是可比的。

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import javax.vecmath.Vector3d;

public class Test {

        public static void main(String[] args){
            voidTestArrayList(100000);
            voidTestHashSet(100000);

            
        }
        

        
        public static void voidTestArrayList(int samples){
            {
                Collection<Vector3d> collectionArrayList=new ArrayList<Vector3d>();
                 preamble(collectionArrayList,samples);
                 testRemoveQuarter(collectionArrayList);
            }
            {
                Collection<Vector3d> collectionArrayList2=new ArrayList<Vector3d>();
                Collection<Vector3d> collectionArrayListDestination=new ArrayList<Vector3d>();
                preamble(collectionArrayList2,samples);

                testRetain3Quarter(collectionArrayList2,collectionArrayListDestination);
            }
            {
                Collection<Vector3d> collectionArrayList3=new ArrayList<Vector3d>();
                preamble(collectionArrayList3,samples);
                Collection<Vector3d> collectionArrayListDestination3=new ArrayList<Vector3d>(collectionArrayList3.size());
                testRetain3QuarterPresized(collectionArrayList3,collectionArrayListDestination3);
            }
        }
        
        public static void voidTestHashSet(int samples){
             Collection<Vector3d> collectionHashSet=new HashSet<Vector3d>();
             preamble(collectionHashSet,samples);
             testRemoveQuarter(collectionHashSet);
             
             Collection<Vector3d> collectionHashSet2=new HashSet<Vector3d>();
             Collection<Vector3d> collectionHashSetDestination=new HashSet<Vector3d>();
             preamble(collectionHashSet2,samples);
             
             testRetain3Quarter(collectionHashSet2,collectionHashSetDestination);
             
        }
        
        public static void voidTestRemoveFromArrayList(){
             Collection<Vector3d> collectionArrayList=new ArrayList<Vector3d>();
             preamble(collectionArrayList,1000);
             testRemoveQuarter(collectionArrayList);
        }
        
        
        public static void preamble(Collection<Vector3d> collection, int numberToAdd){
            //not part of timed test
            for(int i=0;i<numberToAdd;i++){
                collection.add(new Vector3d(Math.random(),Math.random(),Math.random()));
            }
        }
        
        public static void testRemoveQuarter(Collection<Vector3d> collection){
            Iterator<Vector3d> iterator=collection.iterator();
            
            int counter=0;
            while(iterator.hasNext()){
                counter++;
                iterator.next();
                if ((counter%4)==0){
                    counter=0;
                    iterator.remove();
                }
            }
        }
        
        public static void testRetain3QuarterPresized(Collection<Vector3d> collection, Collection<Vector3d> destinationCollection){
            testRetain3Quarter(collection, destinationCollection);
        }
        public static void testRetain3Quarter(Collection<Vector3d> collection, Collection<Vector3d> destinationCollection){
            Iterator<Vector3d> iterator=collection.iterator();
            
            int counter=0;
            while(iterator.hasNext()){
                counter++;
                Vector3d processing=iterator.next();
                if ((counter%4)!=0){
                    counter=0;
                    destinationCollection.add(processing);
                }
            }
        }
        
}

注意重要的是要注意,就集合而言,它包含对对象的引用,因此我对特定对象类的选择是无关紧要的

【讨论】:

  • 关于 ArrayList 的结果,结论是:选择创建新 ArrayList 的方法没有错。有没有给新的 ArrayList 初始化一定的容量,防止它多次调整大小?
  • @LuCio 不,调整目标数组列表可能会进一步提高速度
  • @LuCio 添加了预置结果
【解决方案2】:

您应该对不同的 java.util.List 实现进行基准测试,重点关注添加和删除元素。

【讨论】:

    【解决方案3】:

    如果您坚持使用ArrayList,我会说第二个选项更快。但是您应该自己对其进行基准测试,并尝试使用LinkedList 变体。据我所知,Java LinkedList 是一个双链表,因此在末尾添加元素往往比ArrayList 更快。特别是如果您不知道初始大小。

    【讨论】:

    • 将项目添加到 ArrayList 的末尾也非常快,因为列表仅在超出其初始容量时才需要复制。不同之处在于,如果您在删除点已经有一个迭代器,而 list.removs(x) 为 ArrayList 运行 O(n),则从 LinkedList 中间删除项目是 O(1)。
    【解决方案4】:

    我建议一些这样的基准:

    List<Object> arrayRemove = new ArrayList<Object>(initialMax);
    List<Object> arrayCopy = new ArrayList<Object>((int) (initialMax * (1+retain)/2));
    List<Object> linkedRemove = new LinkedList<Object>();
    List<Object> linkedCopy = new LinkedList<Object>();
    
    for (int i = 0; i < initialMax; i++) {
        arrayRemove.add(new Object());
        linkedRemove.add(new Object());
    }
    
    Random rnd = new Random();
    //Begin benchmarking
    for (int i = 0; i < initialMax; i++) {
        if (rnd.nextDouble() < retain) {
            linkedCopy.add(arrayRemove.get(i)); //1.
            // arrayCopy.add(arrayRemove.get(i)); //2. (same code but...)
        } else {
            // linkedRemove.remove(0); //3.
            // arrayRemove.remove(0); //4.
        }
    }
    // End benchmarking
    

    initialMax = 1500; retain = 2/3 你分别测试所有四种方法。
    此外,当您的“数字”可以随机排列时,请考虑使用HashSet


    编辑

    (基本)测试表明方法4没用,其他的速度差不多:

    import java.util.ArrayList;
    import java.util.Date;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Random;
    
    
    public class Test {
    private static final int initialMax = 150000;
    private static final double retain = 2/3;
    
    public static void main(String[] args) {
        List<Object> arrayRemove = new ArrayList<Object>(initialMax);
        List<Object> arrayCopy = new ArrayList<Object>((int) (initialMax * (1+retain)/2));
        List<Object> linkedRemove = new LinkedList<Object>();
        List<Object> linkedCopy = new LinkedList<Object>();
    
        for (int i = 0; i < initialMax; i++) {
            arrayRemove.add(new Object());
            linkedRemove.add(new Object());
        }
    
        Random rnd = new Random();
        //Begin benchmarking
        Date[] ds = new Date[5];
        ds[0] = new Date();
        for (int i = 0; i < initialMax; i++) {
            if (rnd.nextDouble() < retain) {
                linkedCopy.add(arrayRemove.get(i)); //1.
                // arrayCopy.add(arrayRemove.get(i)); //2. (same code but...)
            } else {
                // linkedRemove.remove(i); //3.
                // arrayRemove.remove(i); //4.
            }
        }
        ds[1] = new Date();
        for (int i = 0; i < initialMax; i++) {
            if (rnd.nextDouble() < retain) {
                // linkedCopy.add(arrayRemove.get(i)); //1.
                arrayCopy.add(arrayRemove.get(i)); //2. (same code but...)
            } else {
                // linkedRemove.remove(i); //3.
                // arrayRemove.remove(i); //4.
            }
        }
        ds[2] = new Date();
        for (int i = 0; i < initialMax; i++) {
            if (rnd.nextDouble() < retain) {
                // linkedCopy.add(arrayRemove.get(i)); //1.
                // arrayCopy.add(arrayRemove.get(i)); //2. (same code but...)
            } else {
                linkedRemove.remove(0); //3.
                // arrayRemove.remove(0); //4.
            }
        }
        ds[3] = new Date();
        for (int i = 0; i < initialMax; i++) {
            if (rnd.nextDouble() < retain) {
                // linkedCopy.add(arrayRemove.get(i)); //1.
                // arrayCopy.add(arrayRemove.get(i)); //2. (same code but...)
            } else {
                // linkedRemove.remove(0); //3.
                arrayRemove.remove(0); //4.
            }
        }
        ds[4] = new Date();
        // End benchmarking
        long[] diff = new long[4];
        System.out.println("Results:");
        for (int i = 0; i < 4; i++) {
            diff[i] = (ds[i + 1].getTime() - ds[i].getTime());
            System.out.println((i+1) + ".: " + diff[i]);
        }
    }
    }
    

    结果是

    结果:
    1.:0-15
    2.: 0
    3.:0-15
    4.: 6895

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-03-22
      • 2021-03-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-24
      • 1970-01-01
      相关资源
      最近更新 更多