关于List循环性能问题

a282700554 2020-04-24 07:04:37
今天在看java8流操作时,想测试一下性能。但是突然发现一个问题。具体看下面两张图,为什么只是原始数据list放置的位置不一样。所消耗的时间差那么大? 因为放前面时,数据已经被读取到cpu缓存中了?
...全文
630 18 打赏 收藏 转发到动态 举报
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
lurie大神 2020-04-30
  • 打赏
  • 举报
回复
你这个问题没有实际意义。首先你在本机测试环境就不是个正式环境。
而且很可能和运行次数有关系,第一次运行要慢得多。
这个和jvm机制有很大关系。然后用流的情况又和量和硬件的问题相关。
多线程是在一定环境下才叫高效率,并不是任何情况下都高效率,具体原因又和数据量、硬件乱七八糟相关了


楼主这种在本机测试java8流操效率,几乎没有意义。
ITjavaman 2020-04-30
  • 打赏
  • 举报
回复
确实是GC的问题,我这边自己测的结果就不发了 在启动命令前加jvm参数-XX:+PrintGCDetails,查看GC情况 (add()和split(")函数创建了不少对象,把这个去掉你再看看效果,) 添加jvm传送门
  • 打赏
  • 举报
回复
-Xms1024M 加上这个执行 测试一: 方式一list普通For 最终耗时 = 215 方式一list2普通For 最终耗时 = 166 测试二: 方式一list普通For 最终耗时 = 222 方式一list2普通For 最终耗时 = 134 所以我觉得应该是 堆内存 在搞鬼。。。 代码对象初始化的位置和后期调用不一样,在堆内的位置(新、幸存、老、gc)也不一样, 当给堆提供了足够大的内存时,变得稳定了许多 但是在细节一下 就找不到了。。。
xiaoxiangqing 2020-04-28
  • 打赏
  • 举报
回复
2楼说的有道理
a282700554 2020-04-27
  • 打赏
  • 举报
回复
引用 2 楼 maradona1984 的回复:
因为list插入数据时,会不断扩容/复制,但你第二种方式相当于把扩容的事情都做过一次了,自然就只是插入数据而已,并没有复制扩容操作了,所以快了不少. 你可以尝试修改代码 List<String> newList1 = new ArrayList<>(); 改成 List<String> newList1 = new ArrayList<>(2000000); 就明白是为啥了
你这说法也说不过去,你看我图,我都是往newList1中add数据。前一次add一百万次733 , 第二次add 187。这个时间相关也太大了吧。具体的你看代码就知道了。
a282700554 2020-04-27
  • 打赏
  • 举报
回复
引用 6 楼 nayi_224 的回复:
你给出的代码甚至都没有控制唯一变量,不能得出任何结论。 我刚才也跑了几次,结果也是相当不稳定。直接说你想测什么吧。
就是为什以同样的循环,耗时差那么多? 根据我的理解,应该是跟cpu 一、二、三级缓存有关,但又不是很确定。
maradona1984 2020-04-27
  • 打赏
  • 举报
回复
引用 3 楼 月光下的大D丶 的回复:
[quote=引用 2 楼 maradona1984 的回复:] 因为list插入数据时,会不断扩容/复制,但你第二种方式相当于把扩容的事情都做过一次了,自然就只是插入数据而已,并没有复制扩容操作了,所以快了不少. 你可以尝试修改代码 List<String> newList1 = new ArrayList<>(); 改成 List<String> newList1 = new ArrayList<>(2000000); 就明白是为啥了
这个我尝试了一下 ,并没有 太大改变: 方式一list普通For 最终耗时 = 723 方式一list2普通For 最终耗时 = 721[/quote] 结果相当不稳定
nayi_224 2020-04-27
  • 打赏
  • 举报
回复
你给出的代码甚至都没有控制唯一变量,不能得出任何结论。 我刚才也跑了几次,结果也是相当不稳定。直接说你想测什么吧。
maradona1984 2020-04-27
  • 打赏
  • 举报
回复
引用 3 楼 月光下的大D丶 的回复:
[quote=引用 2 楼 maradona1984 的回复:] 因为list插入数据时,会不断扩容/复制,但你第二种方式相当于把扩容的事情都做过一次了,自然就只是插入数据而已,并没有复制扩容操作了,所以快了不少. 你可以尝试修改代码 List<String> newList1 = new ArrayList<>(); 改成 List<String> newList1 = new ArrayList<>(2000000); 就明白是为啥了
这个我尝试了一下 ,并没有 太大改变: 方式一list普通For 最终耗时 = 723 方式一list2普通For 最终耗时 = 721[/quote] 的确挺奇怪
  • 打赏
  • 举报
回复
public class ArrayListTest1 { public static void main(String[] args) { //原始List List<String> oldList1 = new ArrayList<>(); for (int i = 0; i < 1000000; i++) { oldList1.add("a:" + i); } //方式一:普通For循环 long start = System.nanoTime(); List<String> newList1 = new ArrayList<>(2000000); for (int i = 0; i < 1000000; i++) { newList1.add(oldList1.get(i).split(":")[1]); } System.out.println("方式一list普通For 最终耗时 = " + ((System.nanoTime() - start) / 100_0000)); //原始List List<String> oldList2 = new ArrayList<>(); for (int i = 0; i < 1000000; i++) { oldList2.add("b:" + i); } //方式一:普通For循环 final long start1 = System.nanoTime(); for (int i = 0; i < 1000000; i++) { newList1.add(oldList2.get(i).split(":")[1]); } System.out.println("方式一list2普通For 最终耗时 = " + ((System.nanoTime() - start1) / 100_0000)); } } public class ArrayListTest2 { public static void main(String[] args) { //原始List List<String> oldList = new ArrayList<>(); for (int i = 0; i < 1000000; i++) { oldList.add("a:" + i); } //原始List List<String> oldList2 = new ArrayList<>(); for (int i = 0; i < 1000000; i++) { oldList2.add("b:" + i); } //方式一:普通For循环 long start = System.nanoTime(); List<String> newList1 = new ArrayList<>(2000000); for (int i = 0; i < 1000000; i++) { newList1.add(oldList.get(i).split(":")[1]); } System.out.println("方式一list普通For 最终耗时 = " + ((System.nanoTime() - start) / 100_0000)); //方式一:普通For循环 long start1 = System.nanoTime(); for (int i = 0; i < 1000000; i++) { newList1.add(oldList2.get(i).split(":")[1]); } System.out.println("方式一list2普通For 最终耗时 = " + ((System.nanoTime() - start1) / 100_0000)); } } 这是测试代码
  • 打赏
  • 举报
回复
引用 2 楼 maradona1984 的回复:
因为list插入数据时,会不断扩容/复制,但你第二种方式相当于把扩容的事情都做过一次了,自然就只是插入数据而已,并没有复制扩容操作了,所以快了不少. 你可以尝试修改代码 List<String> newList1 = new ArrayList<>(); 改成 List<String> newList1 = new ArrayList<>(2000000); 就明白是为啥了
这个我尝试了一下 ,并没有 太大改变: 方式一list普通For 最终耗时 = 723 方式一list2普通For 最终耗时 = 721
maradona1984 2020-04-27
  • 打赏
  • 举报
回复
因为list插入数据时,会不断扩容/复制,但你第二种方式相当于把扩容的事情都做过一次了,自然就只是插入数据而已,并没有复制扩容操作了,所以快了不少. 你可以尝试修改代码 List<String> newList1 = new ArrayList<>(); 改成 List<String> newList1 = new ArrayList<>(2000000); 就明白是为啥了
  • 打赏
  • 举报
回复
我对这个也很感兴趣,试了一下 问题多出来一个。。。 方式一list普通For 最终耗时 = 713 (List<String> newList1 = new ArrayList<>(); 多了一个初始化操作速度居然还快了。。。) 方式一list2普通For 最终耗时 = 850
nayi_224 2020-04-27
  • 打赏
  • 举报
回复
引用 12 楼 a282700554 的回复:
[quote=引用 10 楼 nayi_224 的回复:] 测得次数太少了吧,我执行了好多次,最终结果是时间基本相等,误差大概20%以内。 如果真跟缓存有关,差距十倍以上还差不多
import java.util.ArrayList;
import java.util.List;

public class Test19 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		List<String> oldList1 = new ArrayList<>();
        for (int i = 0; i < 1000000; i++) {
            oldList1.add("a:" + i);
        }
        List<String> oldList2 = new ArrayList<>();
        for (int i = 0; i < 1000000; i++) {
            oldList2.add("b:" + i);
        }
        
      //方式一:普通For循环
        long start = System.nanoTime();
        List<String> newList1 = new ArrayList<>(1000000);
        List<String> newList2 = new ArrayList<>(1000000);
        for (int i = 0; i < 1000000; i++) {
            newList1.add(oldList1.get(i).split(":")[1]);
        }
        System.out.println("方式一list普通For 最终耗时 = " + ((System.nanoTime() - start) / 100_0000));


        //方式一:普通For循环
        long start1 = System.nanoTime();
        for (int i = 0; i < 1000000; i++) {
            newList2.add(oldList2.get(i).split(":")[1]);
        }
        System.out.println("方式一list2普通For 最终耗时 = " + ((System.nanoTime() - start1) / 100_0000));
	}
	
}
重点不在这,重点是oldList2 放置的位置。你仔细看我上传的两张图 [/quote] 又写了点测试,结论没变,是你的测试方式错的离谱。 在整个过程中,内存运行情况、数组复制、字符串缓存是最影响运行时间的部分,但是你完全没控制这些变量。你把我的2个方法单个执行,就会发现运行情况相当稳定,并且跟代码顺序无关
package test.lt20;

import java.util.ArrayList;
import java.util.List;

public class Test19 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//jdk8
		a1();
		//a2();
	}
	
	public static void a1() {
		List<String> oldList1 = new ArrayList<>();
        for (int i = 0; i < 1000000; i++) {
            oldList1.add("a:" + i);
        }
        List<String> newList1 = new ArrayList<>(1000000);
        List<String> newList2 = new ArrayList<>(1000000);
        
        String temp = "";
        for (int i = 0; i < 1000000; i++) {
            temp = oldList1.get(i).split(":")[1];
        }
        
        //方式一:普通For循环
        long start = System.nanoTime();
        
        for (int i = 0; i < 1000000; i++) {
            newList1.add(oldList1.get(i).split(":")[1]);
        }
        System.out.println("方式一list普通For 最终耗时 = " + ((System.nanoTime() - start) / 100_0000));

        List<String> oldList2 = new ArrayList<>();
        for (int i = 0; i < 1000000; i++) {
            oldList2.add("b:" + i);
        }
        
        for (int i = 0; i < 1000000; i++) {
            temp = oldList2.get(i).split(":")[1];
        }

        //方式一:普通For循环
        long start1 = System.nanoTime();
        for (int i = 0; i < 1000000; i++) {
            newList2.add(oldList2.get(i).split(":")[1]);
        }
        System.out.println("方式一list2普通For 最终耗时 = " + ((System.nanoTime() - start1) / 100_0000));
	}
	
	public static void a2() {
		List<String> oldList1 = new ArrayList<>();
        for (int i = 0; i < 1000000; i++) {
            oldList1.add("a:" + i);
        }
        List<String> oldList2 = new ArrayList<>();
        for (int i = 0; i < 1000000; i++) {
            oldList2.add("b:" + i);
        }
        ArrayList<String> newList1 = new ArrayList<>(1000000);
        List<String> newList2 = new ArrayList<>(1000000);
        
        String temp = "";
        for (int i = 0; i < 1000000; i++) {
            temp = oldList1.get(i).split(":")[1];
        }
        for (int i = 0; i < 1000000; i++) {
            temp = oldList2.get(i).split(":")[1];
        }
        
        //方式一:普通For循环
        long start = System.nanoTime();
        for (int i = 0; i < 1000000; i++) {
            newList1.add(oldList1.get(i).split(":")[1]);
        }
        System.out.println("方式一list普通For 最终耗时 = " + ((System.nanoTime() - start) / 100_0000));

        //方式一:普通For循环
        long start1 = System.nanoTime();
        for (int i = 0; i < 1000000; i++) {
            newList2.add(oldList2.get(i).split(":")[1]);
        }
        System.out.println("方式一list2普通For 最终耗时 = " + ((System.nanoTime() - start1) / 100_0000));
	}
	
}
闭包客 2020-04-27
  • 打赏
  • 举报
回复
从代码来看,和缓存是没有关系的。至于为什么实际的运行时间相差了几倍,有可能和程序的外部环境有关系,比如说系统有其他高占用的进程。另外,判断性能的问题,单次的测试是不够的,需要更多的测试,以检验数据的可信度。
a282700554 2020-04-27
  • 打赏
  • 举报
回复
引用 10 楼 nayi_224 的回复:
测得次数太少了吧,我执行了好多次,最终结果是时间基本相等,误差大概20%以内。 如果真跟缓存有关,差距十倍以上还差不多
import java.util.ArrayList;
import java.util.List;

public class Test19 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		List<String> oldList1 = new ArrayList<>();
        for (int i = 0; i < 1000000; i++) {
            oldList1.add("a:" + i);
        }
        List<String> oldList2 = new ArrayList<>();
        for (int i = 0; i < 1000000; i++) {
            oldList2.add("b:" + i);
        }
        
      //方式一:普通For循环
        long start = System.nanoTime();
        List<String> newList1 = new ArrayList<>(1000000);
        List<String> newList2 = new ArrayList<>(1000000);
        for (int i = 0; i < 1000000; i++) {
            newList1.add(oldList1.get(i).split(":")[1]);
        }
        System.out.println("方式一list普通For 最终耗时 = " + ((System.nanoTime() - start) / 100_0000));


        //方式一:普通For循环
        long start1 = System.nanoTime();
        for (int i = 0; i < 1000000; i++) {
            newList2.add(oldList2.get(i).split(":")[1]);
        }
        System.out.println("方式一list2普通For 最终耗时 = " + ((System.nanoTime() - start1) / 100_0000));
	}
	
}
重点不在这,重点是oldList2 放置的位置。你仔细看我上传的两张图
maradona1984 2020-04-27
  • 打赏
  • 举报
回复
引用 9 楼 a282700554 的回复:
[quote=引用 2 楼 maradona1984 的回复:] 因为list插入数据时,会不断扩容/复制,但你第二种方式相当于把扩容的事情都做过一次了,自然就只是插入数据而已,并没有复制扩容操作了,所以快了不少. 你可以尝试修改代码 List<String> newList1 = new ArrayList<>(); 改成 List<String> newList1 = new ArrayList<>(2000000); 就明白是为啥了
你这说法也说不过去,你看我图,我都是往newList1中add数据。前一次add一百万次733 , 第二次add 187。这个时间相关也太大了吧。具体的你看代码就知道了。[/quote] 把正则换成String.format,感觉稳定了,当然还是相当不稳定,跟自己电脑有关系一样
nayi_224 2020-04-27
  • 打赏
  • 举报
回复
测得次数太少了吧,我执行了好多次,最终结果是时间基本相等,误差大概20%以内。 如果真跟缓存有关,差距十倍以上还差不多
import java.util.ArrayList;
import java.util.List;

public class Test19 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		List<String> oldList1 = new ArrayList<>();
        for (int i = 0; i < 1000000; i++) {
            oldList1.add("a:" + i);
        }
        List<String> oldList2 = new ArrayList<>();
        for (int i = 0; i < 1000000; i++) {
            oldList2.add("b:" + i);
        }
        
      //方式一:普通For循环
        long start = System.nanoTime();
        List<String> newList1 = new ArrayList<>(1000000);
        List<String> newList2 = new ArrayList<>(1000000);
        for (int i = 0; i < 1000000; i++) {
            newList1.add(oldList1.get(i).split(":")[1]);
        }
        System.out.println("方式一list普通For 最终耗时 = " + ((System.nanoTime() - start) / 100_0000));


        //方式一:普通For循环
        long start1 = System.nanoTime();
        for (int i = 0; i < 1000000; i++) {
            newList2.add(oldList2.get(i).split(":")[1]);
        }
        System.out.println("方式一list2普通For 最终耗时 = " + ((System.nanoTime() - start1) / 100_0000));
	}
	
}

67,515

社区成员

发帖
与我相关
我的任务
社区描述
J2EE只是Java企业应用。我们需要一个跨J2SE/WEB/EJB的微容器,保护我们的业务核心组件(中间件),以延续它的生命力,而不是依赖J2SE/J2EE版本。
社区管理员
  • Java EE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧