day09[Stream 流,方法引用]
第一章: Stream流
这里的 Stream,是在 Java 8 中,因为 Lambda 带来的函数式编程,引入的一个全新的 Stream 概念,用于解决已有集合中既有的弊端。
-
- 概述
- 传统集合的遍历代码,几乎所有的集合(Collection,Map 等)都支持间接或直接的遍历操作,而当我们需要对集合中的元素进行操作的时候,除了必需的添加、删除、获取外,最典型的就是集合的遍历。
//传统的写法 public class Test1{ public static void main(String[] args) { ArrayList<String>list=new ArrayList<>(); list.add("李文杰"); list.add("李文集"); list.add("李文洁"); list.add("王智雅"); list.add("何舒雅"); list.add("王文文"); //传统的写法 for(String ss:list){ System.out.println(ss); } } 这是一段非常简单的集合遍历操作,集合中的字符串都进行输出操作 |
-
- for循环遍历的弊端
- 为什么使用循环?因为要进行遍历,但循环是遍历的唯一方式吗?遍历是指每一个元素逐一进行处理,而并不是从第一个到最后一个顺次处理的循环,前者是目的,后者是方式。
- 试想一下,如果希望对集合中的元素进行筛选过滤:
- 1、将集合 A 根据条件过滤为子集合 B
- 2、然后在根据条件过滤为子集合 C
Java jdk8 之前的代码:
public class Test1{ public static void main(String[] args) { ArrayList<String>list=new ArrayList<>(); list.add("李文杰"); list.add("李文集"); list.add("李文洁"); list.add("王智雅"); list.add("何舒雅"); list.add("王文文"); ArrayList<String>li=new ArrayList<>(); //传统的写法 for(String ss:list){ if(ss.startsWith("李")){ li.add(ss); } } for(String ss2: li){ System.out.println(ss2); } } } |
分析这段代码中两个 for 循环:
1、 首先筛选所有姓李的人
2、 然后存入到新的集合中
3、 最后进行筛选结果的输出
每当我们需要对集合中的元素进行操作的时候,总是需要进行循环,循环,再循环。循环只 是做事的方式而不是目的,另一方面,使用线性循环就意味着只能遍历一次,如果希望再次
遍历,只能在执行一次代码。
- Lambda 的衍生物 Stream 能给我带来更加优雅简洁的写法
Stream流的优雅写法
//体验Stream 流 public class Test1{ public static void main(String[] args) { ArrayList<String>list=new ArrayList<>(); list.add("李文杰"); list.add("李文集"); list.add("李文洁"); list.add("王智雅"); list.add("何舒雅"); list.add("王文文"); //使用Stream 流输出 list.stream().filter(s->s.startsWith("李")). filter(s->s.length()==3).forEach(s->System.out.println(s)); //foreach 代表的就是for循环 } } 总结为: 获取流,过滤流,过滤流,开头为李,长度为 3,逐一输出,代码中并没有体现使用线性循环或其他任何算法进行遍历。 |
-
- 流式思想的概述
- 整体来看,流式思想类似于工厂车间的“生产流水线”。
- Stream 流其实就是一个集合元素的函数模型,它并不是集合,也不是数据结构,其本身也不存储任何元素。
- Stream 流是一个来自数据源的元素队列。
- 元素是特定类型的对象,形成一个队列,java 中的 Stream 流不存储元素,而是按需计算。
- 数据域流的来源,可以是集合、数组等。
- 当使用一个流的时候,通常包括三个基本步骤:
- 获取一个数据源-->数据转换,执行操作获取想要的结果,每次转换原有 Stream 流对象不改变,返回一个新的 Stream 对象,这就允许对其操作可以像链条一样排列,形成一个管道。
- 获取流Stream
- 位于java.util.stream 包中,interface Stream<T>接口是 Java 8 新加入的最常用的流接口(这并不是一个函数式接口)
- List集合 Set集合 数组获取流的方法:
- 获取一个流非常简单,有以下几种常用方式:
- 所有的 Collection 集合(List,Set)都可以通过 stream 默认方法获取流。
- Stream 接口的静态方法 of 可以获取数组对应的流.
//Collection 集合获取Stream流 public class Test2 { public static void main(String[] args) { //List集合获取流 List<String>list=new ArrayList<>(); Stream<String>listStream=list.stream();
//Set集合获取流 Set<String>set=new HashSet<>(); Stream<String>setStream=set.stream();
//数组获取流Stream String[]array={"13","34","65","76","23"}; Stream<String>stream=Stream.of(array); } } |
- 根据Map集合获取流:
- java.util.Map 接口不是 Collection 的子接口,其 K-V 数据结构不符合流元素的单一特征,所以获取对应的流需要分 key、value 或 entry 等情况。
//Map集合获取流Stream流 public class Test2 { public static void main(String[] args) { //Map集合获取流 Map<String,String>map=new HashMap<>(); //Map集合Stream流获取键 Stream<String>mapStream=map.keySet().stream(); //Map集合Stream流获取值 Stream<String>maps=map.values().stream(); //Map集合通过EntrySet来获取Stream流,得到键和值 Stream<Map.Entry<String,String>>entry=map.entrySet().stream(); } } |
-
- 常用的方法
- 延迟方法:返回值类型任然是一个 Stream 对象,因此支持链式调用(除了终结方法以
外,其余方法都是延迟方法)
- 终结方法:返回值类型不再是 Stream 接口,因此不再支持链式调用。
1.6 Foreach
- 虽然名字叫foreach,但是与增强的for循环不同。
void forEach(Consumer<? super T> action) d |
该方法接收一个Consumer接口的函数,会将每一个流元素交给函数进行处理
基本使用:
public class Test3 { public static void main(String[] args) { //foreach示例 Stream<String>stream=Stream.of("李文杰","张文集","王兰君","李军帽"); stream.forEach(s -> System.out.println(s)); } } |
1.7 Filter
过滤:可以通过Filter方法将一个流转换为另一个流
Stream<T> filter(Predicate<? super T> predicate) |
该方法接收一个 Predicate 函数式接口参数
基本使用:
public class Test3 { public static void main(String[] args) { //Fifter过滤器 Stream<String>stream=Stream.of("李文杰","张文集","王兰君","李军帽"); Stream<String>newstream=stream.filter(s ->s.startsWith("李")); newstream.forEach(s -> System.out.println(s)); } } |
1.8 map映射
1 如果需要将流中的元素映射到另一个流中,可以使用 map 方法.
<R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper) |
基本使用:
public class Test3 { public static void main(String[] args) { //map映射 Stream<String>stream1=Stream.of("12","34","45","66","99"); Stream<Integer>stream2=stream1.map(s ->Integer.parseInt(s)); stream2.forEach(s-> System.out.println(s+10)); } } |
1.9 count 统计个数
流通过count方法来计算流中的个数
long count(); 返回此流中的元素数。 |
基本使用
//count方法的使用。 public class Test3 { public static void main(String[] args) { //count方法来统计流的个数 Stream<String>stream3=Stream.of("刘备","张飞","卧龙","凤雏"); System.out.println("统计流的个数:"+stream3.count()); } } |
2.0 Limit 提取前几个元素
Limit 方法可以对流进行截取,只取前n个。
Stream<T> limit(long maxSize) |
基本使用:
public class Test3 { public static void main(String[] args) { //limit方法使用,对流进行截取 Stream<String>stream4=Stream.of("赵云","关羽","黄忠","马超"); Stream<String>stream5=stream4.limit(3); stream5.forEach(s -> System.out.println(s)); } } |
2.1 Skip 跳过前几个元素
Skip方法可以对流实现跳过的操作,继续执行以下的数据
Stream<T> skip (long n) ; 如果流的当前长度大于 n,则跳过前 n 个,否则将会得到一个长度为 0 的空流 |
基本使用:
public class Test3 { public static void main(String[] args) { //Skip方法实现对流元素的跳过 Stream<String>stream5=Stream.of("赵云","关羽","黄忠","马超"); Stream<String>stream6=stream5.skip(2); stream6.forEach(s -> System.out.println(s)); } } |
2.2 concat组合
如果有两个流,希望合并成为一个流,那么可以使用 Stream 接口静态方法 concat
static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) |
基本使用:
public class Test3 { public static void main(String[] args) { //concat 如果两个流,希望合并为一个流,那么就可以进行组合 Stream<String>stream7=Stream.of("马超"); Stream<String>stream8=Stream.of("西凉人"); Stream<String>stream9=Stream.concat(stream7,stream8); stream9.forEach(s -> System.out.println(s));} } |