ArrayList中存在100W个对象,某些对象的属性type值为2,问怎么取?

问题一个个回答:fori循环肯定可以。

我觉得考官想问如下问题,并期望有如下的答案:

1.100w个对象能不能放在ArrayList中?目前主流的PC机绝对没有问题,服务器就更不要说了。

小编这里有一份Java学习资料,加我的Java学习群:985331340免费获取。以下为部分资料截图

2.使用遍历的方法来查找数据可不可以?对性能没有要求时肯定可以。

3.有没有可能使用多线程的方法来解决?显然可以,这个应该是重点。其思路是将ArrayList划分为多个片,每个线程搜索一个片,最后将结果集合起来。

4.应该使用多少个线程来跑?这个就见仁见智了,一般来说,使用CPU核心数的2倍比较好,这是个经验值。太多线程肯定不行,创建和销毁线程会大量耗费资源。

5.读取ArrayList会不会导致加锁而造成性能降低?这个不会,如果数据不是同时读写,ArrayList是没有问题的。虽然它不是一个线程安全的集合类,但是并发读取是没有问题的,它也没有读锁。

6.这种问题是否可以使用fork-join框架来完成?这个显然可以,这个问题就是典型的fork-join问题,由于fork-join框架采用了任务偷取算法,所以它会比一般的线程池要快一点点完成计算任务。

好了,废话少说,上代码:

public class ArrayList100W {
    private static int LIST_LENGTH = 1000000;
    //线程数量
    private static int THREAD_NUMBER = 1000;
    //每个线程读取的list个数
    private static int SLICE_LENGTH = LIST_LENGTH/THREAD_NUMBER;

    public static void main(String[] args) throws InterruptedException {
        ArrayList<MyObject> myObjectArrayList = new ArrayList<>(LIST_LENGTH);
        for (int i = 0; i < LIST_LENGTH; i++) {
            myObjectArrayList.add(new MyObject());
        }

        //第一种方法,直接遍历
        long start = System.currentTimeMillis();
        int numberEquals2 = 0;
        for (int i = 0; i < LIST_LENGTH; i++) {
            if (myObjectArrayList.get(i).type == 2) {
                numberEquals2 ++;
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("线程数量1,线性遍历,花费的时间:"+(end-start)+" milliseconds, "+"type等于2的个数有:"+numberEquals2);

        //第二种方法,用100个线程来分别跑,用来计数的变量是原子变量
        start = System.currentTimeMillis();
        AtomicInteger atomicNumberEquals2 = new AtomicInteger(0);
        ExecutorService pool = Executors.newFixedThreadPool(THREAD_NUMBER);
        for (int i = 0; i < THREAD_NUMBER; i++) {
            final int threadNumber = i;
            pool.execute(new Runnable() {
                @Override
                public void run() {
                    for (int j = threadNumber*SLICE_LENGTH; j < ((threadNumber+1)*SLICE_LENGTH ); j++) {
                        if (myObjectArrayList.get(j).type == 2) {
                            atomicNumberEquals2.addAndGet(1);
                        }
                    }
                }
            });
        }
        pool.shutdown();
        pool.awaitTermination(1, TimeUnit.DAYS);
        end = System.currentTimeMillis();
        System.out.println("线程数量:"+THREAD_NUMBER+"(原子变量)花费的时间:"+(end-start)+" milliseconds, "+"type等于2的个数有:"+atomicNumberEquals2.get());

        //第三种方法,用100个线程来分别跑,使用数组来计数
        start = System.currentTimeMillis();
        int result[] = new int[THREAD_NUMBER];
        ExecutorService pool2 = Executors.newFixedThreadPool(THREAD_NUMBER);
        for (int i = 0; i < THREAD_NUMBER; i++) {
            final int threadNumber = i;
            pool2.execute(new Runnable() {
                @Override
                public void run() {
                    for (int j = threadNumber*SLICE_LENGTH; j < ((threadNumber+1)*SLICE_LENGTH); j++) {
                        if (myObjectArrayList.get(j).type == 2) {
                            result[threadNumber]++;
                        }
                    }
                }
            });
        }
        pool2.shutdown();
        pool2.awaitTermination(1, TimeUnit.DAYS);
        numberEquals2 = 0;
        for (int i = 0; i < THREAD_NUMBER; i++) {
            numberEquals2 += result[i];
        }
        end = System.currentTimeMillis();
        System.out.println("线程数量:"+THREAD_NUMBER+"(数组存储)花费的时间:"+(end-start)+" milliseconds, "+"type等于2的个数有:"+numberEquals2);

        //第四种方法,获取本机CPU核心数*2,设置为线程数量
        THREAD_NUMBER =  Runtime.getRuntime().availableProcessors()*2;
        SLICE_LENGTH = LIST_LENGTH/THREAD_NUMBER;
        start = System.currentTimeMillis();
        int[] result2 = new int[THREAD_NUMBER];
        ExecutorService pool3 = Executors.newFixedThreadPool(THREAD_NUMBER);
        for (int i = 0; i < THREAD_NUMBER; i++) {
            final int threadNumber = i;
            pool3.execute(new Runnable() {
                @Override
                public void run() {
                    for (int j = threadNumber*SLICE_LENGTH; j < ((threadNumber+1)*SLICE_LENGTH); j++) {
                        if (myObjectArrayList.get(j).type == 2) {
                            result2[threadNumber]++;
                        }
                    }
                }
            });
        }
        pool3.shutdown();
        pool3.awaitTermination(1, TimeUnit.DAYS);
        numberEquals2 = 0;
        for (int i = 0; i < THREAD_NUMBER; i++) {
            numberEquals2 += result2[i];
        }
        end = System.currentTimeMillis();
        System.out.println("线程数量:"+THREAD_NUMBER+"(CPU核心*2)花费的时间:"+(end-start)+" milliseconds, "+"type等于2的个数有:"+numberEquals2);
    }
}

class MyObject{
    int type;
    MyObject(){
        type = new Random().nextInt(100);
    }
}

我分别用线程数量10/100/1000跑了三遍,在我的pc上是这个结果:

线程1000个:

线程数量1,线性遍历,花费的时间:7 milliseconds, type等于2的个数有:9940
线程数量:1000(原子变量)花费的时间:240 milliseconds, type等于2的个数有:9940
线程数量:1000(数组存储)花费的时间:127 milliseconds, type等于2的个数有:9940
线程数量:8(CPU核心*2)花费的时间:23 milliseconds, type等于2的个数有:9940

线程100个:

线程数量1,线性遍历,花费的时间:6 milliseconds, type等于2的个数有:9884
线程数量:100(原子变量)花费的时间:56 milliseconds, type等于2的个数有:9884
线程数量:100(数组存储)花费的时间:20 milliseconds, type等于2的个数有:9884
线程数量:8(CPU核心*2)花费的时间:22 milliseconds, type等于2的个数有:9884

线程10个:

线程数量1,线性遍历,花费的时间:6 milliseconds, type等于2的个数有:9851
线程数量:10(原子变量)花费的时间:23 milliseconds, type等于2的个数有:9851
线程数量:10(数组存储)花费的时间:27 milliseconds, type等于2的个数有:9851
线程数量:8(CPU核心*2)花费的时间:24 milliseconds, type等于2的个数有:9851

结果和我想的差不多。

猜你喜欢

转载自blog.csdn.net/qq_43202482/article/details/88410883