3 流与Lambda表达式的使用详解
简单说,对 Stream 的使用就是实现一个 filter-map-reduce 过程,产生一个最终结果,或者导致一个副作用(side effect)。
3.1流的构造
Stream在Java SE 8中非常重要,我们希望可以在JDK中尽可能广的使用Stream。我们为Collection提供了stream()和parallelStream(),以便把集合转化为流;此外数组可以通过Arrays.stream()被转化为流。
除此之外,Stream中还有一些静态工厂方法(以及相关的原始类型流实现),这些方法被用来创建流,例如Stream.of(),Stream.generate以及IntStream.range。其它的常用类型也提供了流相关的方法,例如String.chars,BufferedReader.lines,Pattern.splitAsStream,Random.ints和BitSet.stream。
下面提供最常见的几种构造 Stream 的样例。
// 构造流的几种常见方法
// 1. Individual values
Stream stream = Stream.of("a","b", "c");
// 2. Arrays
String [] strArray = new String[]{"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);
// 3. Collections
List<String> list =Arrays.asList(strArray);
stream = list.stream();
需要注意的是,对于基本数值型,目前有三种对应的包装类型 Stream:
IntStream、LongStream、DoubleStream。当然我们也可以用 Stream<Integer>、Stream<Long> >、Stream<Double>,但是 boxing 和 unboxing 会很耗时,所以特别为这三种基本数值型提供了对应的 Stream。
Java8 中还没有提供其它数值型 Stream,因为这将导致扩增的内容较多。而常规的数值型聚合运算可以通过上面三种 Stream 进行。
// 数值流的构造
IntStream.of(new int[]{1, 2,3}).forEach(System.out::println);
IntStream.range(1,3).forEach(System.out::println);
IntStream.rangeClosed(1,3).forEach(System.out::println);
3.2流转换为其它数据结构
// 流转换为其它数据结构
// 1. Array
String[] strArray1 = stream.toArray(String[]::new);
// 2. Collection
List<String> list1 =stream.collect(Collectors.toList());
List<String> list2 =stream.collect(Collectors.toCollection(ArrayList::new));
Set set1 =stream.collect(Collectors.toSet());
Stack stack1 = stream.collect(Collectors.toCollection(Stack::new));
// 3. String
String str =stream.collect(Collectors.joining()).toString();
一个 Stream 只可以使用一次,上面的代码为了简洁而重复使用了数次。
3. 3 流的Intermediate操作
接下来,当把一个数据结构包装成 Stream 后,就要开始对里面的元素进行各类操作了。常见的操作可以归类如下。
· Intermediate:
map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered
map/flatMap
我们先来看 map。如果你熟悉 scala 这类函数式语言,对这个方法应该很了解,它的作用就是把 input Stream 的每一个元素,映射成 output Stream 的另外一个元素。
// 转换大写
List<String> output =wordList.stream().
map(String::toUpperCase).
collect(Collectors.toList());
下面看一个完整示例:
package lambda;
import java.util.Arrays;
import java.util.List;
public class MapAndReduceTest {
// applying 12% VAT on each purchase
// Without lambda expressions:
List<Integer> costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
public void applying(){
for (Integer cost : costBeforeTax) {
double price = cost + .12*cost;
System.out.print(price + " ");
}
}
// Applying 12% VAT on each purchase
// Old way:
public void total(){
double total = 0;
for (Integer cost : costBeforeTax) {
double price = cost + .12*cost;
total = total + price;
}
System.out.println("Total : " + total);
}
// With Lambda expression:
List<Integer> costBeforeTax2 = Arrays.asList(100, 200, 300, 400, 500);
public void applying2(){
costBeforeTax2.stream().map((cost) -> cost + .12*cost)
.forEach(System.out::print);
}
public void total2(){
//reduce() 是将集合中所有值结合进一个,Reduce类似SQL语句中的sum(), avg() 或count()
double bill = costBeforeTax2.stream().map((cost) -> cost + .12*cost)
.reduce((sum, cost) -> sum + cost)
.get();
System.out.println("Total : " + bill);
}
public static void main(String[] args) {
MapAndReduceTest mr = new MapAndReduceTest();
mr.applying();
mr.total();
System.out.println("=========================================");
mr.applying2();
mr.total2();
}
}
/*Output:
112.0 224.0 336.0 448.0 560.0 Total : 1680.0
=========================================
112.0224.0336.0448.0560.0Total : 1680.0
* */
// 平方数
List<Integer> nums =Arrays.asList(1, 2, 3, 4);
List<Integer> squareNums =nums.stream().
map(n -> n * n).
collect(Collectors.toList());
这段代码生成一个整数 list 的平方数 {1, 4, 9, 16}。<