什么是Stream API
关于stream,IBM对其有一个概括。链接
Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念。它也不同于 StAX 对 XML 解析的 Stream,也不是 Amazon Kinesis 对大数据实时处理的 Stream。Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。通常编写并行代码很难而且容易出错, 但使用 Stream API 无需编写一行多线程的代码,就可以很方便地写出高性能的并发程序。所以说,Java 8 中首次出现的 java.util.stream 是一个函数式语言+多核时代综合影响的产物。
怎么使用stream API
获得一个stream对象
对于实现了List
接口的对象,获取stream非常简单。
List<Integer> nums = new ArrayList<>();
Stream<Integer> stream = nums.stream();
调用List
的stream()
方法,即可得到一个Stream
对象。
而对于int[]
一类的数组,获取stream也不是很困难。
int[] nums = {1, 2, 3, 4, 5, 6, 7};
IntStream stream = Arrays.stream(nums);
由于Java的基本数据类型导致的问题,此处的stream不再为Stream<T>
类型(毕竟int并不是对象),而是一个所谓的IntStream
,不过这并不影响其的使用。要获得Stream<Integer>
类型的stream,我们只需要将数组更换为Integer[]
类型。
Integer[] nums = {1, 2, 3, 4, 5, 6, 7};
Stream<Integer> stream = Arrays.stream(nums);
stream有什么用?
获得了一个stream对象之后,我们就可以开始利用它。
求数组长度
最简单的,调用stream.count()
方法,即可获得数组的长度。
Integer[] nums = {1, 2, 3, 4, 5, 6, 7};
Stream<Integer> stream = Arrays.stream(nums);
long size = Arrays.stream(nums).count();
//上下两行代码等价
size = nums.length;
有人可能会说,就这?这只是stream最简单的使用,接着来些好玩的例子。
统计不及格学生的学号
Map<String, Integer> score = new HashMap<>();
score.put("201806061100", 95);
score.put("201806061101", 75);
score.put("201806061102", 53);
score.put("201806061103", 54);
score.put("201806061104", 77);
score.keySet().stream()
.filter(key -> score.get(key) < 60)
.forEach(key -> System.out.println(key));
该例子使用了stream的filter方法,其中使用了一个lambda表达式,传入一个key,返回key所对应的value是否小于60。这个filter会返回所有成绩低于60分的学生的学号。
接下来,使用了forEach方法,继续利用lambda表达式,将这些学号依次输出。
将stream转回List
List<String> blame = score.keySet().stream()
.filter(key -> score.get(key) < 60)
.collect(Collectors.toList());
使用collect方法,指定转换方式为toList
,即可将其转回List<String>
对值进行额外的处理
score.keySet().stream()
.filter(key -> score.get(key) < 60)
.map(key -> key.substring(8))
.forEach(key->System.out.println(key));
在这里,我们使用了一个新方法 map
,可以将其解释为映射
。.map(key -> key.substring(8))
,会返回一个新的stream对象,其中的每一项都执行了 .substring(8)
的操作。
排序
Map<String, Integer> score = new HashMap<>();
score.put("201806061100", 95);
score.put("201806061101", 75);
score.put("201806061102", 53);
score.put("201806061103", 54);
score.put("201806061104", 77);
score.keySet().stream()
.sorted((k1, k2) -> score.get(k2) - score.get(k1))
.forEach(key -> System.out.println(key));
使用 sorted
方法,并在其中实现一个比较器,即可完成按照成绩从高到低的顺序进行排序。
更多有趣的方法
有关stream的方法还有很多,读者可自行进入Java的文档进行了解,本文不再进行介绍。
引言
一直以来,Java有一个为人所不满的缺点:啰嗦。有些简单的东西,可能需要撰写更多代码才能完成。
幸好,Java为我们推出了很多新功能,来解决其中的问题。本文主要想介绍Java8的lambda表达式。
lambda
之前的版本
先来看这么一串代码
String[] name = {"Mike", "Tom", "Jerry", "Potty", "Moggy"};
Arrays.sort(name, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
有五个小朋友,他们有不同的名字。我们希望按照字母的顺序为其排序。按照Java7的写法,我们需要完成一个Comparator
接口,并实现其中的compare
方法。以完成排序。
使用lambda的写法
使用IDEA的朋友们可能发现,之前的代码被标为了黄色。IDEA会将你的代码改成这样
Arrays.sort(name, (o1, o2) -> o1.compareTo(o2));
原本的compare
函数消失了,变成了 (o1, o2) -> o1.compareTo(o2)
。这是怎么回事呢?
什么是lambda
其实这就是lambda表达式,Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。使用 Lambda 表达式可以使代码变的更加简洁紧凑。(o1, o2) -> o1.compareTo(o2)
意为,传入了o1,o2两个参数,将 o1.compareTo(o2)
的值作为返回值。简单的语法就完成了原本五六行代码的工作量。
还能更简单吗?
能!如果使用Java8的另外一个特性,方法引用
,撰写的代码量会更少。有关方法引用的使用,我会在之后的文章里进行介绍。
lambda的更多示例
启动子线程
new Thread(() -> System.out.println("Hello World")).start();
等价于
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello World");
}
}).start();
显而易见,使用lambda语句,可以使代码更为简洁。