Java基础快速入门: Stream流核心方法详解

本文纲要

  1. Stream流初体验:传统方式 vs Stream方式
  2. Stream流的思想与特点
  3. 如何获取Stream
    单列集合
    双列集合
    数组
    同种数据类型的多个数据
  4. 中间方法:filter过滤
  5. 其他常用中间方法
    limit 截取
    skip 跳过 concat合并distinct` 去重
  6. 终结方法:forEachcount
  7. Stream流的重要特性:不修改数据源
  8. 收集方法:toListtoSet
  9. 收集方法:toMap
  10. 综合练习

Stream流初体验:传统方式 vs Stream方式

项目代码结构如下:

t 复制代码
mystream/
└── src/
    └── com/
        └── wb/
            └── streamdemo/
                ├── Actor.java 
                ├── MyStream1.java 
                ├── MyStream2.java 
                ├── MyStream3.java 
                ├── MyStream4.java 
                ├── MyStream5.java 
                ├── MyStream6.java 
                ├── MyStream7.java 
                ├── MyStream8.java 
                └── MyStream9.java 

先看一个需求:创建一个集合存储多个字符串,把所有以"张"开头的元素筛选出来,再从中筛选出长度为3的元素,最后遍历。

传统方式 需要手动创建多个集合,循环判断添加:

java 复制代码
// MyStream1.java 传统方式 
ArrayList<String> list1 = new ArrayList<>(List.of("张三丰","张无忌","张翠山","王二麻子","张良","谢广坤"));
 
// 遍历list1,把以"张"开头的元素添加到list2中 
ArrayList<String> list2 = new ArrayList<>();
for (String s : list1) {
    if (s.startsWith("张")) {
        list2.add(s);
    }
}
 
// 遍历list2,把长度为3的元素添加到list3中 
ArrayList<String> list3 = new ArrayList<>();
for (String s : list2) {
    if (s.length() == 3) {
        list3.add(s);
    }
}
// 遍历list3输出 
for (String s : list3) {
    System.out.println(s);
}

 
Stream流方式 可以一行链式调用完成:
 
java 
// MyStream1.java Stream方式 
list1.stream()
     .filter(s -> s.startsWith("张"))
     .filter(s -> s.length() == 3)
     .forEach(s -> System.out.println(s));

两种方式运行结果相同,但Stream流代码量大幅减少、逻辑更清晰。
#mermaid-svg-MwAUHwS7TZ6ALb6v{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-MwAUHwS7TZ6ALb6v .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-MwAUHwS7TZ6ALb6v .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-MwAUHwS7TZ6ALb6v .error-icon{fill:#552222;}#mermaid-svg-MwAUHwS7TZ6ALb6v .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-MwAUHwS7TZ6ALb6v .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-MwAUHwS7TZ6ALb6v .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-MwAUHwS7TZ6ALb6v .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-MwAUHwS7TZ6ALb6v .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-MwAUHwS7TZ6ALb6v .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-MwAUHwS7TZ6ALb6v .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-MwAUHwS7TZ6ALb6v .marker{fill:#333333;stroke:#333333;}#mermaid-svg-MwAUHwS7TZ6ALb6v .marker.cross{stroke:#333333;}#mermaid-svg-MwAUHwS7TZ6ALb6v svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-MwAUHwS7TZ6ALb6v p{margin:0;}#mermaid-svg-MwAUHwS7TZ6ALb6v .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-MwAUHwS7TZ6ALb6v .cluster-label text{fill:#333;}#mermaid-svg-MwAUHwS7TZ6ALb6v .cluster-label span{color:#333;}#mermaid-svg-MwAUHwS7TZ6ALb6v .cluster-label span p{background-color:transparent;}#mermaid-svg-MwAUHwS7TZ6ALb6v .label text,#mermaid-svg-MwAUHwS7TZ6ALb6v span{fill:#333;color:#333;}#mermaid-svg-MwAUHwS7TZ6ALb6v .node rect,#mermaid-svg-MwAUHwS7TZ6ALb6v .node circle,#mermaid-svg-MwAUHwS7TZ6ALb6v .node ellipse,#mermaid-svg-MwAUHwS7TZ6ALb6v .node polygon,#mermaid-svg-MwAUHwS7TZ6ALb6v .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-MwAUHwS7TZ6ALb6v .rough-node .label text,#mermaid-svg-MwAUHwS7TZ6ALb6v .node .label text,#mermaid-svg-MwAUHwS7TZ6ALb6v .image-shape .label,#mermaid-svg-MwAUHwS7TZ6ALb6v .icon-shape .label{text-anchor:middle;}#mermaid-svg-MwAUHwS7TZ6ALb6v .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-MwAUHwS7TZ6ALb6v .rough-node .label,#mermaid-svg-MwAUHwS7TZ6ALb6v .node .label,#mermaid-svg-MwAUHwS7TZ6ALb6v .image-shape .label,#mermaid-svg-MwAUHwS7TZ6ALb6v .icon-shape .label{text-align:center;}#mermaid-svg-MwAUHwS7TZ6ALb6v .node.clickable{cursor:pointer;}#mermaid-svg-MwAUHwS7TZ6ALb6v .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-MwAUHwS7TZ6ALb6v .arrowheadPath{fill:#333333;}#mermaid-svg-MwAUHwS7TZ6ALb6v .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-MwAUHwS7TZ6ALb6v .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-MwAUHwS7TZ6ALb6v .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MwAUHwS7TZ6ALb6v .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-MwAUHwS7TZ6ALb6v .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MwAUHwS7TZ6ALb6v .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-MwAUHwS7TZ6ALb6v .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-MwAUHwS7TZ6ALb6v .cluster text{fill:#333;}#mermaid-svg-MwAUHwS7TZ6ALb6v .cluster span{color:#333;}#mermaid-svg-MwAUHwS7TZ6ALb6v div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-MwAUHwS7TZ6ALb6v .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-MwAUHwS7TZ6ALb6v rect.text{fill:none;stroke-width:0;}#mermaid-svg-MwAUHwS7TZ6ALb6v .icon-shape,#mermaid-svg-MwAUHwS7TZ6ALb6v .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MwAUHwS7TZ6ALb6v .icon-shape p,#mermaid-svg-MwAUHwS7TZ6ALb6v .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-MwAUHwS7TZ6ALb6v .icon-shape .label rect,#mermaid-svg-MwAUHwS7TZ6ALb6v .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MwAUHwS7TZ6ALb6v .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-MwAUHwS7TZ6ALb6v .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-MwAUHwS7TZ6ALb6v :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 原始集合
stream 获取流
filter 过滤: 以张开头
filter 再过滤: 长度为3
forEach 输出

Stream流的思想与特点

Stream流可以形象地看作一条"流水线":

  1. 获取Stream流:创建流水线并把数据放上去。
  2. 中间方法:流水线上的各种加工操作(过滤、映射、去重等),执行完可以继续调用其它方法,返回的还是流。
  3. 终结方法:流水线的最后一步,如遍历、计数、收集,执行后流就关闭了,不能再使用。

#mermaid-svg-nzugWpAotWI2OTYN{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-nzugWpAotWI2OTYN .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-nzugWpAotWI2OTYN .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-nzugWpAotWI2OTYN .error-icon{fill:#552222;}#mermaid-svg-nzugWpAotWI2OTYN .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-nzugWpAotWI2OTYN .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-nzugWpAotWI2OTYN .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-nzugWpAotWI2OTYN .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-nzugWpAotWI2OTYN .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-nzugWpAotWI2OTYN .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-nzugWpAotWI2OTYN .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-nzugWpAotWI2OTYN .marker{fill:#333333;stroke:#333333;}#mermaid-svg-nzugWpAotWI2OTYN .marker.cross{stroke:#333333;}#mermaid-svg-nzugWpAotWI2OTYN svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-nzugWpAotWI2OTYN p{margin:0;}#mermaid-svg-nzugWpAotWI2OTYN .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-nzugWpAotWI2OTYN .cluster-label text{fill:#333;}#mermaid-svg-nzugWpAotWI2OTYN .cluster-label span{color:#333;}#mermaid-svg-nzugWpAotWI2OTYN .cluster-label span p{background-color:transparent;}#mermaid-svg-nzugWpAotWI2OTYN .label text,#mermaid-svg-nzugWpAotWI2OTYN span{fill:#333;color:#333;}#mermaid-svg-nzugWpAotWI2OTYN .node rect,#mermaid-svg-nzugWpAotWI2OTYN .node circle,#mermaid-svg-nzugWpAotWI2OTYN .node ellipse,#mermaid-svg-nzugWpAotWI2OTYN .node polygon,#mermaid-svg-nzugWpAotWI2OTYN .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-nzugWpAotWI2OTYN .rough-node .label text,#mermaid-svg-nzugWpAotWI2OTYN .node .label text,#mermaid-svg-nzugWpAotWI2OTYN .image-shape .label,#mermaid-svg-nzugWpAotWI2OTYN .icon-shape .label{text-anchor:middle;}#mermaid-svg-nzugWpAotWI2OTYN .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-nzugWpAotWI2OTYN .rough-node .label,#mermaid-svg-nzugWpAotWI2OTYN .node .label,#mermaid-svg-nzugWpAotWI2OTYN .image-shape .label,#mermaid-svg-nzugWpAotWI2OTYN .icon-shape .label{text-align:center;}#mermaid-svg-nzugWpAotWI2OTYN .node.clickable{cursor:pointer;}#mermaid-svg-nzugWpAotWI2OTYN .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-nzugWpAotWI2OTYN .arrowheadPath{fill:#333333;}#mermaid-svg-nzugWpAotWI2OTYN .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-nzugWpAotWI2OTYN .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-nzugWpAotWI2OTYN .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nzugWpAotWI2OTYN .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-nzugWpAotWI2OTYN .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nzugWpAotWI2OTYN .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-nzugWpAotWI2OTYN .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-nzugWpAotWI2OTYN .cluster text{fill:#333;}#mermaid-svg-nzugWpAotWI2OTYN .cluster span{color:#333;}#mermaid-svg-nzugWpAotWI2OTYN div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-nzugWpAotWI2OTYN .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-nzugWpAotWI2OTYN rect.text{fill:none;stroke-width:0;}#mermaid-svg-nzugWpAotWI2OTYN .icon-shape,#mermaid-svg-nzugWpAotWI2OTYN .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nzugWpAotWI2OTYN .icon-shape p,#mermaid-svg-nzugWpAotWI2OTYN .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-nzugWpAotWI2OTYN .icon-shape .label rect,#mermaid-svg-nzugWpAotWI2OTYN .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nzugWpAotWI2OTYN .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-nzugWpAotWI2OTYN .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-nzugWpAotWI2OTYN :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 获取Stream流
中间操作1 如filter
中间操作2 如map
...
终结操作 如forEach/count/collect
流关闭

下面这张流程图也对应了饮料工厂的流水线比喻:

检查瓶子 → 消毒 → 灌装 → 密封 → 包装。每一步都可以保留或剔除瓶子,与Stream的中间操作逻辑一致。
#mermaid-svg-mthO5rwuGJTiIqpo{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mthO5rwuGJTiIqpo .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mthO5rwuGJTiIqpo .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mthO5rwuGJTiIqpo .error-icon{fill:#552222;}#mermaid-svg-mthO5rwuGJTiIqpo .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mthO5rwuGJTiIqpo .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mthO5rwuGJTiIqpo .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mthO5rwuGJTiIqpo .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mthO5rwuGJTiIqpo .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mthO5rwuGJTiIqpo .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mthO5rwuGJTiIqpo .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mthO5rwuGJTiIqpo .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mthO5rwuGJTiIqpo .marker.cross{stroke:#333333;}#mermaid-svg-mthO5rwuGJTiIqpo svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mthO5rwuGJTiIqpo p{margin:0;}#mermaid-svg-mthO5rwuGJTiIqpo .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-mthO5rwuGJTiIqpo .cluster-label text{fill:#333;}#mermaid-svg-mthO5rwuGJTiIqpo .cluster-label span{color:#333;}#mermaid-svg-mthO5rwuGJTiIqpo .cluster-label span p{background-color:transparent;}#mermaid-svg-mthO5rwuGJTiIqpo .label text,#mermaid-svg-mthO5rwuGJTiIqpo span{fill:#333;color:#333;}#mermaid-svg-mthO5rwuGJTiIqpo .node rect,#mermaid-svg-mthO5rwuGJTiIqpo .node circle,#mermaid-svg-mthO5rwuGJTiIqpo .node ellipse,#mermaid-svg-mthO5rwuGJTiIqpo .node polygon,#mermaid-svg-mthO5rwuGJTiIqpo .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mthO5rwuGJTiIqpo .rough-node .label text,#mermaid-svg-mthO5rwuGJTiIqpo .node .label text,#mermaid-svg-mthO5rwuGJTiIqpo .image-shape .label,#mermaid-svg-mthO5rwuGJTiIqpo .icon-shape .label{text-anchor:middle;}#mermaid-svg-mthO5rwuGJTiIqpo .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-mthO5rwuGJTiIqpo .rough-node .label,#mermaid-svg-mthO5rwuGJTiIqpo .node .label,#mermaid-svg-mthO5rwuGJTiIqpo .image-shape .label,#mermaid-svg-mthO5rwuGJTiIqpo .icon-shape .label{text-align:center;}#mermaid-svg-mthO5rwuGJTiIqpo .node.clickable{cursor:pointer;}#mermaid-svg-mthO5rwuGJTiIqpo .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-mthO5rwuGJTiIqpo .arrowheadPath{fill:#333333;}#mermaid-svg-mthO5rwuGJTiIqpo .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-mthO5rwuGJTiIqpo .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-mthO5rwuGJTiIqpo .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mthO5rwuGJTiIqpo .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-mthO5rwuGJTiIqpo .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mthO5rwuGJTiIqpo .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-mthO5rwuGJTiIqpo .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-mthO5rwuGJTiIqpo .cluster text{fill:#333;}#mermaid-svg-mthO5rwuGJTiIqpo .cluster span{color:#333;}#mermaid-svg-mthO5rwuGJTiIqpo div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-mthO5rwuGJTiIqpo .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mthO5rwuGJTiIqpo rect.text{fill:none;stroke-width:0;}#mermaid-svg-mthO5rwuGJTiIqpo .icon-shape,#mermaid-svg-mthO5rwuGJTiIqpo .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mthO5rwuGJTiIqpo .icon-shape p,#mermaid-svg-mthO5rwuGJTiIqpo .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-mthO5rwuGJTiIqpo .icon-shape .label rect,#mermaid-svg-mthO5rwuGJTiIqpo .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mthO5rwuGJTiIqpo .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-mthO5rwuGJTiIqpo .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-mthO5rwuGJTiIqpo :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 合格
不合格
空瓶上流水线
检查
消毒
剔除
灌装
密封
包装出厂

如何获取Stream流

Stream 流只能在以下几种数据源上获得:

数据源 获取方式
单列集合(Collection实现类) 集合对象.stream()
双列集合(Map 不能直接获取,需通过 keySet().stream()entrySet().stream() 间接获取
数组 Arrays.stream(数组)
同种数据类型的多个数据 Stream.of(数据1, 数据2, ...)

代码演示:

java 复制代码
// MyStream2.java 
 
// ========== 单列集合 ==========
ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.stream().forEach(s -> System.out.println(s));
 
// ========== 双列集合 ==========
HashMap<String, Integer> hm = new HashMap<>();
hm.put("zhangsan", 23);
hm.put("lisi", 24);
hm.put("wangwu", 25);
hm.put("zhaoliu", 26);
hm.put("qianqi", 27);
 
// 方式1:通过keySet获取键的流 
hm.keySet().stream().forEach(s -> System.out.println(s));
 
// 方式2:通过entrySet获取键值对对象的流 
hm.entrySet().stream().forEach(s -> System.out.println(s));
 
// ========== 数组 ==========
int[] arr = {1, 2, 3, 4, 5};
Arrays.stream(arr).forEach(s -> System.out.println(s));
 
// ========== 同种数据类型的多个数据 ==========
Stream.of(1, 2, 3, 4, 5, 6, 7, 8).forEach(s -> System.out.println(s));

注意:双列集合需要间接获取,keySet() 得到的是所有键的 SetentrySet() 得到的是包含键值对的Set,之后再用 .stream() 转为流

中间方法:filter过滤

filter 方法接收一个 Predicate 接口,只有一个抽象方法 boolean test(T t),用来判断数据是否留下。

  • 返回 true:当前数据保留
  • 返回 false:当前数据剔除

代码推导过程:

  1. 匿名内部类方式(最繁琐)
  2. 完整Lambda表达式
  3. 简化Lambda表达式(方法体只有一行时可省略大括号、return和分号)
java 复制代码
// MyStream3.java 
ArrayList<String> list = new ArrayList<>();
list.add("张三丰");
list.add("张无忌");
list.add("张翠山");
list.add("王二麻子");
list.add("张良");
list.add("谢广坤");
 
// 方式一:匿名内部类 
list.stream().filter(
    new Predicate<String>() {
        @Override 
        public boolean test(String s) {
            boolean result = s.startsWith("张");
            return result;
        }
    }
).forEach(s -> System.out.println(s));
 
// 方式二:完整Lambda(Predicate接口只有一个抽象方法,可以用Lambda简化)
list.stream().filter(
    (String s) -> {
        boolean result = s.startsWith("张");
        return result;
    }
).forEach(s -> System.out.println(s));
 
// 方式三:简化Lambda(参数类型可省略,单行方法体可省略大括号和return)
list.stream().filter(s -> s.startsWith("张")).forEach(s -> System.out.println(s));

关键点:filter 会遍历流中的每个数据,s 依次代表每个元素,我们的判断逻辑返回布尔值即可。

其他常用中间方法

1 ) limit 截取

保留前n个元素,后面的丢弃。

java 复制代码
// MyStream4.method1()
list.stream().limit(2).forEach(s -> System.out.println(s));
// 输出:张三丰、张无忌(只保留前两个)

2 ) skip 跳过

跳过前n个元素,保留后面的。

java 复制代码
// MyStream4.method2()
list.stream().skip(2).forEach(s -> System.out.println(s));
// 输出:张翠山、王二麻子、张良、谢广坤...(跳过了前两个)

3 ) concat 合并

静态方法,将两个流合并为一个。

java 复制代码
// MyStream4.method3()
ArrayList<String> list1 = new ArrayList<>(List.of("张三丰","张无忌","张翠山"));
ArrayList<String> list2 = new ArrayList<>(List.of("王二麻子","张良","谢广坤"));
 
// 写法一:分步 
Stream<String> stream1 = list1.stream();
Stream<String> stream2 = list2.stream();
Stream<String> stream3 = Stream.concat(stream1, stream2);
stream3.forEach(s -> System.out.println(s));
 
// 写法二:链式 
Stream.concat(list1.stream(), list2.stream()).forEach(s -> System.out.println(s));

4 ) distinct 去重

去除流中重复的元素,底层依赖 hashCode()equals() 方法。

java 复制代码
// MyStream4.method4()
list.add("谢广坤");
list.add("谢广坤");
list.add("谢广坤");
list.stream().distinct().forEach(s -> System.out.println(s));
// 多个谢广坤只会保留一个 

终结方法:forEach与count

终结方法是流的最后一步,执行后流关闭。

1 ) forEach

遍历流中的每个元素,参数是 Consumer 接口,抽象方法 void accept(T t)

同样支持从匿名内部类到Lambda的简化:

java 复制代码
// MyStream5.method1()
// 方式一:匿名内部类 
list.stream().forEach(
    new Consumer<String>() {
        @Override 
        public void accept(String s) {
            System.out.println(s);
        }
    }
);
 
// 方式二:完整Lambda 
list.stream().forEach(
    (String s) -> {
        System.out.println(s);
    }
);
 
// 方式三:简化Lambda 
list.stream().forEach(s -> System.out.println(s));

2 ) count

返回流中元素个数,返回类型为 long

java 复制代码
// MyStream5.main()
long count = list.stream().count();
System.out.println(count);  // 6 

Stream流的重要特性:不修改数据源

Stream流 只能操作流上的数据,不会修改原始集合或数组。

java 复制代码
// MyStream6.java 
ArrayList<Integer> list = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
    list.add(i);
}
 
// 流中过滤出偶数,并输出 
list.stream()
    .filter(number -> number % 2 == 0)
    .forEach(number -> System.out.println(number));  // 输出2,4,6,8,10 
 
// 再次遍历原集合,仍然包含1~10所有数字 
for (Integer integer : list) {
    System.out.println(integer);  // 1,2,3,...,10 
}

即使对流做了过滤、去重等操作,原集合内容不变。如果想把结果保留下来,需要使用收集方法。

收集方法:toList 和 toSet

collect 方法负责将流中的数据收集到一个新的集合中,它需要借助 Collectors 工具类来创建容器并添加数据。

  • Collectors.toList():收集到 List 集合
  • Collectors.toSet():收集到 Set 集合(自动去重)
java 复制代码
// MyStream7.java 
ArrayList<Integer> list1 = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
    list1.add(i);
}
list1.add(10);
list1.add(10);
list1.add(10);  // 故意添加重复元素 
 
// 收集到List(保留重复)
List<Integer> list = list1.stream()
    .filter(number -> number % 2 == 0)
    .collect(Collectors.toList());
System.out.println(list);  // [2, 4, 6, 8, 10, 10, 10, 10, 10]
 
// 收集到Set(自动去重)
Set<Integer> set = list1.stream()
    .filter(number -> number % 2 == 0)
    .collect(Collectors.toSet());
System.out.println(set);   // [2, 4, 6, 8, 10]

分工说明:

  • filter 负责过滤数据;
  • collect 负责把流中最终的数据收集起来;
  • Collectors.toList() / Collectors.toSet() 在底层创建对应集合并添加数据。

收集方法:toMap

当需要把流中的数据收集到双列集合(Map)时,需要使用 Collectors.toMap(),并指定 的获取方式

java 复制代码
// MyStream8.java 
ArrayList<String> list = new ArrayList<>();
list.add("zhangsan,23");
list.add("lisi,24");
list.add("wangwu,25");
 
Map<String, Integer> map = list.stream().filter(
    s -> {
        String[] split = s.split(",");
        int age = Integer.parseInt(split[1]);
        return age >= 24;          // 只保留年龄>=24的人 
    }
).collect(Collectors.toMap(
    s -> s.split(",")[0],         // 如何获取键(姓名)
    s -> Integer.parseInt(s.split(",")[1])  // 如何获取值(年龄)
));
 
System.out.println(map); // {lisi=24, wangwu=25}

toMap 方法需要两个Lambda参数:

  1. 第一个Lambda:如何从流中的一个元素得到Map的键
  2. 第二个Lambda:如何从流中的一个元素得到Map的值

同样可以进行简化(单参数可省略括号,单行可省略return和大括号)。

综合练习

下面用一个综合案例巩固学过的Stream操作。

需求:分别存储6位男演员和6位女演员,按要求处理:

  1. 男演员只要名字为3个字的前两人;
  2. 女演员只要姓杨的,且排除第一个;
  3. 将过滤后的男女演员姓名合并;
  4. 把合并后的每个姓名包装成演员对象(Actor),然后遍历输出。

Actor 类:

java 复制代码
// Actor.java 
package com.wb.streamdemo;
 
public class Actor {
    private String name;
 
    public Actor() {
    }
 
    public Actor(String name) {
        this.name = name;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    @Override 
    public String toString() {
        return "Actor{" +
                "name='" + name + '\'' +
                '}';
    }
}

主测试类:

java 复制代码
// MyStream9.java 
package com.wb.streamdemo;
 
import java.util.ArrayList;
import java.util.stream.Stream;
 
public class MyStream9 {
    public static void main(String[] args) {
        ArrayList<String> manList = new ArrayList<>();
        manList.add("张国立");
        manList.add("张晋");
        manList.add("刘烨");
        manList.add("郑伊健");
        manList.add("徐峥");
        manList.add("王宝强");
 
        ArrayList<String> womanList = new ArrayList<>();
        womanList.add("郑爽");
        womanList.add("杨紫");
        womanList.add("关晓彤");
        womanList.add("张天爱");
        womanList.add("杨幂");
        womanList.add("赵丽颖");
 
        // 1. 男演员名字为3个字的前两人 
        Stream<String> stream1 = manList.stream()
                .filter(name -> name.length() == 3)
                .limit(2);
        // 结果:张国立、郑伊健 
 
        // 2. 女演员姓杨的,跳过第一个 
        Stream<String> stream2 = womanList.stream()
                .filter(name -> name.startsWith("杨"))
                .skip(1);
        // 结果:杨幂(跳过了杨紫)
 
        // 3. 合并流 
        // 4. 将每个姓名封装为Actor对象并遍历 
        Stream.concat(stream1, stream2)
              .forEach(name -> {
                  Actor actor = new Actor(name);
                  System.out.println(actor);
              });
    }
}

输出结果:

log 复制代码
Actor{name='张国立'}
Actor{name='郑伊健'}
Actor{name='杨幂'}

过程图解:
#mermaid-svg-lzvuM8u1bKkyUV2V{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-lzvuM8u1bKkyUV2V .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-lzvuM8u1bKkyUV2V .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-lzvuM8u1bKkyUV2V .error-icon{fill:#552222;}#mermaid-svg-lzvuM8u1bKkyUV2V .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-lzvuM8u1bKkyUV2V .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-lzvuM8u1bKkyUV2V .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-lzvuM8u1bKkyUV2V .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-lzvuM8u1bKkyUV2V .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-lzvuM8u1bKkyUV2V .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-lzvuM8u1bKkyUV2V .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-lzvuM8u1bKkyUV2V .marker{fill:#333333;stroke:#333333;}#mermaid-svg-lzvuM8u1bKkyUV2V .marker.cross{stroke:#333333;}#mermaid-svg-lzvuM8u1bKkyUV2V svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-lzvuM8u1bKkyUV2V p{margin:0;}#mermaid-svg-lzvuM8u1bKkyUV2V .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-lzvuM8u1bKkyUV2V .cluster-label text{fill:#333;}#mermaid-svg-lzvuM8u1bKkyUV2V .cluster-label span{color:#333;}#mermaid-svg-lzvuM8u1bKkyUV2V .cluster-label span p{background-color:transparent;}#mermaid-svg-lzvuM8u1bKkyUV2V .label text,#mermaid-svg-lzvuM8u1bKkyUV2V span{fill:#333;color:#333;}#mermaid-svg-lzvuM8u1bKkyUV2V .node rect,#mermaid-svg-lzvuM8u1bKkyUV2V .node circle,#mermaid-svg-lzvuM8u1bKkyUV2V .node ellipse,#mermaid-svg-lzvuM8u1bKkyUV2V .node polygon,#mermaid-svg-lzvuM8u1bKkyUV2V .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-lzvuM8u1bKkyUV2V .rough-node .label text,#mermaid-svg-lzvuM8u1bKkyUV2V .node .label text,#mermaid-svg-lzvuM8u1bKkyUV2V .image-shape .label,#mermaid-svg-lzvuM8u1bKkyUV2V .icon-shape .label{text-anchor:middle;}#mermaid-svg-lzvuM8u1bKkyUV2V .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-lzvuM8u1bKkyUV2V .rough-node .label,#mermaid-svg-lzvuM8u1bKkyUV2V .node .label,#mermaid-svg-lzvuM8u1bKkyUV2V .image-shape .label,#mermaid-svg-lzvuM8u1bKkyUV2V .icon-shape .label{text-align:center;}#mermaid-svg-lzvuM8u1bKkyUV2V .node.clickable{cursor:pointer;}#mermaid-svg-lzvuM8u1bKkyUV2V .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-lzvuM8u1bKkyUV2V .arrowheadPath{fill:#333333;}#mermaid-svg-lzvuM8u1bKkyUV2V .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-lzvuM8u1bKkyUV2V .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-lzvuM8u1bKkyUV2V .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lzvuM8u1bKkyUV2V .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-lzvuM8u1bKkyUV2V .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lzvuM8u1bKkyUV2V .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-lzvuM8u1bKkyUV2V .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-lzvuM8u1bKkyUV2V .cluster text{fill:#333;}#mermaid-svg-lzvuM8u1bKkyUV2V .cluster span{color:#333;}#mermaid-svg-lzvuM8u1bKkyUV2V div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-lzvuM8u1bKkyUV2V .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-lzvuM8u1bKkyUV2V rect.text{fill:none;stroke-width:0;}#mermaid-svg-lzvuM8u1bKkyUV2V .icon-shape,#mermaid-svg-lzvuM8u1bKkyUV2V .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lzvuM8u1bKkyUV2V .icon-shape p,#mermaid-svg-lzvuM8u1bKkyUV2V .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-lzvuM8u1bKkyUV2V .icon-shape .label rect,#mermaid-svg-lzvuM8u1bKkyUV2V .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lzvuM8u1bKkyUV2V .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-lzvuM8u1bKkyUV2V .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-lzvuM8u1bKkyUV2V :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 男演员列表
过滤: 名字长度为3
截取前两个: limit 2
stream1: 张国立,郑伊健
女演员列表
过滤: 姓杨
跳过第一个: skip 1
stream2: 杨幂
Stream.concat 合并
map: 封装为Actor对象
遍历打印

该案例综合运用了 filterlimitskipconcat 以及 forEach 方法,充分展示了Stream流对集合数据处理的链式与声明式风格

总结

Stream 流是Java 8引入的强大数据处理工具,通过获取流 → 中间操作 → 终结操作的流水线模型,可以极大简化集合/数组的复杂处理逻辑。

分类 方法 作用
获取流 stream() / Arrays.stream() / Stream.of() 将数据源转成流
中间操作 filter 过滤数据
中间操作 limit / skip 截取前n个 / 跳过前n个
中间操作 distinct 去重
中间操作 concat (静态) 合并两个流
终结操作 forEach 遍历每个元素
终结操作 count 统计元素个数
终结操作 collect + Collectors.toList/toSet/toMap 将流收集为集合

掌握这些核心方法后,你就可以在实际项目中写出更简洁、更易读的Java代码了。