1.什么是Stream
流(Stream)中保存对集合或数组数据的操作。
日常工作中,经常需要对集合中的元素进行相关操作。如:增加、删除、获取元素、遍历
2.流式思想
注意:Stream流和IO流(InputStream/OutputStream)没有任何关系,请暂时忘记对传统IO流的固有印象。
Stream流式思想类似于工厂车间的“生产流水线”,Stream流不是一种数据结构,不会保存数据,而是对数据进行加工处理。Stream 可以看做是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。
3.获取Stream流的两种方式
两种常用的方式:
1.集合.stream()方法来获取流;数组则:Arrays.stream(数组)
2.使用Stream.of(集合)获取流
//方式1:根据Collection获取流
//Collection接口中有一个默认的方法:default Stream<E> stream()
//1.List获取流
List<String> list = new ArrayList<>();
Stream<String> stream01 = list.stream();
//2.Set获取流
Set<String> set = new HashSet<>();
Stream<String> stream02 = set.stream();
//3.Map获取流
// Map并没有继承自 Collection 接口,所有无法通过该 map.stream()获取流。
// 但是可用通过如下三种方式获取:
Map<String,String> map = new HashMap<>();
Stream<String> stream03 = map.keySet().stream();
Stream<String> stream04 = map.values().stream();
Stream<Map.Entry<String, String>> stream05 = map.entrySet().stream();
//方式2:Stream中的静态方法of获取流
//1.字符串获取流
Stream<String> stream06 = Stream.of("aa", "bb", "cc");
//2.数组类型(基本类型除外)
String[] strs = {"aa","bb","cc"};
Stream<String> stream07 = Stream.of(strs);
//3.基本数据类型的数组
int[] arr = {1,2,3,4};
//看着没报错,但是看到返回值是 int[],这是 Stream流把整个数组看做一个元素来操作,而不是操作数组中的int元素(这样子是不行的)
Stream<int[]> stream08 = Stream.of(arr);
4.Stream流常用方法
Stream流模型的操作很丰富,这里介绍一些常用的API。这些方法可以被分成两种类型:
方法名 | 方法作用 | 返回值类型 | 方法种类 |
forEach | 遍历(逐一处理) | void | 终结 |
count | 统计个数 | long | 终结 |
filter | 过滤 | Stream | 函数拼接 |
limit | 取用前几个 | Stream | 函数拼接 |
skip | 跳过前几个 | Stream | 函数拼接 |
map | 映射 | Stream | 函数拼接 |
concat | 组合 | Stream | 函数拼接 |
终结方法:返回值类型不再是Stream类型的方法,不再支持链式调用。在此终结方法包括 count() 和 forEach() 方法;
非终结方法:又叫函数拼接方法。值返回值类型仍然是 Stream 类型的方法,支持链式调用(除了终结方法外,其与方法均为非终结方法)
提醒:以下所有代码部分,能简化部分尽量简化,均使用最简格式。
4.1 foreach
void forEach(Consumer<? super T> action);
1.forEach()方法用来遍历流中的数据,是一个终结方法。
2.该方法接收一个Consumer接口函数,会将每一个流元素交给该函数进行处理。
示例如下:
List<String> names = new ArrayList<>();
Collections.addAll(names,"张无忌","周芷若","杨逍","张强","张三丰","赵敏");
//未简写
names.forEach((String str)->{
System.out.println(str);
});
//简写1
names.forEach(str-> System.out.println(str));
//最终简写
names.forEach(System.out::println);
4.2 count
long count();
1.count()方法,用来统计集合中元素个数,是一个终结方法。
2.该方法返回一个long值代表元素个数。
示例如下:
List<String> names = new ArrayList<>();
Collections.addAll(names,"张无忌","周芷若","杨逍","张强","张三丰","赵敏");
//count()计算集合中元素个数
long count = names.stream().count();
System.out.println("元素个数为: "+count); //元素个数为:6
4.3 filter
Stream<T> filter(Predicate<? super T> predicate);
1.filter()方法,用于过滤数据,返回符合过滤条件的数据,是一个非终结方法。
2.可以通过filter()方法将一个流转换成另一个子集流。
3.该接口接收一个Predicate函数式接口参数(可以是一个Lambda表达式或方法引用)作为筛选条件。
4.因为filter()是一个非终结方法,所以必须调用终止方法。
示例如下:
List<String> names = new ArrayList<>();
Collections.addAll(names,"张无忌","周芷若","杨逍","张强","张三丰","赵敏");
//filter()过滤,返回以"J"开头的名字
names.stream()
.filter(str->str.startsWith("J"))
.forEach(System.out::println);
//使用BiPredicate,就搞复杂了,如下,没啥意思(只会让人看不懂,哈哈)
BiPredicate<String,String> consumer = String::startsWith;
names.stream()
.filter(str-> consumer.test(str,"张"))
.forEach(System.out::println);
4.4 limit
Stream<T> limit(long maxSize);
1.limit()方法,用来对Stream流中的数据进行截取,只取用前n个,是一个非终结方法。
2.参数是一个long型,如果集合当前长度大于参数则进行截取,否则不进行操作。因为limit()是一个非终结方法,所以必须调用 终止方法。
示例如下:
List<String> names = new ArrayList<>();
Collections.addAll(names,"张无忌","周芷若","杨逍","张强","张三丰","赵敏");
//limit()截取,截取list集合前三个元素
names.stream().limit(3).forEach(System.out::println);
4.5 skip
Stream<T> skip(long n);
1.skip()是跳过前几个元素,去取后面的元素,获取一个截取之后的新流,它是一个非终结方法。
2.参数是一个long型,如果Stream流的当前长度 > n,则跳过前n个,否则将会得到一个长度为0的空流。
3.因为 limit() 是一个非终结方法,所以必须调用终止方法。
示例如下:
List<String> names = new ArrayList<>();
Collections.addAll(names,"张无忌","周芷若","杨逍","张强","张三丰","赵敏");
//skip()跳过list集合前2个元素,获取剩下的元素
names.stream().skip(2).forEach(System.out::println);
备注:
使用 skip() 和 limit() 方法,即可实现类似分页的操作了。示例如下:
//一页10条 分页操作
//第一页
skip(0).limit(10)
//第二页
skip(10).limit(10)
//第三页
skip(20).limit(10)
...
4.6 map
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
1.map() 方法,可以将流中的元素映射到另一个流中。即:可以将一种类型的流转换为另一种类型的流。
2.该接口需要一个 Function 函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流。
3.map() 方法是一个非终结方法,所以必须调用终止方法。
示例如下:
List<String> list = new ArrayList<>();
Collections.addAll(list,"11","22","33","44","55");
//通过map()方法,可以将String类型的流转换为int类型的流
list.stream().map((String str)->{
return Integer.parseInt(str);
}).forEach((Integer num) -> {
System.out.println(num);
});
//简化
list.stream().map(str->Integer.parseInt(str)).forEach(str->System.out.println(str));
//简化后
list.stream().map(Integer::parseInt).forEach(System.out::println);
4.7 concat
public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) {
Objects.requireNonNull(a);
Objects.requireNonNull(b);
@SuppressWarnings("unchecked")
Spliterator<T> split = new Streams.ConcatSpliterator.OfRef<>(
(Spliterator<T>) a.spliterator(), (Spliterator<T>) b.spliterator());
Stream<T> stream = StreamSupport.stream(split, a.isParallel() || b.isParallel());
return stream.onClose(Streams.composedClose(a, b));
}
1.concat()方法,可将两个Stream流合并成一个流进行返回。如果是三个流,则需要两两合并,不能一次性合并三个流。
2.concat()方法是Stream接口的静态方法,可以直接使用 类名.方法名 调用。
注意:
concat()方法此处接收的是Stream类型,不能接收IntStream等类型。
concat()是一个静态方法,与java.lang.String中的concat()方法是不同的。
示例如下:
//concat()方法
Stream<Integer> aStream = Stream.of(1, 2, 3);
Stream<Integer> bStream = Stream.of(4, 5, 6);
//合并a、b流
Stream<Integer> concatStream = Stream.concat(aStream, bStream);
concatStream.forEach(System.out::println);
4.8 sorted
sorted()方法,可以用来对 Stream 流中的数据进行排序。sorted()方法共有以上两种情况:
(1)根据元素的自然规律排序,默认升序
Stream<T> sorted();
(2)根据比较器指定的规则排序
Stream<T> sorted(Comparator<? super T> comparator);
因为 sorted() 方法是一个非终结方法,所以必须调用终止方法。
示例代码:
//sorted():根据元素的自然规律排序
Stream<Integer> stream01 = Stream.of(66,33,11,55);
stream01 .sorted().forEach(System.out::println);
//sorted(Comparator<? super T> comparator):根据比较器规则升序排序
Stream<Integer> stream02 = Stream.of(66,33,11,55);
stream02.sorted((a,b) -> a-b).forEach(System.out::println);
//根据比较器规则降序排序
stream02.sorted((a,b) -> b-a).forEach(System.out::println);
4.9 distinct
Stream<T> distinct();
1. distinct()方法,可以用来去除重复数据。
2.因为distinct()方法是一个非终结方法,所以必须调用终止方法。
去除重复数据,此处有几种情况:
(1)基本类型去重
(2)String类型去重
(3)引用类型去重(对象去重)
提示:
第(1)(2)使用 distinct()方法可以直接去重
第(3)对象类型需要重写 equals() 和 hasCode() 方法,使用 distinct() 方法才能去重成功。
示例如下:
//基本类型去重
Stream<Integer> stream01 = Stream.of(66,33,11,55,33,22,55,66);
stream01.distinct().forEach(System.out::println);
//字符串去重
Stream<String> stream02 = Stream.of("AA","BB","AA");
stream02.distinct().forEach(System.out::println);
//自定义对象去重
//Person对象类,有String name,Integer age 两个属性,两参数构造器,get/set()方法,重写了equals(),hashCode(),toString()方法。此处就不附Person实体类了)
BiFunction<String,Integer,Person> p = Person::new;
Person p1 = p.apply("西施", 18);
Person p2 = p.apply("貂蝉", 20);
Person p3 = p.apply("王昭君", 22);
Person p4 = p.apply("杨玉环", 23);
Person p5 = p.apply("杨玉环", 23);
Stream<Person> stream03 = Stream.of(p1, p2, p3, p4, p5);
//去重复对象
stream03.distinct().forEach(System.out::println);
4.10 match
//allMatch 全匹配(匹配所有,所有元素都需要满足条件-->返回true)
boolean allMatch(Predicate<? super T> predicate);
//anyMatch 匹配某个元素(只要有一个元素满足条件即可-->返回true)
boolean anyMatch(Predicate<? super T> predicate);
//noneMatch 匹配所有元素(所有元素都不满足指定条件-->返回true)
boolean noneMatch(Predicate<? super T> predicate);
1.match()方法,可以用来判断 Stream 流中的数据是否匹配指定的条件。
2.allMatch()、anyMatch()、noneMatch()方法都是终结方法,返回值为boolean。
示例如下:
Stream<Integer> stream01 = Stream.of(5, 3, 6, 1);
boolean allMatch = stream01.allMatch(i -> i > 0);
System.out.println("allMatch匹配:"+allMatch);
Stream<Integer> stream02 = Stream.of(5, 3, 6, 1);
boolean anyMatch = stream02.anyMatch(i -> i > 5);
System.out.println("anyMatch匹配:"+anyMatch);
Stream<Integer> stream03 = Stream.of(5, 3, 6, 1);
boolean noneMatch = stream03.noneMatch(i -> i < 3);
System.out.println("noneMatch匹配:"+noneMatch);
结果:
allMatch匹配:true
anyMatch匹配:true
noneMatch匹配:false
4.11 max / min
Optional<T> max(Comparator<? super T> comparator);
Optional<T> min(Comparator<? super T> comparator);
1.max()和min()方法,用来获取Stream流中的最大值和最小值。
2.该接口需要一个Comparator函数式接口参数,根据指定排序规则来获取最大值、最小值。
为了保证数据的准确性,此处排序规则需要是升序排序。因为:max() 方法获取的是排序后的最后一个值,min() 方法获取的是排序后的第一个值。如果使用降序排序后,那么 max() 和 min() 方法就相反了,就有异常了。
示例如下:
//max()
Stream<Integer> stream01 = Stream.of(33, 11, 22, 5);
Optional<Integer> max = stream01.max((i1, i2) -> i1 - i2);
System.out.println("最大值:"+max.get());
//min()
Stream<Integer> stream02 = Stream.of(33, 11, 22, 5);
Optional<Integer> min = stream02.min((i1, i2) -> i1 - i2);
System.out.println("最小值:"+min.get());