商道如狼道 发表于 2024-9-23 16:04:22

JAVA根本之七-Collection和它的并行和流处理

Collection 翻下词典,有很多含义:
收集;聚集;(常指同类的)收藏品;募捐,募集;作品集;聚积;取走;一群人;拿走;(常为季节性推出的)系列时装(或家用品);一批物品
选择“聚集”作为翻译名,我以为可行,除非我们现在重新创造一个汉语词组。
 
对于CRUD和非CRUD,聚集都是一个无比重要的东西,因为计算机的本质是对信息的处理。
信息一般不是单个,是一堆,一堆堆,一块块,一个个....
 
网上关于聚集的资料无比多,所以本文主要是做一个简要的介绍,并添加一些注意事项和个人感悟。
 
一、简介

不外Collection的子孙过于多,用现有词汇命名这些子孙并不容易,有待创建新的词汇。
常用知名子孙有:
List  --  列表,javaDoc的释义是:有序聚集。
   --ArrayList   动态大小列表 ,这是crud中最常用的范例 。不包管顺序
   --LinkedList  双链列表,可以固定成员顺序。本身实现了Deque的接口,可用于辅助实现FiLo的算法
Set  - 无重复聚集,允许有一个null成员
  ---TreeSet   有序聚集
  -- HastSet  哈希聚集  ,主要是操纵的性能好一些
    -- LinkedHashSet  双向链哈希聚集,保持了插入顺序,又具有对应的性能
Queue -队列
   --Deque  双端操纵队列。它有一个著名的实现  LinkedList
Buffer --缓冲
  不外这个主要是阿帕奇的实现org.apache.commons.collections.Buffer,算不得java的根本范例
 
如果是初级程序员,大概以CRUD为主的,那么只要学些掌握ArrayList就差不多了,因为现在的大部分的ORM大概JDBC的上级实现都适用ArrayList来存储数据集。
 
二、聚集的基本方法

仅仅介绍Collection的接口方法,为了便于理解,以LinkedList为例子。
这些方法都极其简单,也没有什么特别好解释的,直接上例子吧!
package study.base.types.collection.list;

import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
* 演示Collection接口的基本操作和LinkedList的一些典型操作
* @author lto
*/
public class TestLinkedList {
    private LinkedList<MoneyJar> list;
    private String[] givers = new String[]{"爸爸","妈妈","哥哥","姐姐","爷爷","奶奶"};
    private Random random = new Random();
    private Map<String,Long> realGivers;

    public TestLinkedList(int size) {
      this.list = new LinkedList<>();
      this.realGivers = new HashMap<>();
      //插入100个MoneyJar,金额和日期都是随机的,giver是随机
      for (int i = 0; i < size; i++) {
            String giver = givers;
            int amount = random.nextInt(100);
            this.list.add(new MoneyJar(giver, amount, new Date()));
      }
      //按照giver分组统计个数,并赋值给realGivers
      this.list.stream().collect(Collectors.groupingBy(MoneyJar::giver,
                Collectors.counting())).forEach((k,v)->{
            realGivers.put(k,(long)v);});
      //打印realGivers
      this.realGivers.forEach((k,v)->{System.out.println(String.format("%s共有%d个", k, v));});
    }

    public void count(){
      long start = System.currentTimeMillis();
      final long[] total = {0};
      this.list.spliterator().forEachRemaining(mj-> total += mj.amount());
      System.out.println(String.format("总共%d元",total));
      System.out.println("耗费时间:"+(System.currentTimeMillis()-start));
    }

    public void sortByAmount(){
      this.list.sort((o1, o2) -> o1.amount().compareTo(o2.amount()));
    }
    /**
   * 统计每个giver给的钱,并打印结果
   */
    public void sumByGiver(){
      System.out.println("--------------------****************-----------------------------");
      //根据giver分组统计每个giver给的钱,并返回一个ListMap
      long start = System.currentTimeMillis();
      Map<String, Integer> result= this.list.stream().collect(Collectors.groupingBy(MoneyJar::giver,
                Collectors.summingInt(MoneyJar::amount)));
      //打印统计结果
      result.forEach((k,v)->{System.out.println(String.format("%s给的钱是%d", k, v));});
      System.out.println("耗费时间:"+(System.currentTimeMillis()-start));

      //采用for循环的方式,分组统计
      System.out.println("采用for循环的方式,分组统计-----------------------------");
      long start1 = System.currentTimeMillis();
      Map<String,List<Integer>> result1= new HashMap<>();
      //初始化result1,把realGivers的每个元素作为key,初始值为0
         this.realGivers.forEach((k,v)->{
            result1.put(k,new ArrayList<>());
      });
      //遍历list,计算每个giver给的钱

      for (MoneyJar moneyJar : list) {
         result1.get(moneyJar.giver()).add(moneyJar.amount());
      }

      //根据result1的成员个数,创建对应的线程,然后在线程中计算每个giver给的钱,并计算总和
      int numThreads=result1.size();
      CountDownLatch latch = new CountDownLatch(numThreads);

      ExecutorService executor = Executors.newFixedThreadPool(numThreads);
      result1.forEach((k,v)->{
            Runnable worker = () -> {
                try {
                  long sum=0;
                  for (int i : v) {
                        sum+=i;
                  }
                  System.out.println(String.format("%s给的钱是%d", k, sum));
                } finally {
                  latch.countDown(); // 计数减一
                }
            };
            // 使用executor提交任务,而不是直接启动Thread
            executor.submit(worker);
      });
      try {
            // 等待所有线程完成
            latch.await();
            System.out.println("All threads have finished.");
      } catch (InterruptedException e) {
            e.printStackTrace();
      }
      // 关闭executor,释放资源
      executor.shutdown();
      System.out.println("耗费时间:"+(System.currentTimeMillis()-start1));
    }


    public void splitToSum(){
      //把钱罐的钱分为n份,分别统计,然后再合并总的金额,并统计耗费时间
      System.out.println("--   采用并行流的方法");
      long start = System.currentTimeMillis();
      Long total=list.parallelStream().mapToLong(MoneyJar::amount).sum();
      System.out.println("耗费时间:"+(System.currentTimeMillis()-start));
      System.out.println("总金额是"+total.toString());

      //采用传统的for循环方式累积
      System.out.println("--   采用传统的for循环的方法");
      start = System.currentTimeMillis();
      long sum=0;
      for (MoneyJar moneyJar : list) {
            sum+=moneyJar.amount();
      }
      System.out.println("总金额是"+sum);
      System.out.println("耗费时间:"+(System.currentTimeMillis()-start));
    }

    /**
   * 把小于等于指定的金额的钱都清理掉
   * @param amount
   */
    public void purgeSmallMoney(int amount){
      this.list.removeIf(moneyJar -> moneyJar.amount()<=amount);
    }

    record MoneyJar(String giver,Integer amount,
                  Date putDay){
    }

    public static void main(String[] args) {
      //当10万个的时候,并行的速度反而是for的3倍左右。
      TestLinkedList test = new TestLinkedList(200);
      test.splitToSum();
      test.sortByAmount();
      System.out.println("-- 排序后 -----");
      for (MoneyJar moneyJar : test.list) {
            System.out.println(moneyJar);
      }
      //测试100万的情况
      TestLinkedList test100 = new TestLinkedList(1000000);
      test100.splitToSum();

      //测试2000万的情况
      TestLinkedList test1000 = new TestLinkedList(20000000);
      test1000.splitToSum();

      //以上三个例子,哈无例外,都是简单的循环胜出。那么parametrizedStream的效率就值得怀疑了。
      //是否因为没有正确设置并行度,还是计算机的环境存在问题
      test1000.sumByGiver();
      test1000.count();
    }
}
页: [1]
查看完整版本: JAVA根本之七-Collection和它的并行和流处理