Stream
当我们在需要对集合中的元素进行操作的时候,除了必需的添加,删除,获取外,最典型的就是遍历数组了
public static void main(String[] args) {
List<String> list = Arrays.asList("AAA","ABB","As","CC","DD");
// 获取所有以 A 开头的数据
List<String> list1 = new ArrayList<>();
for (String s : list) {
if(s.startsWith("A")){
list1.add(s);
}
}
List<String> list2 = new ArrayList<>();
// 获取长度为 3 的数据
for (String s : list1) {
if (s.length() == 3) {
list2.add(s);
}
}
for (String s : list2) {
System.out.println(s);
}
}
以上代码可以看出有很多的循环,只要有了不同的需求,就需要进行一遍循环,是很繁琐的,因此,通过 JDK8 新特性的stream流可以解决这个问题。
Stream 更加优雅的代码解决方案
public static void main(String[] args) {
List<String> list = Arrays.asList("AAA","ABB","As","CC","DD");
// 获取所有以 A 开头的数据
// 获取长度为 3 的数据
list.stream()
.filter(s -> s.startsWith("A"))
.filter(s -> s.length() == 3)
.forEach(System.out::println);
}
以上代码含义:获取流,过滤“A”,过滤长度,逐一打印。代码相比于上面的案例更加简介直观
1. Stream流式思想概述
Stream流式思想类似于工厂车间的“生产流水线”,Stream流不是一种数据结构,不保存数据,而是对数据进行加工处理。Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。
Stream API 可以让我们快速完成许多复制的操作,如筛选,切片,映射,查找,去重复,统计,匹配和归约
2. Stream流的获取方式
2.1 根据Collection获取
首先, java.util.Collection 接口中加入了 defatult 方法 Stream,也就是说 Collection 接口下的所有的实现都可以通过 stream 方法来获取 Stream 流。
public static void main(String[] args) {
List<String> list = Arrays.asList("AAA","ABB","As","CC","DD");
list.stream();
Set<String> set = new HashSet<>();
set.stream();
Vector vector = new Vector();
vector.stream();
}
对于集合 Map 接口没有实现 Collection 接口,那么,我们可以根据 Map 获取对应的 key 和 value 的集合
2.2 通过 Stream 的 of 方法
在实际开发中我们不可避免的还是会操作到数组中的数据,由于数组对象不可能添加默认方法,所有Stream接口中提供了静态方法 of
public static void main(String[] args) {
Stream<String> a1 = Stream.of("a1", "a2", "a3");
String[] arr1 = {"aa","bb","cc"};
Stream<String> arr11 = Stream.of(arr1);
Integer[] arr2 = {1,2,3,4};
Stream<Integer> arr21 = Stream.of(arr2);
arr21.forEach(System.out::println);
// 注意:基本数据类型的数组是不会将其中的元素一个一个给打印出来的
int[] arr3 = {1,2,3,4};
Stream.of(arr3).forEach(System.out::println);
}
3. Stream 常用方法
3.1 forEach —— 终结方法
forEach 用来遍历流中的数据的
void forEach(Consumer<? super T> action);
该方法接受一个 Consumer 接口,会将每一个流元素交给函数处理
public static void main(String[] args) {
Stream.of("aa","bb","cc","dd").forEach(System.out::println);
}
3.2 count —— 终结方法
Stream流中的 count 方法用来统计其中的元素个数的
long count();
该方法返回一个 long 值,代表元素的个数
long count = Stream.of("aa", "bb", "cc", "dd").count();
System.out.println(count);
3.3 filter
filter 方法的作用是用来过滤数据数据的。返回符合条件的数据。
可以通过filter方法将一个流转换成另一个子集流
Stream<T> filter(Predicate<? super T> predicate);
该接口接收一个 Predicate 函数式接口参数作为筛选条件
Stream.of("a1","a2","a3","s","sa","d","f","cc","xx")
.filter(s -> s.contains("a"))
.forEach(System.out::println);
输出:
a1
a2
a3
sa
3.4 limit
limit 方法可以对流进行截取处理,只取前 n 个数据
Stream<T> limit(long maxSize);
参数是一个 long 类型的数值,如果集合当前长度大于参数就进行截取,否则不进行操作
Stream.of("a1","a2","a3","s","sa","d","f","cc","xx")
.limit(3)
.forEach(System.out::println);
输出
a1
a2
a3
3.5 skip
如果希望跳过前面几个元素,可以使用 skip 方法获取一个截取之后的新流
Stream<T> skip(long n);
操作:
Stream.of("a1","a2","a3","s","sa","d","f","cc","xx")
.skip(3)
.forEach(System.out::println);
输出:
s
sa
d
f
cc
xx
3.6 map
如果我们需要将流中的元素映射到另一个流中,可以使用 map 方法
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
该接口需要一个Function函数式接口参数,可以将当前流中的 T 类型数据转换成另一种 R 类型的数据
Stream.of("1","2","3","4","5","6","7")
.map(Integer::parseInt)
.forEach(System.out::println);
3.7 sorted
如果需要将数据排序,可以使用 sorted 这个方法
Stream<T> sorted();
在使用的时候可以根据自然规则排序,也可以通过比较来指定对应的排序规则
Stream.of("1","3","2","0","9","4","7")
.map(Integer::parseInt)
.sorted() // 根据数据日然顺序排序
.sorted((o1,o2) -> 02 - o1)
.forEach(System.out::println);
3.8 distinct
如果要去掉重复的数据,可以使用该方法
Stream<T> distinct();
使用
Stream.of("1","3","2","2","7","4","7")
.map(Integer::parseInt)
.sorted() // 根据数据日然顺序排序
.sorted((o1,o2) -> 02 - o1)
.distinct()
.forEach(System.out::println);
Stream.of(
new Person("张三",18),
new Person("李四",22),
new Person("张三",18)
).distinct()
.forEach(System.out::println);
Stream 流中的 distinct
基本数据类型是可以直接去重的,但是自定义数据类型,我们需要重写 hashCode
和 equals
方法来进行去重
3.9 match
anyMatch、allMatch、noneMatch
anyMatch 表示判断的条件里,任意一个元素成功,返回 true
allMatch 表示判断里的元素,所有的都是,返回 true
noneMatch 跟 allMatch 相反,判断条件里的元素,所有的都不是,返回 true
List<String> strs = Arrays.asList("a", "a", "a", "a", "b");
boolean aa = strs.stream().anyMatch(str -> str.equals("a"));
boolean bb = strs.stream().allMatch(str -> str.equals("a"));
boolean cc = strs.stream().noneMatch(str -> str.equals("a"));
long count = strs.stream().filter(str -> str.equals("a")).count();
System.out.println(aa);// TRUE
System.out.println(bb);// FALSE
System.out.println(cc);// FALSE
System.out.println(count);// 4
3.10 find
findFirst、findAny
findFirst:返回列表中的第一个元素。
findAny:返回的元素是不确定的,对于同一个列表多次调用 findAny() 有可能会返回不同的值。使用 findAny() 是为了更高效的性能。如果是数据较少,串行的情况下,一般会返回第一个结果,如果是并行的情况,那就不能确保是第一个。
List<String> lst1 = Arrays.asList("Jhonny", "David", "Jack", "Duke", "Jill","Dany","Julia","Jenish","Divya");
List<String> lst2 = Arrays.asList("Jhonny", "David", "Jack", "Duke", "Jill","Dany","Julia","Jenish","Divya");
Optional<String> findFirst = lst1.parallelStream().filter(s -> s.startsWith("D")).findFirst();
Optional<String> fidnAny = lst2.parallelStream().filter(s -> s.startsWith("J")).findAny();
System.out.println(findFirst.get()); //总是打印出David
System.out.println(fidnAny.get()); //会随机地打印出Jack/Jill/Julia
3.11 max 和 min
max
Optional<Person> max = Stream.of(
new Person("aa", 77),
new Person("bb", 66),
new Person("cc", 55),
new Person("dd", 44),
new Person("eef", 33),
new Person("ff", 22)
).max(Comparator.comparingInt(Person::getAge));
System.out.println(max);
max.ifPresent(s -> System.out.println(s.getAge()));
// 结果
Optional[Person{name='aa', age=77}]
77
min
Optional<Person> max = Stream.of(
new Person("aa", 77),
new Person("bb", 66),
new Person("cc", 55),
new Person("dd", 44),
new Person("eef", 33),
new Person("ff", 22)
).min(Comparator.comparingInt(Person::getAge));
System.out.println(max);
max.ifPresent(s -> System.out.println(s.getAge()));
// 结果
Optional[Person{name='ff', age=22}]
22
3.12 reduce 方法
如果需要将所有数据归纳得到又给数据,可以使用 reduce 方法
T reduce(T identity, BinaryOperator)
使用:
public static void main(String[] args) {
Integer sum = Stream.of(3,4,5,9)
// identity 默认值
// 第一次的时候会将默认值赋值给 x
// 之后每次会将 上一次的操作结果赋值给x y就是每次从数据中获取的元素
.reduce(0, (x, y) -> {
return x + y;
});
System.out.println(sum);
// 获取最大值
Integer max = Stream.of(4,5,3,9)
.reduce(0, (x, y) -> {
return x > y? x : y;
});
System.out.println(max);
}
3.13 map 和 reduce 的组合
Integer sum = Stream.of(3,4,5,9)
// identity 默认值
// 第一次的时候会将默认值赋值给 x
// 之后每次会将 上一次的操作结果赋值给x y就是每次从数据中获取的元素
.reduce(0, (x, y) -> x + y);
System.out.println(sum);
// 获取最大值
Integer max = Stream.of(4,5,3,9)
.reduce(0, (x, y) -> x > y? x : y);
System.out.println(max);
Integer sumAge = Stream.of(
new Person("aa", 22),
new Person("bb", 32),
new Person("cc", 12),
new Person("dd", 56),
new Person("ee", 43)
).map(Person::getAge)
.reduce(0, Integer::sum);
System.out.println(sumAge);
// 求年龄的最大值
Integer maxAge = Stream.of(
new Person("aa", 22),
new Person("bb", 32),
new Person("cc", 12),
new Person("dd", 56),
new Person("ee", 43)
).map(Person::getAge)
.reduce(0, Math::max);
System.out.println(maxAge);
Integer count = Stream.of("a", "b", "c", "d").map(ch -> "a".equals(ch) ? 1 : 0)
.reduce(0, Integer::sum);
System.out.println(count);
3.14 mapToInt
如果需要将 Stream 中的 Integer 类型转换成 int 类型,可以使用 mapToInt 方法来实现
使用
// Integer 占用的内存比 int 多很多,在 Stream 流操作中会自动装修和拆箱操作
Integer arr[] = {1,2,3,5,6,8};
Stream.of(arr)
.filter(i -> i > 0)
.forEach(System.out::println);
System.out.println("----------------");
// 为了提高程序代码的效率,我们可以先将流中的 Integer 数据转换为 int 数据,然后再操作
IntStream intStream = Stream.of(arr)
.mapToInt(Integer::intValue);
intStream.filter(i -> i > 3)
.forEach(System.out::println);
3.15 concat
如果有两个流希望合并成一个流,那么可以使用 Stream 接口的静态方法 concat
使用:
Stream<String> stream1 = Stream.of("a","b","c");
Stream<String> stream2 = Stream.of("x","y","z");
// 通过 concat 方法将两个流合并为一个新的流
Stream.concat(stream1, stream2).forEach(System.out::println);