⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 cnblogs.com/funnyzpc/p/10382053.html 「funnyZpC」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

一. Stream(流)是什么,干什么

Stream是一类用于替代对集合操作的工具类+Lambda式编程,他可以替代现有的遍历、过滤、求和、求最值、排序、转换等

二. Stream操作方式

  • 并行方式parallelStream
  • 顺序方式Stream

三. Stream优势

  • Lambda 可有效减少冗余代码,减少开发工作量
  • 内置对集合List、Map的多种操作方式,含基本数据类型处理
  • 并行Stream有效率优势(内置多线程)

四. Stream(流)的基本使用

  • 遍历forEach

@Test
public void stream() {
//操作List
List<Map<String, String>> mapList = new ArrayList() {
{
Map<String, String> m = new HashMap();
m.put("a", "1");
Map<String, String> m2 = new HashMap();
m2.put("b", "2");
add(m);
add(m2);
}
};
mapList.stream().forEach(item-> System.out.println(item));

//操作Map
Map<String,Object> mp = new HashMap(){
{
put("a","1");
put("b","2");
put("c","3");
put("d","4");
}
};
mp.keySet().stream().forEachOrdered(item-> System.out.println(mp.get(item)));
}

  • 过滤filter

     List<Integer> mapList = new ArrayList() {
{
add(1);
add(10);
add(12);
add(33);
add(99);
}
};
//mapList.stream().forEach(item-> System.out.println(item));
mapList = mapList.stream().filter(item->{
return item>30;
}).collect(Collectors.toList());
System.out.println(mapList);

  • 转换map和极值

      @Test
    public void trans(){
    List<Person> ps = new ArrayList<Person>(){
    {
    Person p1 = new Person();
    p1.setAge(11);
    p1.setName("张强");

    Person p2 = new Person();
    p2.setAge(17);
    p2.setName("李思");

    Person p3 = new Person();
    p3.setAge(20);
    p3.setName("John");

    add(p1);
    add(p2);
    add(p3);
    }
    };
    //取出所有age字段为一个List
    List<Integer> sumAge = ps.stream().map(Person::getAge).collect(Collectors.toList());
    System.out.println(sumAge);
    //取出age最大的那
    Integer maxAge =sumAge.stream().max(Integer::compare).get();
    System.out.println(maxAge);
    }

    class Person{

    private String name;
    private Integer age;

    public String getName() {
    return name;
    }

    public void setName(String name) {
    this.name = name;
    }

    public Integer getAge() {
    return age;
    }

    public void setAge(Integer age) {
    this.age = age;
    }

}

#  五. Stream(流)的效率
+ 模拟非耗时简单业务逻辑
class Person{
private String name;
private int age;
private Date joinDate;
private String label;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public Date getJoinDate() {
return joinDate;
}

public void setJoinDate(Date joinDate) {
this.joinDate = joinDate;
}

public String getLabel() {
return label;
}

public void setLabel(String label) {
this.label = label;
}

public class DataLoopTest { private static final Logger LOG= LoggerFactory.getLogger(DataLoopTest.class);

private static final List<Person> persons = new ArrayList<>();
static {
for(int i=0;i<=1000000;i++){
Person p = new Person();
p.setAge(i);
p.setName("zhangSan");
p.setJoinDate(new Date());
persons.add(p);
}
}

/**
* for 循环耗时 ===> 1.988
* for 循环耗时 ===> 2.198
* for 循环耗时 ===> 1.978
*
*/
@Test
public void forTest(){
Instant date_start = Instant.now();
int personSize = persons.size();
for(int i=0;i<personSize;i++){
persons.get(i).setLabel(persons.get(i).getName().concat("-"+persons.get(i).getAge()).concat("-"+persons.get(i).getJoinDate().getTime()));
}
Instant date_end = Instant.now();
LOG.info("for 循环耗时 ===> {}", Duration.between(date_start,date_end).toMillis()/1000.0);
}

/**
* forEach 循环耗时 ===> 1.607
* forEach 循环耗时 ===> 2.242
* forEach 循环耗时 ===> 1.875
*/
@Test
public void forEach(){
Instant date_start = Instant.now();
for(Person p:persons){
p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime()));
}
Instant date_end = Instant.now();
LOG.info("forEach 循环耗时 ===> {}", Duration.between(date_start,date_end).toMillis()/1000.0);
}

/**
* streamForeach 循环耗时 ===> 1.972
* streamForeach 循环耗时 ===> 1.969
* streamForeach 循环耗时 ===> 2.125
*/
@Test
public void streamForeach(){
Instant date_start = Instant.now();
persons.stream().forEach(p->p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime())));
Instant date_end = Instant.now();
LOG.info("streamForeach 循环耗时 ===> {}", Duration.between(date_start,date_end).toMillis()/1000.0);
}

/**
* parallelStreamForeach 循环耗时 ===> 1.897
* parallelStreamForeach 循环耗时 ===> 1.942
* parallelStreamForeach 循环耗时 ===> 1.642
*/
@Test
public void parallelStreamForeach(){
Instant date_start = Instant.now();
persons.parallelStream().forEach(p->p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime())));
Instant date_end = Instant.now();
LOG.info("parallelStreamForeach 循环耗时 ===> {}", Duration.between(date_start,date_end).toMillis()/1000.0);
}

}

+  模拟耗时简单业务逻辑

public class DataLoopBlockTest { private static final Logger LOG= LoggerFactory.getLogger(DataLoopTest.class);

private static final List<Person> persons = new ArrayList<>();
static {
for(int i=0;i<=100000;i++){
Person p = new Person();
p.setAge(i);
p.setName("zhangSan");
p.setJoinDate(new Date());
persons.add(p);
}
}

/**
* for 循环耗时 ===> 101.385
* for 循环耗时 ===> 102.161
* for 循环耗时 ===> 101.472
*
*/
@Test
public void forTest(){
Instant date_start = Instant.now();
int personSize = persons.size();
for(int i=0;i<personSize;i++){
try {
Thread.sleep(1);
persons.get(i).setLabel(persons.get(i).getName().concat("-"+persons.get(i).getAge()).concat("-"+persons.get(i).getJoinDate().getTime()));
}catch (Exception e){
e.printStackTrace();
}
}
Instant date_end = Instant.now();
LOG.info("for 循环耗时 ===> {}", Duration.between(date_start,date_end).toMillis()/1000.0);
}

/**
* forEach 循环耗时 ===> 101.027
* forEach 循环耗时 ===> 102.488
* forEach 循环耗时 ===> 101.608
*/
@Test
public void forEach(){
Instant date_start = Instant.now();
for(Person p:persons){
try {
Thread.sleep(1);
p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime()));
}catch (Exception e){
e.printStackTrace();
}
}
Instant date_end = Instant.now();
LOG.info("forEach 循环耗时 ===> {}", Duration.between(date_start,date_end).toMillis()/1000.0);
}

/**
* streamForeach 循环耗时 ===> 103.246
* streamForeach 循环耗时 ===> 101.128
* streamForeach 循环耗时 ===> 102.615
*/
@Test
public void streamForeach(){
Instant date_start = Instant.now();
//persons.stream().forEach(p->p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime())));
persons.stream().forEach(p->{
try {
Thread.sleep(1);
p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime()));
}catch (Exception e){
e.printStackTrace();
}
});
Instant date_end = Instant.now();
LOG.info("streamForeach 循环耗时 ===> {}", Duration.between(date_start,date_end).toMillis()/1000.0);
}

/**
* parallelStreamForeach 循环耗时 ===> 51.391
* parallelStreamForeach 循环耗时 ===> 53.509
* parallelStreamForeach 循环耗时 ===> 50.831
*/
@Test
public void parallelStreamForeach(){
Instant date_start = Instant.now();
//persons.parallelStream().forEach(p->p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime())));
persons.parallelStream().forEach(p->{
try {
Thread.sleep(1);
p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime()));
}catch (Exception e){
e.printStackTrace();
}
});
Instant date_end = Instant.now();
LOG.info("parallelStreamForeach 循环耗时 ===> {}", Duration.between(date_start,date_end).toMillis()/1000.0);
//LOG.info("\r\n===> {}",JSON.toJSONString(persons.get(10000)));
}

}



可以看到在百万数据下做简单数据循环处理,对于普通for(for\foreach)循环或stream(并行、非并行)下,几者的效率差异并不明显, 注意: 在百万数据下,普通for、foreach循环处理可能比stream的方式快许多,对于这点效率的损耗,其实lambda表达式对代码的简化更大! 另外,在并行流的循环下速度提升了一倍之多,当单个循环耗时较多时,会拉大与前几者的循环效率 (以上测试仅对于循环而言,其他类型业务处理,比如排序、求和、最大值等未做测试,个人猜测与以上测试结果相似)

# 六. Stream(流)注意项
+ 并行stream不是线程安全的,当对循坏外部统一对象进行读写时候会造成意想不到的错误,这需要留意
+ 因stream总是惰性的,原对象是不可以被修改的,在集合处理完成后需要将处理结果放入一个新的集合容器内
+ 普通循环与stream(非并行)循环,在处理处理数据量比较大的时候效率是一致的,推荐使用stream的形式
+ 对于List删除操作,目前只提供了removeIf方法来实现,并不能使用并行方式
+ 对于lambda表达式的写法
- 当表达式内只有一个返回boolean类型的语句时候语句是可以简写的,例如:

persons.parallelStream().forEach(p->p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime())));

- 当表达式内会有一些复杂处理逻辑时需要加上大括号,这与初始化List参数方式大致一致
persons.parallelStream().forEach(p->{
try {
Thread.sleep(1);
p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime()));
}catch (Exception e){
e.printStackTrace();
}
});
# 七. stream&Lambda表达式常用api方法
+ 流到流之间的转换类
- filter(过滤)
- map(映射转换)
- mapTo[Int|Long|Double] (到基本类型流的转换)
- flatMap(流展开合并)
- flatMapTo[Int|Long|Double]
- sorted(排序)
- distinct(不重复值)
- peek(执行某种操作,流不变,可用于调试)
- limit(限制到指定元素数量)
- skip(跳过若干元素)

+ 流到终值的转换类
- toArray(转为数组)
- reduce(推导结果)
- collect(聚合结果)
- min(最小值)
- max(最大值)
- count (元素个数)
- anyMatch (任一匹配)
- allMatch(所有都匹配)
- noneMatch(一个都不匹配)
- findFirst(选择首元素)
- findAny(任选一元素)

+ 直接遍历类
- forEach (不保证顺序遍历,比如并行流)
- forEachOrdered(顺序遍历)

+ 构造流类
- empty (构造空流)
- of (单个元素的流及多元素顺序流)
- iterate (无限长度的有序顺序流)
- generate (将数据提供器转换成无限非有序的顺序流)
- concat (流的连接)
- Builder (用于构造流的Builder对象)

文章目录
  1. 1. 一. Stream(流)是什么,干什么
  2. 2. 二. Stream操作方式
  3. 3. 三. Stream优势
  4. 4. 四. Stream(流)的基本使用