※目录
⚜ 1 简介
⚜ 2 流接口Stream API
⚜ 3 流操作Stream Operations
⚜ 4 综合示例
⚜ 5 小结
⚜ 参考文章
本文涉及的示例源代码可在GitHub账号【hcysky】找到并下载。
返回目录
✨ 在Java8 API
中增添了新的抽象类Streams
,将待处理的元素集合视作一种流,并在管道中传输,且可在管道的节点上进行多种处理,比如排序、筛选、聚合等。
✨ 因此,在Java8及以上版本中,可利用stream().filter
来筛选出理想的结果,也即根据条件来查找对象集合。
返回目录
Streams 使用stream()和of()方法,可以由不同的数据源创建,比如集和Collection、数组Array。
String[] array = new String[]{"Beijing", "Tianjin", "Shanghai"};
Stream<String> stream_method = Arrays.stream(array);//using stream() method
Stream<String> stream_of = Stream.of("Beijing", "Tianjin", "Shanghai");//using of() method
stream()
默认方法被添加到集合Collection
接口,并允许使用任何集合作为元素源创建Stream
:
Stream<String> stream = list.stream();
import java.util.*;
import java.util.stream.Stream;
//使用method()和of()方法来创建Stream
//数据源包括集和Collection、数组Array
//数据类型包括Integer和String
public class stream_creation {
public static void main(String[] args) {
//Integer
List<Integer> list_int = Arrays.asList(9, 12, 25, 36, 42, 55, 73, 95); //by Collection
System.out.println("Output(x > 10 && x < 30):");
list_int.stream().filter(number -> (number > 10 && number < 30)).forEach(System.out::println);//使用stream().filter获取大于10且小于30的数,并打印出来
Integer[] array_int = new Integer[] { 9, 12, 25, 36, 42, 55, 73, 95 };//by Array
Stream<Integer> stream_int1 = Arrays.stream(array_int);//using stream() method
System.out.println("Output(x % 2 == 0):");
stream_int1.filter(number -> (number % 2 == 0)).forEach(System.out::println);
Stream<Integer> stream_int2 = Stream.of(9, 12, 25, 36, 42, 55, 73, 95);//using of() method
System.out.println("Output(x % 5 == 0):");
stream_int2.filter(number -> (number % 5 == 0)).forEach(System.out::println);
//String
List<String> list_str = Arrays.asList("Beijing", "Tianjin", "Shanghai"); //by Collection
System.out.println("Output(location != Shanghai):");
list_str.stream().filter(location -> location != "Shanghai").forEach(System.out::println);
String[] array_str = new String[] { "Beijing", "Tianjin", "Shanghai" };//by Array
Stream<String> stream1 = Arrays.stream(array_str);//using stream() method
System.out.println("Output(location != Beijing):");
stream1.filter(location -> location != "Beijing").forEach(System.out::println);
Stream<String> stream2 = Stream.of("Beijing", "Tianjin", "Shanghai");//using of() method
System.out.println("Output(location != Tianjin):");
stream2.filter(location -> location != "Tianjin").forEach(System.out::println);
}
}
Stream API
还通过提供以并行模式在流元素上运行操作的parallelStream()
方法来简化多线程。parallelStream
是流并行处理程序的替代方法。- 下面的代码允许为流的每个元素并行的运行
doWork()
方法:
list.parallelStream().forEach(element -> doWork(element));
以下实例利用 parallelStream()
输出满足条件的整数及其数量.
import java.util.*;
import java.util.stream.Stream;
public class stream_multi_threading {
public static void main(String[] args) {
List<Integer> list_int = Arrays.asList(9, 12, 25, 36, 42, 55, 73, 95); //by Collection
System.out.println("Output(x > 10 && x < 30):");
//使用stream().filter获取大于10且小于30的数,并打印出来
list_int.stream().filter(number -> (number > 10 && number < 30)).forEach(System.out::println);
//打印满足上述条件整数的数量
long count = list_int.parallelStream().filter(number -> (number > 10 && number < 30)).count();
System.out.println("Output number(x > 10 && x < 30):\t" + count);
}
}
返回目录
- 可以基于流执行许多有用的操作。
- 流操作可以细分为中间操作 (返回
Stream
) 和终端操作 (返回定类型的结果)。中间操作允许有多重链式处理。- 值得注意的是:对流的操作不会改变源。
简单示例如下:distinct()
方法表示一个中间操作,它创建一个新流,其中包含前一个流的唯一性元素集。count()
方法是一个终端操作,它返回流的大小。
long count = list.stream().distinct().count();
流 API 有助于替换
for
、for-each
和while
循环。它专注于操作的逻辑,但不能处理元素序列的迭代。
下面的示例:是将for
循环替代为stream
的iterating
的操作:
for循环:
for (String string : list) {
if (string.contains("Apple")) {
return true;
}
}
Stream的Iterating操作:
boolean isExist = list.stream().anyMatch(element -> element.contains("Apple"));
import java.util.*;
import java.util.stream.Stream;
public class stream_iterating {
public static void main(String[] args) {
List<Integer> list_int = Arrays.asList(9, 12, 25, 36, 42, 55, 73, 95); //by Collection
//基于Iterating,查询是否有整除5发整数
System.out.print("Output( 25 ? ):\t");
boolean isExistNum1 = list_int.stream().anyMatch(number -> number == 25);
if (isExistNum1) {
System.out.println("Yes");
} else {
System.out.println("No");
}
//基于Iterating,查询是否有整数17
System.out.print("Output( 17 ? ):\t");
boolean isExistNum2 = list_int.stream().anyMatch(number -> number == 17);
if (isExistNum2) {
System.out.println("Yes");
} else {
System.out.println("No");
}
}
}
filter()
方法允许我们选取满足谓词predicate
的元素流。
例如,下面语句创建了list
,并添加了元素:
ArrayList<String> list = new ArrayList<>();
list.add("One");
list.add("Derek");
list.add("Change");
list.add("Italy");
list.add("");
list.add("Thursday");
list.add("Apple");
下面的代码创建一个List的Stream,查找此流中包含 字符a
的所有元素,并创建一个仅包含筛选元素的新流:
Stream<String> stream = list.stream().filter(element -> element.contains("a"));
import java.util.*;
import java.util.stream.Stream;
public class stream_filtering {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("One");
list.add("Derek");
list.add("Change");
list.add("Italy");
list.add("");
list.add("Thursday");
list.add("Apple");
Stream<String> stream = list.stream().filter(element -> element.contains("a"));
stream.forEach(System.out::println);//forEach()迭代流中的每个元素
}
}
map():将特定的函数作用于源Stream的元素,然后把得到的新元素形成新的Stream。
以下代码片段使用 map 输出了对应元素的平方数:
List<Integer> numList = Arrays.asList(1, 2, 3, 4, 5);
// 获取对应的平方数
List<Integer> numSquareList = numList.stream().map( number -> number*number).distinct().collect(Collectors.toList());
import java.util.*;
import java.util.stream.Collectors;
// import java.util.stream.Stream;
public class stream_mapping {
public static void main(String[] args) {
List<Integer> numList = Arrays.asList(1, 2, 3, 4, 5);
System.out.println("numList:");
numList.stream().forEach(System.out::println);
//基于map()方法,将源numList的所有元素计算出平方数,并形成了心流numSquareList
List<Integer> numSquareList = numList.stream().map(number -> number * number).distinct()
.collect(Collectors.toList());
System.out.println("\nnumSquareList:");
numSquareList.stream().forEach(System.out::println);
}
}
anyMatch()、allMatch()、noneMatch()
:流 API 提供了一组方便的工具,用于根据某些谓词predicate
验证序列的元素。
根据它们的名字,其功能是一目了然的。
这些都是返回布尔值boolean
的终端操作terminal operations
:
boolean isAnyValid = list.stream().anyMatch(element -> element.contains("h")); // true or false
boolean isAllValid = list.stream().allMatch(element -> element.contains("h")); // true or false
boolean isNoneValid = list.stream().noneMatch(element -> element.contains("h")); // true or false
对于空的流,具有任何给定谓词predicate
的 allMatch() 方法都将返回 true:
Stream.empty().allMatch(Objects::nonNull); // true
Stream.empty().anyMatch(Objects::nonNull); // false
同样,这是合理的,因为我们找不到满足此条件的元素。
import java.util.*;
import java.util.stream.Stream;
public class stream_matching {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("One");
list.add("Derek");
list.add("Change");
list.add("Italy");
list.add("");
list.add("Thursday");
list.add("Apple");
list.stream().forEach(System.out::println);//输出整个list
boolean isAnyValid = list.stream().anyMatch(element -> element.contains("k")); //anyMatch
if (isAnyValid) {
System.out.println("anyMatch(k) is true");
} else {
System.out.println("anyMatch(k) is false");
}
boolean isAllValid = list.stream().allMatch(element -> element.contains("a")); //allMatch
if (isAllValid) {
System.out.println("allMatch(a) is true");
} else {
System.out.println("allMatch(a) is false");
}
boolean isNoneValid = list.stream().noneMatch(element -> element.contains("x")); //noneMatch
if (isNoneValid) {
System.out.println("noneMatch(x) is true");
} else {
System.out.println("noneMatch(x) is false");
}
}
}
- reduce():Stream API 允许在 Stream 类型中,根据指定的函数将元素序列规约到某个值。
- 此方法采用两个参数:第一个 为 起始值,第二个 为累加器函数。
假设您有一个 List,并且希望得到所有这些元素和一些初始Integer
的总和(在此示例中为 10)。可以运行以下代码,结果将为 16 (10 + 1 + 2 + 3)。
List<Integer> integers = Arrays.asList(1, 2, 3);
Integer reduced = integers.stream().reduce(10, (a, b) -> a + b);
import java.util.*;
public class stream_reduction {
public static void main(String[] args) {
List<Integer> intList = Arrays.asList(1, 2, 3);
intList.stream().forEach(System.out::println);
Integer reduceSum = intList.stream().reduce(10, (a, b) -> a + b);
System.out.println("result: " + reduceSum);
}
}
- 规约也可以由 Stream 类型的
collect()
方法提供。在将流转换为聚合器或映射器,并以单个字符串的形式表示流的情况下,此操作非常方便。- 有一个实用程序类聚合器
Collectors
,它为几乎所有典型的收集操作提供了解决方案。对于某些负责的任务,可以创建自定义聚合器。
List<String> resultList
= list.stream().map(element -> element.toUpperCase()).collect(Collectors.toList());
import java.util.*;
import java.util.stream.Collectors;
public class stream_collecting {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("One");
list.add("Derek");
list.add("Change");
list.add("Italy");
list.add("");
list.add("Thursday");
list.add("Apple");
System.out.println("List: ");
list.stream().forEach(System.out::println);
System.out.println("\nresultList: ");
List<String> resultList = list.stream().map(element -> element.toUpperCase()).collect(Collectors.toList());
resultList.stream().forEach(System.out::println);
}
}
返回目录
下面为一个简单的示例:新建了一个类
Fruit
,包含两个成员变量price
和name
;使用List
来存储Fruit类型的变量,然后由stream().filter
来筛选满足条件的集和。
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;
public class stream_filter {
public static void main(String[] args) throws IOException{
List<Fruit> fruits = new ArrayList<Fruit>();
fruits.add(new Fruit("Apple", 9.9));
fruits.add(new Fruit("Orange", 12.5));
fruits.add(new Fruit("Banana", 15.9));
//展示原始的水果列表
System.out.println(">>>>>> Before filter: ");
for(Fruit r : fruits){
System.out.println("Name: " + r.name + ", Price: " + r.price);
}
//filter-1: 筛选出水果价格大于10.0的
List<Fruit> res1 = fruits.stream().filter(f -> f.getPrice() > 10.0).collect(Collectors.toList()); //Note: 此处的f可以设置为任意的变量名
System.out.println(">>>>>> After filter-1: ");
for( Fruit r : res1){
System.out.println("Name: " + r.name + ", Price: " + r.price);
}
//filter-2: 筛选出水果名称为"Apple"的
List<Fruit> res2 = fruits.stream().filter(o -> "Apple".equals(o.getName())).collect(Collectors.toList()); //Note: 此处的o可以设置为任意的变量名
System.out.println(">>>>>> After filter-2: ");
for( Fruit r : res2){
System.out.println("Name: " + r.name + ", Price: " + r.price);
}
}
public static class Fruit{
String name;
double price;
public Fruit(String name, double price) {
this.name = name;
this.price = price;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setPrice(double price) {
this.price = price;
}
public double getPrice() {
return this.price;
}
}
}
返回目录
在本文中,我们简要介绍了Java 8 最有趣的功能之一 Java Stream
,并为每个常用API
提供了可直接运行的源码示例
,也提供了一个综合示例 。 当然,还有许多使用Stream
的更高级的例子;本文的目的只是提供一个快速且实用的介绍,比如:介绍您可以使用该功能开始做什么,并作为探索和进一步学习的起点。