首先,让我们澄清一下java流量的概念:流量是代表任何能够输出数据的数据源对象或能够接受数据的接收对象。其本质是数据传输,根据数据传输特性将流量抽象为各种类型,便于更直观的数据操作。 今天我们单独来谈谈java中的stream流,着重讲一下实际操作stream流。
Stream流不是一种不保存数据的数据结构。它只在原始数据集中定义了一组操作。这些操作是惰性的,即每当访问流中的一个元素时,这一系列操作就会在这个元素上进行。stream从起源开始(source)到接收的(sink)有序数据。Stream 它就像一个先进的迭代器,但只能通过一次,就像一条河流向东流动;在流动过程中,对流中的元素,如“过滤长度大于 10 的字符串”、“获得每个字符串的首字母”等。Stream不保存数据,所以每个Stream流只能使用一次。
Java的IO模型设计非常出色。它采用Decorator(装饰者)模式,根据功能划分Stream。您可以动态组装这些Stream,以获得您需要的功能。例如,如果您需要一个缓冲文件输入流,您应该使用FileinputStream和BufferedInputStream。Stream流上应用的操作可分为两种:Intermediate(中间操作)和Terminal(终止操作)。中间操作的返回结果是Stream,因此可以叠加多个中间操作;终止操作用于返回我们最终需要的数据,只有一个终止操作。
举个例子:
List list = new ArrayList<>();
list.add("北京");
list.add("中国");
list.add("世界");
list.add("世界");
long count = list.stream().distinct().count();
System.out.println(count);
distinct() 该方法是一个中间操作(去重),它将返回一个新的流(没有共同元素)。
Stream distinct();
count() 该方法是终端操作,返回流中的元素数。
long count();
中间操作不会立即执行。只有当终端操作时,流量才会开始真正遍历,用于映射、过滤等。一般来说,多个操作一次实施,性能大大提高。
使用Stream流,可以清楚地知道我们想做什么样的数据集操作,具有很强的可读性。并且很容易获得并行Stream流,不需要自己编写多线程代码,可以让我们更加专注于业务逻辑。
让我们具体介绍一下stream流的一些实际操作:
一、流的创造:
如果是数组,可以使用 Arrays.stream() 或者 Stream.of() 创建流;如果是集合,可以直接使用 stream() 由于该方法已添加到方法创建流中 Collection 接口中。
public class CreateStreamDemo {
public static void main(String[] args) {
String[] arr = new String[]{"1", "2", "3"};
Stream stream = Arrays.stream(arr);
stream = Stream.of("1", "2", "3");
List list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
stream = list.stream();
}
}
查看 Stream 如果是源码,你会发现的 of() 该方法实际上在内部调用 Arrays.stream() 方法。
public static Stream of(T... values) {
return Arrays.stream(values);
}
此外,集合也可以调用 parallelStream() 创建并发流的方法,默认使用 ForkJoinPool.commonPool()线程池。
List aList = new ArrayList<>();
Stream parallelStream = aList.parallelStream();
二、操作流
Stream 类别提供了许多有用的操作流方法,我们重点介绍一些出场率高的方法。
1)过滤
通过 filter() 该方法可以从流动中筛选出我们想要的元素。
public class FilterStreamDemo {
public static void main(String[] args) {
List list = new ArrayList<>();
list.add("java");
list.add("C");
list.add("C++");
list.add("phython");
Stream stream = list.stream().filter(element -> element.contains("j"));
stream.forEach(System.out::println);
}
}
filter() 方法接收的是一个 Predicate(Java 8 新的函数接口,接受输入参数返回布尔值结果)类型的参数,因此,我们可以直接使用一个 Lambda 表达式传递给该方法,比如说 element -> element.contains("j") 就是筛选出带“j”的字符串。
forEach() 方法接收的是一个 Consumer(Java 8 新的函数接口,接受输入参数和无返回操作)类型的参数,类名 :: 方法名是 Java 8 新语法的引入,System.out 返回 PrintStream 类,println 如我们所知,方法是打印的。
stream.forEach(System.out::println); 相当于在 for 类似于以下代码的循环打印:
for (String s : strs) {
System.out.println(s);
}
显然,一行代码看起来更简单。看看程序的输出结果:Java
2)映射
如果你想通过某种操作将流中的元素转化为新的流中元素,可以使用它 map() 方法。
public class MapStreamDemo {
public static void main(String[] args) {
List list = new ArrayList<>();
list.add("java");
list.add("C");
list.add("C++");
list.add("phython");
Stream stream = list.stream().map(String::length);
stream.forEach(System.out::println);
}
}
map() 方法接收的是一个 Function(Java 8 接受输入参数的新函数接口 T,回到一个结果 R)类型参数,此时参数 为 String 类的 length 方法,也就是把 Stream 流转成一个 Stream 的流。
程序输出结果如下:
3
3
2
3
3)匹配
Stream 元素匹配提供了三种方法,即:
anyMatch(),只要有条件匹配元素进入,就会返回 true。
allMatch(),只有一个元素不匹配输入条件,返回 false;如果全部匹配,则返回 true。
noneMatch(),只要有条件匹配元素进入,就会返回 false;如果全部匹配,则返回 true。
public class MatchStreamDemo {
public static void main(String[] args) {
List list = new ArrayList<>();
list.add("java");
list.add("C");
list.add("C++");
list.add("phython");
boolean anyMatchFlag = list.stream().anyMatch(element -> element.contains("j"));
boolean allMatchFlag = list.stream().allMatch(element -> element.length() < 10);
boolean noneMatchFlag = list.stream().noneMatch(element -> element.endsWith("b"));
System.out.println(anyMatchFlag);
System.out.println(allMatchFlag);
System.out.println(noneMatchFlag);
}
}
因为“java”以“j”字母开头,所以 anyMatchFlag 应该为 true;因为“java”、“C”、“C++”、“phython”字符串长度小于 10,所以 allMatchFlag 为 true;因为 4 字符串的结尾都不是“b”,所以 noneMatchFlag 为 true。
程序输出结果如下:
true
true
true
4)组合
reduce() 该方法的主要作用是把握 Stream 中元素的组合有两种用法:
Optional reduce(BinaryOperator accumulator)
没有起始值,只有一个参数,即操作规则,此时返回 Optional。
T reduce(T identity, BinaryOperator accumulator)
有起始值、操作规则和两个参数。此时返回的类型与起始值类型相同。
让我们来看看下面的例子。
public class ReduceStreamDemo {
public static void main(String[] args) {
Integer[] ints = {0, 1, 2, 3};
List list = Arrays.asList(ints);
Optional optional = list.stream().reduce((a, b) -> a + b);
Optional optional1 = list.stream().reduce(Integer::sum);
System.out.println(optional.orElse(0));
System.out.println(optional1.orElse(0));
int reduce = list.stream().reduce(6, (a, b) -> a + b);
System.out.println(reduce);
int reduce1 = list.stream().reduce(6, Integer::sum);
System.out.println(reduce1);
}
}
操作规则可以是 Lambda 表达式(例如 (a, b) -> a + b),也可以是类名::方法名(例如 Integer::sum)。
程序运行结果如下:
6
6
12
12
0、1、2、3 当没有起始值相加时,结果是 6;有起始值 6 结果是 12。
三、转换流
由于集合或数组可以转换为流量,因此也应该有相应的方法来转换流量——collect() 该方法满足了这一需求。
public class CollectStreamDemo {
public static void main(String[] args) {
List list = new ArrayList<>();
list.add("java");
list.add("C");
list.add("C++");
list.add("phython");
String[] strArray = list.stream().toArray(String[]::new);
System.out.println(Arrays.toString(strArray));
List list1 = list.stream().map(String::length).collect(Collectors.toList());
List list2 = list.stream().collect(Collectors.toCollection(ArrayList::new));
System.out.println(list1);
System.out.println(list2);
String str = list.stream().collect(Collectors.joining(", ")).toString();
System.out.println(str);
}
}
toArray() 这种方法可以将流转换成数组,你可能会更好奇 String[]::new,它是什么?让我们看看。 toArray() 源代码的方法。
A[] toArray(IntFunction generator);
也就是说 String[]::new 是一个 IntFunction,通过反编译字节码,可以生成所需的新数组函数,看看它到底是什么:
String[] strArray = (String[])list.stream().toArray((x$0) -> {
return new String[x$0];
});
System.out.println(Arrays.toString(strArray));
也就是说,它相当于返回指定长度的字符串数组。
当我们需要按照一定的规则将一个集合转换为另一个集合时,我们可以使用它 map() 方法和 collect() 方法。
List list1 = list.stream().map(String::length).collect(Collectors.toList());
通过 stream() 该方法创建集合流,然后通过 map(String:length) 将其映射成字符串长度的新流,最后通过 collect() 该方法将其转换为新的集合。
Collectors 它是一种内置一系列收集器的收集器工具,例如 toList() 收集元素的方法是一种新的方法 java.util.List 中;比如说 toCollection() 收集元素的方法是一种新的方法 java.util.ArrayList 中;比如说 joining() 方法律将元素收集到一个可以用分隔符指定的字符串中。
看看程序的输出结果:
[java, C, C++, phython]
[3, 3, 2, 3]
[java, C, C++, phython]
java, C, C++, phython
朋友们,看完这篇文章,你们觉得吗?stream流的功能非常强大。让我们一起学习和掌握stream流的相关知识。当然,要彻底掌握stream流,需要更多的实践来学习理论。“学而不思则忽,思而不学则危。”在不断学习新知识的同时,一定要记得多思考,举一反三方学习。