Lambda表达式之Stream

Java8引入了Stream以实现对集合更方便地进行函数式编程。

Set和List都可以通过stream()方法获取Stream实例;

数组可以通过Arrays.stream(array)工具方法获取Stream实例。

 

对Stream的操作分lazy方法和eager方法:

lazy方法依然返回Stream实例,并没有立马开始计算;

而eager方法相当于正式进入运算阶段,返回的类型由具体方法决定。

 

以下是操作Stream的常见方法,这些方法的操作效果可以参照reactivex编程的数据流图,同一个方法名的操作大体是一致的,何况java reactivex编程和Lambda的结合还是比较紧密的:

RxMarbles -- Interactive diagrams of Rx Observables

Reactivex Operators

 

先定义一个用于测试的类

class Item {
    public String id;
    public int count;

    public Item(String id, int count) {
        this.id = id;
        this.count = count;
    }
}

 

然后初始化了如下三个实例

Item item1 = new Item("it1", 1);
Item item2 = new Item("it2", 2);
Item item3 = new Item("it3", 3);

 

和一个Item列表

List<Item> list = new ArrayList<>();
list.add(item1);
list.add(item2);
list.add(item3);

 

1. forEach -- eager方法,循环对每个元素执行相关指令

list.stream().forEach(item -> System.out.println(item.id));

 这段代码单独放在这边没有什么意义,可以用于下面lazy方法的调试

 

 2. filter -- lazy方法,用于过滤数据

 

list.stream()
    .filter(item -> item.count > 2)
    .forEach(item -> System.out.println(item.id));
输出的结果只包含it3。

 

3. map -- lazy方法,用于转变Stream

list.stream()
    .map(item -> item.id)   //从Item的Stream转变成了String的Stream
    .forEach(System.out::println);
 类似的还有mapToInt、mapToLong、mapToDouble,传入的lambda或者函数应该返回int、long、double类型的数据,转换成的Stream依次为IntStream、LongStream、DoubleStream,这些类型的Stream有自己专有的成员函数用于简化相关的计算,例如sum()返回加和。

4. flatMap -- lazy方法,用于将多个Stream转换成一个Stream

Arrays.stream(new String[]{"A,B,C", "D,E"})
      .flatMap(s -> Arrays.stream(s.split(",")))
      .forEach(System.out::println);
 flatMap中的lambda或函数需要返回Stream,这边将字符串数组中的每个字符串按逗号切分转换成了两个Stream,然后flatMap将这两个Stream合并成了一个大的Stream;flatMap也有map类似的三个的额外方法。

5. reduce -- eager方法,实现从一组值中生成一个值

OptionalInt sum = Arrays.stream(new int[]{1, 2, 3})
				.reduce((left, right) -> left + right);
System.out.println(sum.orElse(0));

 这边通过reduce实现求知的效果,由于没有设置初始值,所以返回的是一个Optional类型的对象,该对象的作用在于警示有可能返回的值不存在,需要先判断再取值。

进一步演示通过reduce查找Item列表中count最大的元素

Item res = list.stream()
        .reduce(item1, (left, right) -> left.count > right.count ? left : right);
System.out.println(res.id);	

 reduce中传入的lambda或函数应该起到两个元素变成一个的作用。

6. sum、count、min、max -- eager方法,可以认为是封装reduce后的常用方法

Optional<Item> min = list.stream()
	.min(Comparator.comparingInt(item -> item.count));

7. collect -- eager方法,用于将Stream转换成集合或Map

Set<Integer> set = list.stream()
                .filter(item -> item.count > 2)
                .map(item -> item.count)
                .collect(Collectors.toSet());

 collect还有其他高阶的用法,之后再补充

 

以上都只演示了比较简单的处理逻辑,对于复杂的逻辑可以写在函数中,然后将该函数作为相关方法的参数。

 

关于引入Stream对java语法的影响:

1. interface里面可以实现方法,但是必须用default修饰

比如Collection中生成Stream的接口,主要还是为了兼容以前的代码

default Stream<E> stream() {
    return StreamSupport.stream(spliterator(), false);
}

The Three Rules

If you’re ever unsure of what will happen with default methods or with multiple inheritance of behavior, there are three simple rules for handling conflicts:

1. Any class wins over any interface. So if there’s a method with a body, or an abstract declaration, in the superclass chain, we can ignore the interfaces completely.

2. Subtype wins over supertype. If we have a situation in which two interfaces are competing to provide a default method and one interface extends the other, the subclass wins.

3. No rule 3. If the previous two rules don’t give us the answer, the subclass must either implement the method or declare it abstract.

Rule 1 is what brings us compatibility with old code.

2. interface里面可以有static方法

比如Stream.of()

public static<T> Stream<T> of(T... values) {
    return Arrays.stream(values);
}

 

 

 

 

猜你喜欢

转载自xuanzhui.iteye.com/blog/2316989