学习基于记录,而不止于记录。
希望自己能坚持下去~
0.写在前面
java 版本:1.8.0_181
开发工具:IntelliJ IDEA 2018.3.2 (Ultimate Edition)
总结Java8 Stream API(java.util.stream.*)的基本用法。
1.基本了解
Stream是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
“集合讲的是数据,流讲的是计算!”
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对
集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之, Stream API 提供了一种高效且易于使用的处理数据的方式。
2.注意
- Stream 自己不会存储元素。
- Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
- Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
3.流的操作步骤
1)创建Stream
2)中间操作
3)终止操作
4.创建Stream
- 通过Collection 系列集合提供的stream() 或者 parallelStream
- 通过Arrays中静态方法stream()获取数组流
- 通过stream类中的静态方法of()
- 无限流
package stream;
import methodconstructorreference.Animal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
/**
* @Author: grj
* @Date: 2020/5/19 21:31
* 创建流
*/
public class TestStream1 {
public static void main(String[] args) {
}
//1.通过Collection 系列集合提供的stream() 或者 parallelStream
public static void test1() {
List<String> list = new ArrayList<>();
Stream<String> stringStream = list.stream();
}
//2.通过Arrays中静态方法stream()获取数组流
public static void test2() {
Animal[] animals = new Animal[5];
Stream<Animal> animalStream = Arrays.stream(animals);
}
//3.通过stream类中的静态方法of()
public static void test3() {
Stream<String> stream = Stream.of("a","b","c");
}
//4.无限流
public static void test4() {
//迭代
Stream<Integer> integerStream = Stream.iterate(0, (x) -> x+2);
//生成
Stream.generate(() -> Math.random()*100);
}
}
5.中间操作
注意:
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理! 而在终止操作时一次性全部处理,称为惰性求值
。
- 筛选与切片
方法 | 描述 |
---|---|
filter(Predicate p) | 接收 Lambda , 从流中排除某些元素。 |
distinct() | 筛选,通过流所生成元素的 hashCode() 和 equals() 去 除重复元素 |
limit(long maxSize) | 截断流,使其元素不超过给定数量。 |
skip(long n) | 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素 不足 n 个,则返回一个空流。与 limit(n) 互补 |
代码实例:
package stream;
import methodconstructorreference.Animal;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Stream;
/**
* @Author: grj
* @Date: 2020/5/19 21:38
* 中间操作: 筛选和切片
*/
public class TestStream2 {
static List<Animal> animals = Arrays.asList(
new Animal("黑色","马"),
new Animal("白色","鸽子"),
new Animal("红色","朱雀"),
new Animal("灰色","狼"),
new Animal("粉色","兔子"),
new Animal("蓝色","鹦鹉"),
new Animal("绿色","野鸡"),
new Animal("红色","朱雀")
);
public static void main(String[] args) {
// test1();
// test3();
// test4();
test5();
}
/*
filter
内部迭代由Stream API完成
*/
public static void test1() {
Stream<Animal> animalStream = animals.stream()
.filter((a) -> a.getName().length() > 1);
//"惰性求值"
animalStream.forEach(System.out::println);
}
//外部迭代
public static void test2() {
Iterator<Animal> animalIterator = animals.iterator();
while (animalIterator.hasNext()) {
System.out.println("小动物:"+animalIterator.next());
}
}
/*
limit
*/
public static void test3() {
animals.stream()
.filter((a) -> a.getName().length() > 1)
.limit(2)
.forEach(System.out::println);
}
/*
skip
*/
public static void test4() {
animals.stream()
.filter((a) -> a.getName().length() > 1)
.skip(2)
.forEach(System.out::println);
}
/*
distinct
*/
public static void test5() {
animals.stream()
.distinct()
.forEach(System.out::println);
}
}
package methodconstructorreference;
import java.util.Objects;
/**
* @Author: grj
* @Date: 2020/5/19 21:14
*/
public class Animal {
String color;
String name;
public Animal() {
}
public Animal(String color, String name) {
this.color = color;
this.name = name;
}
@Override
public String toString() {
return "Animal{" +
"color='" + color + '\'' +
", name='" + name + '\'' +
'}';
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Animal animal = (Animal) o;
return Objects.equals(color, animal.color) &&
Objects.equals(name, animal.name);
}
@Override
public int hashCode() {
return Objects.hash(color, name);
}
}
- 映射
方法 | 描述 |
---|---|
map(Function f) | 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。 |
mapToDouble(ToDoubleFunction f) | 接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 DoubleStream。 |
mapToInt(ToIntFunction f) | 接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 IntStream。 |
mapToInt(ToIntFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。 |
flatMap(Function f) | 接收一个函数作为参数,将流中的每个值都换成另 一个流,然后把所有流连接成一个流 |
代码实例:
package stream;
import methodconstructorreference.Animal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Stream;
/**
* @Author: grj
* @Date: 2020/5/19 21:38
* 中间操作: 映射
*/
public class TestStream3 {
static List<Animal> animals = Arrays.asList(
new Animal("黑色","马"),
new Animal("白色","鸽子"),
new Animal("红色","朱雀"),
new Animal("灰色","狼"),
new Animal("粉色","兔子"),
new Animal("蓝色","鹦鹉"),
new Animal("绿色","野鸡"),
new Animal("红色","朱雀")
);
public static void main(String[] args) {
// test1();
test2();
}
/*
map
*/
public static void test1() {
animals.stream()
.map(Animal::getName)
.forEach(System.out::println);
}
/*
flatMap
*/
public static void test2() {
List<String> stringList = Arrays.asList(
"abc",
"hhh",
"wc",
"UFO"
);
//嵌套两层的流
Stream<Stream<Character>> result = stringList.stream()
.map(TestStream3::filterCharacter);
//1.遍历操作也要两层,很麻烦
// result.forEach((s) -> s.forEach(System.out::println) );
//2.使用flatMap
stringList.stream()
.flatMap(TestStream3::filterCharacter)
.forEach(System.out::println);
}
public static Stream<Character> filterCharacter(String str) {
List<Character> characterList = new ArrayList<>();
for (Character c :
str.toCharArray()) {
characterList.add(c);
}
return characterList.stream();
}
}
- 排序
方法 | 描述 |
---|---|
sorted() | 产生一个新流,其中按自然顺序排序 |
sorted(Comparator comp) | 产生一个新流,其中按比较器顺序排序 |
代码实例:
package stream;
import methodconstructorreference.Animal;
import java.util.Arrays;
import java.util.List;
/**
* @Author: grj
* @Date: 2020/5/19 21:38
* 中间操作: 排序
*/
public class TestStream4 {
static List<Animal> animals = Arrays.asList(
new Animal("黑色", "马"),
new Animal("白色", "鸽子"),
new Animal("红色", "朱雀"),
new Animal("灰色", "狼"),
new Animal("粉色", "兔子"),
new Animal("蓝色", "鹦鹉"),
new Animal("绿色", "野鸡"),
new Animal("红色", "朱雀")
);
public static void main(String[] args) {
// test1();
test2();
}
/*
sorted():自然排序
*/
public static void test1() {
List<String> list = Arrays.asList("ac", "bv", "ed", "cd", "xc", "fg");
list.stream()
.sorted()
.forEach(System.out::println);
}
/*
sorted(Comparator com)---定制排序(Comparator)
*/
public static void test2() {
animals.stream()
.sorted((e1, e2) -> {
if (e1.getName().equals(e2.getName())) {
return e1.getColor().compareTo(e2.getColor());
} else {
return e1.getColor().compareTo(e2.getColor());
}
})
.forEach(System.out::println);
}
}
6.终止操作
- 查找与匹配
方法 | 描述 |
---|---|
allMatch(Predicate p) | 检查是否匹配所有元素 |
anyMatch(Predicate p) | 检查是否至少匹配一个元素 |
noneMatch(Predicate p) | 检查是否没有匹配所有元素 |
findFirst() | 返回第一个元素 |
findAny() | 返回当前流中的任意元素 |
count() | 返回流中元素总数 |
max(Comparator c) | 返回流中最大值 |
min(Comparator c) | 返回流中最小值 |
forEach(Consumer c) | 内部迭代(使用 Collection 接口需要用户去做迭 代,称为外部迭代。相反,Stream API 使用内部 迭代——它帮你把迭代做了) |
(不知道为啥,findAny总是返回第一个。。。)
代码实例:
package stream;
import methodconstructorreference.Animal;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
/**
* @Author: grj
* @Date: 2020/5/20 20:27
* 终止操作:查找与匹配
*/
public class TestStream5 {
static List<Animal> animals = Arrays.asList(
new Animal("黑色", "马"),
new Animal("白色", "鸽子"),
new Animal("红色", "朱雀"),
new Animal("灰色", "狼"),
new Animal("粉色", "兔子"),
new Animal("蓝色", "鹦鹉"),
new Animal("绿色", "野鸡"),
new Animal("红色", "朱雀")
);
public static void main(String[] args) {
// test1();
// test2();
// test3();
// test4();
// test5();
test6();
}
public static void test1() {
boolean match = animals.stream()
.allMatch((a) -> a.getColor().equals("白色"));
System.out.println("是否都是白色的:"+match);
}
public static void test2() {
boolean match = animals.stream()
.anyMatch((a) -> a.getColor().equals("白色"));
System.out.println("是否有白色的:"+match);
}
public static void test3() {
boolean match = animals.stream()
.noneMatch((a) -> a.getColor().equals("白色"));
System.out.println("是否没白色的:"+match);
}
public static void test4() {
Optional<Animal> first = animals.stream()
.findFirst();
System.out.println("第一只动物:"+first.get());
}
public static void test5() {
Optional<Animal> any = animals.stream()
.findAny();
System.out.println("任意一只动物:"+any.get());
}
public static void test6() {
long count = animals.stream()
.count();
System.out.println("count:"+count);
Optional<Animal> max = animals.stream()
.max((x1, x2) -> x1.getName().compareTo(x2.getName()));
System.out.println("max:"+max.get());
Optional<Animal> min = animals.stream()
.min((x1, x2) -> x1.getName().compareTo(x2.getName()));
System.out.println("min:"+min.get());
}
}
- 规约
方法 | 描述 |
---|---|
reduce(T iden, BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回 T |
reduce(BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。 返回 Optional<T> |
备注:map 和reduce 的连接通常称为map-reduce 模式,因Google 用它 来进行网络搜索而出名。
代码实例:
package stream;
import methodconstructorreference.Animal;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
/**
* @Author: grj
* @Date: 2020/5/20 21:00
* 终止操作:归约
*/
public class TestStream6 {
static List<Animal> animals = Arrays.asList(
new Animal("黑色","马"),
new Animal("白色","鸽子"),
new Animal("红色","朱雀"),
new Animal("灰色","狼"),
new Animal("粉色","兔子"),
new Animal("蓝色","鹦鹉"),
new Animal("绿色","野鸡"),
new Animal("红色","朱雀")
);
public static void main(String[] args) {
// test1();
test2();
}
public static void test1() {
List<Integer> integerList = Arrays.asList(1,2,3,4,5,6);
Integer reduce = integerList.stream()
.reduce(0, (x, y) -> x + y);
System.out.println("reduce:"+reduce);
}
public static void test2() {
//可能为空的时候才会用Optional包裹
Optional<String> reduce = animals.stream()
.map((a)-> a.getName())
.reduce(String::concat);
System.out.println("reduce:"+reduce.get());
}
}
- 收集
方法 | 描述 |
---|---|
collect(Collector c) | 将流转换为其他形式。接收一个 Collector接口的 实现,用于给Stream中元素做汇总的方法 |
collect内部的方法:
方法 | 返回类型 | 作用 | 用法 |
---|---|---|---|
toList | List<T> | 把流中元素收集到List | List<Employee> emps= list.stream().collect(Collectors.toList()); |
toSet | Set<T> | 把流中元素收集到Set | Set<Employee> emps= list.stream().collect(Collectors.toSet()); |
toCollection | Collection<T> | 把流中元素收集到创建的集合 | Collection<Employee>emps=list.stream().collect(Collectors.toCollection(ArrayList::new)); |
counting | Long | 计算流中元素的个数 | long count = list.stream().collect(Collectors.counting()); |
summingInt | Integer | 对流中元素的整数属性求和 | inttotal=list.stream().collect(Collectors.summingInt(Employee::getSalary)); |
averagingInt | Double | 计算流中元素Integer属性的平均值 | doubleavg= list.stream().collect(Collectors.averagingInt(Employee::getSalary)); |
summarizingInt | IntSummaryStatistics | 收集流中Integer属性的统计值,如:平均值 | IntSummaryStatisticsiss= list.stream().collect(Collectors.summarizingInt(Employee::getSalary)); |
joining | String | 连接流中每个字符串 | String str=list.stream().map(Employee::getName).collect(Collectors.joining()); |
maxBy | Optional<T> | 根据比较器选择最大值 | Optional<Emp>max= list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary))); |
minBy | Optional<T> | 根据比较器选择最小值 | Optional<Emp> min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary))); |
reducing | 归约产生的类型 | 从一个作为累加器的初始值 开始,利用BinaryOperator与流中元素逐个结合,从而归 约成单个值 | inttotal=list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum)); |
collectingAndThen | 转换函数返回的类型 | 包裹另一个收集器,对其结 果转换函数 | inthow= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size)); |
groupingBy | Map<K, List<T>> | 根据某属性值对流分组,属 性为K,结果为V | Map<Emp.Status, List<Emp>> map= list.stream().collect(Collectors.groupingBy(Employee::getStatus)); |
partitioningBy | Map<Boolean, List<T>> | 根据true或false进行分区 | Map<Boolean,List<Emp>>vd= list.stream().collect(Collectors.partitioningBy(Employee::getManage)); |
(部分代码演示)
代码示例:
package stream;
import methodconstructorreference.Animal;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.stream.Collectors;
/**
* @Author: grj
* @Date: 2020/5/20 21:07
* 终止操作:收集
*/
public class TesstStream7 {
static List<Animal> animals = Arrays.asList(
new Animal("黑色","马"),
new Animal("白色","鸽子"),
new Animal("红色","朱雀"),
new Animal("灰色","狼"),
new Animal("粉色","兔子"),
new Animal("蓝色","鹦鹉"),
new Animal("绿色","野鸡"),
new Animal("红色","朱雀")
);
public static void main(String[] args) {
test1();
}
/*
想存入什么集合类型自己定义
*/
public static void test1(){
animals.stream()
.map((a)-> a.getColor())
// .collect(Collectors.toList());
// .collect(Collectors.toSet());
// .collect(Collectors.toCollection(HashSet::new));
.collect(Collectors.toCollection(LinkedHashSet::new));
}
}
7.总结
Stream API部分内容量比较多,而且实际开发过程用的也多,但是常用的其实就那么几种方法,比如:map,filter, collect等。将流操作的三个步骤理清楚,可以帮助记忆,关键还是多用。