不同的列表类采用不同的遍历方式

接下来实现一个功能,计算60万个学生的成绩平均分

使用foreach遍历ArrayList的demo:


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

public class Test {

    public static int average(List<Integer> scores) {
        int sum = 0;
        for (Integer score : scores) {
            sum = sum + score;
        }
        return sum / scores.size();
    }

    public static void main(String[] args) {
        int studentNum = 60 * 10000;//学生数量
        List<Integer> scores = new ArrayList<>(studentNum);
        for (int i = 0; i < studentNum; i++) {
            scores.add(new Random().nextInt(100));
        }
        long startTime = System.currentTimeMillis();
        System.out.println("平均分:" + average(scores));
        long endTime = System.currentTimeMillis();
        System.out.println("耗时:" + (endTime - startTime) + "ms");
    }
}

运行结果如下:
在这里插入图片描述

使用下标遍历ArrayList的demo:

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

public class Test {

    public static int average(List<Integer> scores) {
        int sum = 0;
        for (int i = 0; i < scores.size(); i++) {
            sum = sum + scores.get(i);
        }
        return sum / scores.size();
    }

    public static void main(String[] args) {
        int studentNum = 60 * 10000;//学生数量
        List<Integer> scores = new ArrayList<>(studentNum);
        for (int i = 0; i < studentNum; i++) {
            scores.add(new Random().nextInt(100));
        }
        long startTime = System.currentTimeMillis();
        System.out.println("平均分:" + average(scores));
        long endTime = System.currentTimeMillis();
        System.out.println("耗时:" + (endTime - startTime) + "ms");
    }
}

运行结果:
在这里插入图片描述

可以发现使用下标来遍历ArrayList,性能大概提高了42%左右。

先看看foreach遍历操作是怎样的,直接看下编译后的部分class文件,如下:
在这里插入图片描述
可以发现foreach就是个语法糖,其实本质上还是用的迭代器模式,所以多出了需要判断是否存在下一个元素,然后才获取下一个元素的操作,这也是foreach遍历耗时的原因。

那是不是说可以放弃foreach了,都用下标遍历是不是会更好,毕竟好像开发用了这么久的foreach,只是因为使用方便而已?

使用foreach可以说是多了几步耗时的操作,而使用下标遍历则节省了哪些操作,所以下标遍历会更快,但是这是因为ArrayList是随机存取的,前后元素之间是没有什么关联关系的,所以下一个元素是否存在的这种判断就可以省去了,因为反正通过下标就能取到,那有没有前后元素是有关联关系的集合呢,有的,LinkedList就是其中之一,所以接下来使用LinkedList来存取学生分数,不过这里的学生数用一万就好了,60万学生成绩使用下标遍历测试时间就比较久了。

使用下标遍历LinkedList的demo:

import java.util.LinkedList;
import java.util.List;
import java.util.Random;

public class Test {

    public static int average(List<Integer> scores) {
        int sum = 0;
        for (int i = 0; i < scores.size(); i++) {
            sum = sum + scores.get(i);
        }
        return sum / scores.size();
    }

    public static void main(String[] args) {
        int studentNum = 10000;//学生数量
        List<Integer> scores = new LinkedList<>();
        for (int i = 0; i < studentNum; i++) {
            scores.add(new Random().nextInt(100));
        }
        long startTime = System.currentTimeMillis();
        System.out.println("平均分:" + average(scores));
        long endTime = System.currentTimeMillis();
        System.out.println("耗时:" + (endTime - startTime) + "ms");
    }
}

运行结果如下:
在这里插入图片描述
使用foreach遍历LinkedList的demo:

import java.util.LinkedList;
import java.util.List;
import java.util.Random;

public class Test {

    public static int average(List<Integer> scores) {
        int sum = 0;
        for (Integer score : scores) {
            sum = sum + score;
        }
        return sum / scores.size();
    }

    public static void main(String[] args) {
        int studentNum = 10000;//学生数量
        List<Integer> scores = new LinkedList<>();
        for (int i = 0; i < studentNum; i++) {
            scores.add(new Random().nextInt(100));
        }
        long startTime = System.currentTimeMillis();
        System.out.println("平均分:" + average(scores));
        long endTime = System.currentTimeMillis();
        System.out.println("耗时:" + (endTime - startTime) + "ms");
    }
}

运行结果如下:
在这里插入图片描述

可以发现使用foreach遍历LinkedLis后,性能直线提高了96%左右。

可以看下下标遍历LinkedList慢的原因,跟进LinkedList的get(int index)方法中,如下:在这里插入图片描述
在这里插入图片描述
从截图中源码可以清晰的看到LinkedList的get(int index)方法的逻辑,先把下标和中间值做比较,如果小于中间值,则从头节点开始遍历,如果大于中间值,则从尾节点开始反向遍历。因此每次调用get(int index)方法时都会遍历一次,所以遍历耗时的原因就在这了。

总结

ArrayList遍历建议用下标,LinkedList遍历建议用迭代器,不同的列表类试着采用不同的遍历方式。

猜你喜欢

转载自blog.csdn.net/weixin_38106322/article/details/108370082