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]