Stream流
- 创建流
- 中间操作
- 1、filter
- 2、map
- 3、distinct
- 4、sorted
- 5、limit
- 6、skip
- 7、flatMap
- 终结操作
- 1、forEach
- 2、count
- 3、max&min
- 4、collect
- 5、查找与匹配
创建流
单例集合:集合对象.stream()
List<Integer> list = new ArrayList<>();
Stream<Integer> stream = list.stream();
数组:Arrays.stream(数组) 或 Stream.of(数组) 来创建
Integer[] arr = {1,2,3,4};
Stream<Integer> stream1 = Arrays.stream(arr);
Stream<Integer> stream2 = Stream.of(arr);
双例集合:转换成单例集合后再创建
Map<String, String> map = new HashMap<>();
Stream<Map.Entry<String, String>> stream3 = map.entrySet().stream();
中间操作
1、filter
对流中的元素进行条件过滤,符合过滤条件的才能继续留在流中。
public static void test() {
List<Author> authors = getAuthors();
authors.stream()
.filter(author -> author.getAge() > 18) //中间操作
.forEach(author -> System.out.println(author)); //终结操作
}
2、map
可以把流中的元素进行计算或转换。
转换:
public static void test() {
List<Author> authors = getAuthors();
//泛型中,第一个参数为方法的参数类型,第二个参数为方法的返回值类型
authors.stream()
.map(author -> author.getName())
.forEach(name -> System.out.println(name));
}
3、distinct
去除流中的重复元素。
注意:distinct方法是依赖Object的equals方法来判断是否是相同对象,所以需要重写equals方法。
4、sorted
对流中的元素进行排序。
方式一:调用sorted()空参方法
在比较的实体类上要实现Comparable接口,不然会报类型不匹配的异常。
public static void test2() {
List<Author> authors = getAuthors();
authors.stream()
.distinct()
.sorted()
.forEach(author -> System.out.println(author.getAge()));
}
方式二:在sorted方法中实现Comparator接口
public static void test2() {
List<Author> authors = getAuthors();
authors.stream()
.distinct()
.sorted(new Comparator<Author>() {
@Override
public int compare(Author o1, Author o2) {
return o1.getAge()-o2.getAge();
}
})
.forEach(author -> System.out.println(author.getAge()));
}
优化
public static void test2() {
List<Author> authors = getAuthors();
authors.stream()
.distinct()
.sorted((o1, o2) -> o1.getAge()-o2.getAge())
.forEach(author -> System.out.println(author.getAge()));
}
5、limit
可以设置流的最大长度,超出的部分将被抛弃。
//对流中的元素按照年龄进行降序排序,并且要求不能有重复元素,打印其中年龄最大的两个作家。
public static void test2() {
List<Author> authors = getAuthors();
authors.stream()
.distinct()
.sorted((o1, o2) -> o2.getAge()-o1.getAge())
.limit(2)
.forEach(author -> System.out.println(author.getName()));
}
6、skip
跳过流中的前n个元素,返回剩下的元素。
7、flatMap
map只能把一个对象转换成另一个对象来作为流中的元素。而flatMap可以把一个对象转换成多个对象作为流中的元素。
案例1:打印所有书籍的名字,要求对重复的元素进行去重。
map方式:Author对象的books属性是集合类型,使用原来map转换对象,要使用嵌套循环进行打印。
public static void test2() {
List<Author> authors = getAuthors();
authors.stream()
.map(author -> author.getBooks())
.forEach(books -> {
for (Book book : books) {
System.out.println(book.getName());
}
});
}
flatMap方式:
public static void test2() {
List<Author> authors = getAuthors();
authors.stream()
.flatMap(author -> author.getBooks().stream())
.forEach(book -> System.out.println(book.getName()));
}
案例二:打印现有数据的所有分类,要求对分类进行去重。不能出现这种格式:哲学,爱情,要将它们拆开输出。
public static void test3() {
List<Author> authors = getAuthors();
authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct()
.flatMap(book -> Arrays.stream(book.getCategory().split(",")))
.distinct()
.forEach(category -> System.out.println(category));
}
终结操作
1、forEach
对流中的元素进行遍历操作,我们通过传入的参数去指定对遍历到的元素进行什么具体操作。
2、count
获取当前流中元素的个数。
//打印这些作家的所出书籍的数量
public static void test4() {
List<Author> authors = getAuthors();
long count = authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct()
.count();
System.out.println(count);
}
3、max&min
获取流中的最值
//分别获取这些作家所出书籍的最高分和最低分
public static void test4() {
List<Author> authors = getAuthors();
Optional<Integer> max = authors.stream()
.flatMap(author -> author.getBooks().stream())
.map(book -> book.getScore())
.max((o1, o2) -> o1 - o2);
Optional<Integer> min = authors.stream()
.flatMap(author -> author.getBooks().stream())
.map(book -> book.getScore())
.min(((o1, o2) -> o1 - o2));
System.out.println(max.get());
System.out.println(min.get());
}
4、collect
把当前流转换成一个集合。
list集合
//获取一个存放所有作者名字的list集合
public static void test5() {
List<Author> authors = getAuthors();
List<String> nameList = authors.stream()
.map(author -> author.getName())
.collect(Collectors.toList());
System.out.println(nameList);
}
set集合
//获取一个存放所有作者名字的set集合
public static void test5() {
List<Author> authors = getAuthors();
Set<String> nameList = authors.stream()
.map(author -> author.getName())
.collect(Collectors.toSet());
System.out.println(nameList);
}
map集合
//获取一个Map集合,map的key为作者名,value为List<Book>
public static void test5() {
List<Author> authors = getAuthors();
Map<String, List<Book>> map = authors.stream()
.distinct()
.collect(Collectors.toMap(author -> author.getName(), author -> author.getBooks()));
System.out.println(map);
}
5、查找与匹配
(1)anyMatch:判断是否有任意符合匹配条件的元素,结果为boolean类型。
//判断是否有作家年龄在18以上
public static void test6() {
List<Author> authors = getAuthors();
boolean flag = authors.stream()
.anyMatch(author -> author.getAge() > 18);
System.out.println(flag);
}
(2)allMatch:判断是否都符合条件,如果都符合返回true,否则返回false
//判断是否所有作家年龄在18以上
public static void test6() {
List<Author> authors = getAuthors();
boolean flag = authors.stream()
.allMatch(author -> author.getAge() > 18);
System.out.println(flag);
}
(3)noneMatch:判断流中的元素是否都不符合匹配条件,如果都不符合结果为true,否则结果为false。
(4)findAny:获取流中的任意一个元素。该方法没有办法保证获取到的一定是流中的第一个元素。
//获取任意一个年龄大于18的作家,如果存在就输出他的名字
public static void test7() {
List<Author> authors = getAuthors();
Optional<Author> any = authors.stream()
.filter(author -> author.getAge() > 18)
.findAny();
//如果这个Optional中有元素,则执行方法,没有就不执行
any.ifPresent(author -> System.out.println(author.getName()));
}
(5)findFirst:获取流中的第一个元素。
//获取一个年龄最小的作家,并输出他的姓名
public static void test7() {
List<Author> authors = getAuthors();
Optional<Author> any = authors.stream()
.sorted((o1, o2) -> o1.getAge()-o2.getAge())
.findFirst();
any.ifPresent(author -> System.out.println(author.getName()));
}
(6)reduce归并
对流中的数据按照你指定的计算方式计算出一个结果。(缩减操作)
reduce的作用是把stream中的元素给组合起来。我们可以传入一个初始值,它会按照我们的计算方式依次拿流中的元素和初始化值进行计算,计算结果再和后面的元素计算。
它内部的计算方式如下:
T result = identity;
for (T element : this stream)
result = accumulator.apply(result, element)
return result;
其中identity就是我们可以通过方法参数传入的初始值,accumulator的apply具体进行什么计算也是我们通过方法参数来确定的。
案例1:使用reduce求所有作者年龄的和
public static void test8() {
List<Author> authors = getAuthors();
Integer sum = authors.stream()
.distinct()
.map(author -> author.getAge())
//初始值为0,后面 result+=element,最后 return result
.reduce(0, (result, element) -> result + element);
System.out.println(sum);
}
案例2:使用reduce求所有作者中年龄的最大值
public static void test8() {
List<Author> authors = getAuthors();
Integer max = authors.stream()
.map(author -> author.getAge())
.reduce(Integer.MIN_VALUE, (result, element) -> result > element ? result : element);
System.out.println(max);
}
reduce有个重载形式,内部代码如下:
boolean foundAny = false;
T result = null;
for (T element : this stream) {
if(!foundAny) {
foundAny = true;
result = element;
} else {
result = accumulator.apply(result, element);
}
}
return foundAny ? Optional.of(result) : Optional.empty();
利用这个重载形式,求作者年龄的最大值,不用传递初始值了。
public static void test8() {
List<Author> authors = getAuthors();
Optional<Integer> max = authors.stream()
.map(author -> author.getAge())
.reduce((result, element) -> result > element ? result : element);
System.out.println(max.get());
}
注意事项
惰性求值:如果没有终结操作,中间操作是不会得到执行的。
流是一次性的:一旦一个流对象经过一个终结操作后,这个流就不能再被使用了,只能重新创建流对象再使用。
不会影响原数据:我们在流中可以对数据做很多处理,但正常情况下是不会影响原来集合中的元素的。