目录
Java8的两个重大改变,一个是Lambda表达式,另一个就是的Stream API表达式
Stream 是Java8中处理集合的关键抽象概念,它可以对集合进行非常复杂的查找、过滤、筛选等操作
1.为什么使用stream流
当我们需要对集合中的元素进行操作的时候,除了必需的添加、删除、获取外,最典型的就是集合遍历。我们来体验 集合操作数据的弊端,需求如下:
案例:
一个ArrayList集合中存储有以下数据:张三,王五,王小二,王老五,李四
需求:1.拿到所有姓王的 2.拿到名字长度为3个字的 3.打印这些数据public class Test01 { public static void main(String[] args) { //需求:1.拿到所有王姓 2.拿到名字长度为3个字的 3.打印 List<String> list = new ArrayList<>(); list.add("张三"); list.add("王五"); list.add("王小二"); list.add("王老五"); list.add("李四"); //1.拿到王姓 List<String> list01 = new ArrayList<>(); for (String n : list){ if (n.startsWith("王")){ //startsWith:开头为王 list01.add(n); } } //2.拿到名字长度为3个字的 List<String> list02 = new ArrayList<>(); for (String n : list01){ if (n.length()==3){ //长度为3个字的 list02.add(n); } } //3.打印 for (String s : list02){ System.out.println(s); } } }
分析:
循环遍历的弊端
这段代码中含有三个循环,每一个作用不同:
首先筛选所有姓王的人;
然后筛选名字有三个字的人;
最后进行对结果进行打印输出。
每当我们需要对集合中的元素进行操作的时候,总是需要进行循环、循环、再循环。这是理所当然的么?不是。循环 是做事情的方式,而不是目的。每个需求都要循环一次,还要搞一个新集合来装数据,如果希望再次遍历,只能再使 用另一个循环从头开始。
那Stream能给我们带来怎样更加优雅的写法呢?
Stream初体验
public class Test01 { public static void main(String[] args) { //需求:1.拿到所有王姓 2.拿到名字长度为3个字的 3.打印 List<String> list = new ArrayList<>(); list.add("张三"); list.add("王五"); list.add("王小二"); list.add("王老五"); list.add("李四"); //stream流完成 list.stream().filter(t->t.startsWith("王")).filter(t->t.length()==3).forEach(System.out::println); } }
2. Stream流的原理
Stream流式思想类似于工厂车间的“生产流水线”,Stream流不是一种数据结构,不保存数据,而是对数据进行加工 处理。Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。
3.步骤
- 获取Stream流对象
- 中间操作---返回类型还是Stream流对象。
- 终止操作---不在是Stream流对象
4.获取Stream流对象的方式
- 通过集合对象调用stream()
- 通过Arrays获取stream流对象
- 通过Stream流里面of方法
实例:
public class Test02 { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("张三"); list.add("李四"); list.add("王五"); //第一种:通过集合对象调用stream Stream<String> stream = list.stream();//获取Stream流 stream.forEach(System.out::println); //打印 //第二种:通过Arrays获取stream流对象 String[] arr = {}; Stream<String> stream1 = Arrays.stream(arr); //第三种:通过Stream流里面of方法 Stream<Integer> integerStream = Stream.of(1, 2, 3, 4); //上面三种流都是串行流。 并行流: Stream<String> stringStream = list.parallelStream(); stringStream.forEach(System.out::println); } }
5.Stream流的api方法
filter:过滤
filter:根据指定的条件过滤流中的元素,只保留满足条件的元素
forEach:结束
forEach:对流中的每个元素执行指定的操作,处理完所有元素后才结束流
count():计算
count():计算流中元素的数量,处理完所有元素后返回数量值
min:最小
min: 根据指定的比较器,找到流中的最小元素,处理完所有元素后返回最小元素
max:最大
max: 根据指定的比较器,找到流中的最大元素,处理完所有元素后返回最大元素
collect:收集
collect: 根据指定的收集器,将流中的元素进行收集操作,处理完所有元素后返回一个结果对象。
sorted:排序
sorted(): 对流中的元素进行排序,需要保持状态来进行排序操作。
findFirst:返回流中的第一个
findFirst(): 返回流中的第一个元素,如果流为空则返回一个空的Optional对象。
findAny:返回流中的任意一个
findAny(): 返回流中的任意一个元素,如果流为空则返回一个空的Optional对象anyMatch:是否存在满足指定条件
anyMatch 判断流中是否存在满足指定条件的元素,一旦找到满足条件的元素,立即返回true,不再继续处理剩余元素。
allMatch:所有元素是否都满足指定条件
allMatch 判断流中的所有元素是否都满足指定条件,一旦发现有不满足条件的元素,立即返回false,不再继续处理剩余元素。
案例:
public class Test03 { public static void main(String[] args) { //举个简单的例子: //假设有一个Person类和一个Person列表 // 现在有两个需求:1)找到年龄大于18岁的人并输出;2)找出所有中国人的数量 List<Person> personList = new ArrayList<>(); personList.add(new Person("欧阳雪", 18, "中国", 'F')); personList.add(new Person("Tom", 24, "美国", 'M')); personList.add(new Person("Harley", 22, "英国", 'F')); personList.add(new Person("向天笑", 20, "中国", 'M')); personList.add(new Person("李康", 22, "中国", 'M')); personList.add(new Person("小梅", 20, "中国", 'F')); personList.add(new Person("何雪", 21, "中国", 'F')); personList.add(new Person("李康", 22, "中国", 'M')); //1. 年龄大于18 filter:过滤掉不满足条件的元素. forEach:输出元素. ---如果没有终止函数,那么中间函数的代码不会被执行。 personList.stream().filter(item -> { System.out.println("======="); return item.getAge() > 18; }).forEach(System.out::println); //2. 找出中国人 并统计个数: count() long count1 = personList.stream().filter(item -> item.getCountry().equals("中国")).count(); System.out.println("中国人:" + count1); //找出年龄最大和最小 Person person = personList.stream().max(((o1, o2) -> o1.getAge() - o2.getAge())).get(); Person person2 = personList.stream().min(((o1, o2) -> o1.getAge() - o2.getAge())).get(); System.out.println(person); System.out.println(person2); //把Person-年龄大于20人--里面名称----新的集合。 List<String> collect = personList.stream().filter(item -> item.getAge() > 20).map(item -> item.getName()).collect(Collectors.toList()); System.out.println(collect); //sorted排序 按照年龄从小到大排序 List<Person> collect = personList.stream().sorted((o1, o2) -> o1.getAge() - o2.getAge()).collect(Collectors.toList()); System.out.println(collect); //findFirst(): 返回流中的第一个元素,如果流为空则返回一个空的Optional对象。 Optional<Person> first = personList.stream().filter(item->item.getAge()>=20&&item.getAge()<=24).findFirst(); System.out.println(first.get()); //findAny(): 返回流中的任意一个元素,如果流为空则返回一个空的Optional对象 Optional<Person> any = personList.stream().filter(item -> item.getAge() > 20).findAny(); System.out.println(any.get()); //anyMatch 判断流中是否存在满足指定条件的元素,一旦找到满足条件的元素,立即返回true,不再继续处理剩余元素。 //判断有没有姓名为小梅的 boolean b = personList.stream().anyMatch(a -> a.getName().equals("小梅")); System.out.println(b); //allMatch 判断流中的所有元素是否都满足指定条件,一旦发现有不满足条件的元素,立即返回false,不再继续处理剩余元素。 //判断是不是所有姓名都叫小梅 boolean b2 = personList.stream().allMatch(a -> a.getName().equals("小梅")); System.out.println(b2); } } @Data @NoArgsConstructor @AllArgsConstructor class Person { private String name; private Integer age; private String country; private char sex; }
map:计算或转换
map: 可以把对流中的元素进行计算或转换
public class Test04 { public static void main(String[] args) { //整数数组每个元素+3 List<Integer> list = Arrays.asList(1, 17, 27, 7); list.stream().map(item->item+3).forEach(System.out::println); List<String> list2=Arrays.asList("hello","world","java","spring","springmvc"); //字符串大写 list2.stream().map(String::toUpperCase).forEach(System.out::println); } }
reduce:归约
reduce:归约,也称缩减,顾名思义,是把一个流缩减成一个值,能实现对集合求和、求乘积和求最值操作。
List<Integer> list= Arrays.asList(1,2,3,5); Optional<Integer> reduce = list.stream().reduce((t1, t2) -> t1 * t2); //求积 System.out.println(reduce);
6.注意事项
惰性求值(如果没有终结操作,没有中间操作是不会得到执行的)
流是一次性的(一旦一个流对象经过一个终结操作后。这个流就不能再被使用)
- 不会影响原数据(我们在流中可以多数据做很多处理。但是正常情况下是不会影响原来集合中的元素的。这往往也是我们期望的)