您的位置:首页 > 编程语言 > Java开发

java8 Lambda 与 Stream

2016-08-04 08:52 344 查看

Lambda

Lambda表达式的语法

基本语法:

(parameters) -> expression




(parameters) ->{ statements; }


函数式接口

Java SE 8中增加了一个新的包:
java.util.function
,它里面包含了常用的函数式接口,例如:

Predicate<T>
——接收
T
对象并返回
boolean
[译:断言-prɛdɪkət]

Consumer<T>
——接收
T
对象,不返回值[译:消费-kən’sumɚ]

Function<T, R>
——接收
T
对象,返回
R
对象

Supplier<T>
——提供
T
对象(例如工厂),不接收值

UnaryOperator<T>
——接收
T
对象,返回
T
对象

BinaryOperator<T>
——接收两个
T
对象,返回
T
对象

除了上面的这些基本的函数式接口,我们还提供了一些针对
原始类型(Primitive type)
的特化(Specialization)函数式接口,例如IntSupplier和LongBinaryOperator。(我们只为int、long和double提供了特化函数式接口,如果需要使用其它原始类型则需要进行类型转换)同样的我们也提供了一些针对多个参数的函数式接口,例如BiFunction

lambda 变量

lambda可以访问给它传递的参数,也能自己内部定义变量,当然还可以访问外部变量,不过lambda表达式访问外部变量有一个非常重要的限制:变量不可变(只是引用不可变,而不是真正的不可变)

lambda眼中的this

在lambda中,this不是指向lambda表达式产生的那个对象,而是声明它的外部对象。

lambda方法引用(Method reference)和构造器引用(construct reference)

方法引用:

方法引用可以在某些条件成立的情况下,更加简化lambda表达式的声明。方法引用语法格式有以下三种:

objectName::instanceMethod
ClassName::staticMethod
ClassName::instanceMethod


前两种方式类似,等同于把lambda表达式的参数直接当成instanceMethod|staticMethod的参数来调用。比如System.out::println等同于x->System.out.println(x);Math::max等同于(x, y)->Math.max(x,y)。

最后一种方式,等同于把lambda表达式的第一个参数当成instanceMethod的目标对象,其他剩余参数当成该方法的参数。比如String::toLowerCase等同于x->x.toLowerCase()。

构造器引用

构造器引用语法如下:ClassName::new,把lambda表达式的参数当成ClassName构造器的参数 。例如BigDecimal::new等同于x->new BigDecimal(x)。

java.util.stream.Stream 流

Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作;高级版本的 Stream,用户只要给出需要对其包含的元素执行什么操作,比如 “过滤掉长度大于 10 的字符串”、“获取每个字符串的首字母”等,Stream 会隐式地在内部进行遍历,做出相应的数据转换。

流的构成

当我们使用一个流的时候,通常包括三个基本步骤:

获取一个数据源(source)→ 数据转换(Intermediate 操作)→执行操作(terminal 操作)获取想要的结果
,每次转换原有 Stream 对象不改变,返回一个新的 Stream 对象(可以有多次转换),这就允许对其操作可以像链条一样排列,变成一个管道。

Intermediate操作:

一个流可以后面跟随零个或多个 intermediate 操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。

Terminal操作:

一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个 side effect。

转换操作都是 lazy 的,多个转换操作只会在 Terminal 操作的时候融合起来,一次循环完成。

生成 Stream Source

从 Collection 和数组

Collection.stream()
Collection.parallelStream()
Arrays.stream(T array) or Stream.of()


从 BufferedReader

java.io.BufferedReader.lines()


静态工厂

java.util.stream.IntStream.range()
java.nio.file.Files.walk()


自己构建

java.util.Spliterator


其它

Random.ints()
BitSet.stream()
Pattern.splitAsStream(java.lang.CharSequence)
JarFile.stream()


Reduce Stream

汇聚操作(也称为折叠)接受一个元素序列为输入,反复使用某个合并操作,把序列中的元素合并成一个汇总的结果。比如查找一个数字列表的总和或者最大值,或者把这些数字累积成一个List对象。Stream接口有一些通用的汇聚操作,比如reduce()和collect();也有一些特定用途的汇聚操作,比如sum(),max()和count()。注意:sum方法不是所有的Stream对象都有的,只有IntStream、LongStream和DoubleStream是实例才有。

下面会分两部分来介绍汇聚操作:

可变汇聚:把输入的元素们累积到一个可变的容器中,比如Collection或者StringBuilder;

其他汇聚:除去可变汇聚剩下的,一般都不是通过反复修改某个可变对象,而是通过把前一次的汇聚结果当成下一次的入参,反复如此。比如reduce,count,allMatch

可变汇聚

可变汇聚对应的只有一个方法:collect,正如其名字显示的,它可以把Stream中的要有元素收集到一个结果容器中(比如Collection)。先看一下最通用的collect方法的定义(还有其他override方法):

/**
*Supplier supplier是一个工厂函数,用来生成一个新的容器;
*BiConsumer accumulator也是一个函数,用来把Stream中的元素添加到结果容器中;
*BiConsumer combiner还是一个函数,用来把中间状态的多个结果容器合并成为一个(并发的时候会用到)
**/
<R> R collect(Supplier<R> supplier,BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);


List<Integer> nums = Lists.newArrayList(1,1,null,2,3,4,null,5,6,7,8,9,10);
List<Integer> numsWithoutNull = nums.stream().filter(
4000
num -> num != null).
collect(() -> new ArrayList<Integer>(),
(list, item) -> list.add(item),
(list1, list2) -> list1.addAll(list2));


上面这段代码就是对一个元素是Integer类型的List,先过滤掉全部的null,然后把剩下的元素收集到一个新的List中。进一步看一下collect方法的三个参数,都是lambda形式的函数。

第一个函数生成一个新的ArrayList实例;

第二个函数接受两个参数,第一个是前面生成的ArrayList对象,二个是stream中包含的元素,函数体就是把stream中的元素加入ArrayList对象中。第二个函数被反复调用直到原stream的元素被消费完毕;

第三个函数也是接受两个参数,这两个都是ArrayList类型的,函数体就是把第二个ArrayList全部加入到第一个中;

但是上面的collect方法调用也有点太复杂了,没关系!我们来看一下collect方法另外一个override的版本

<R, A> R collect(Collector<? super T, A, R> collector);


这样清爽多了!少年,还有好消息,Java8还给我们提供了Collector的工具类–Collectors,其中已经定义了一些静态工厂方法,比如:Collectors.toCollection()收集到Collection中,Collectors.toList()收集到List中和Collectors.toSet()收集到Set中。下面看看使用Collectors对于代码的简化:

List<Integer> numsWithoutNull = nums.stream().filter(num -> num != null).
collect(Collectors.toList());


其他汇聚

reduce方法:reduce方法非常的通用,后面介绍的count,sum等都可以使用其实现。

Stream API

/**

*从集合中过滤掉Predicate返回为false 的元素。

*接收一个Predicate函数接口类型

* @return the new stream

*/

Stream<T> filter(Predicate<? super T> predicate);


/**

* 把流对象中的每一个元素转换成另一个对象元素。

* @return the new stream

*/

<R> Stream<R> map(Function<? super T, ? extends R> mapper);


//打印persons集合中的每一个人的FirstName。
persons.stream()
.map(person -> person.getFirstName())
.forEach(e-> System.out.println(e));


/**

* 映射成Int 类型

* @return the new stream

*/

IntStream mapToInt(ToIntFunction<? super T> mapper);


/**

** 映射成Long 类型

* @return the new stream

*/

LongStream mapToLong(ToLongFunction<? super T> mapper);


/**

* 映射成Double类型

* @return the new stream

*/

DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);


/**

*把流对象中的每一个元素转换成新的流对象。

* @return the new stream

*/

<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);


//打印persons集合中的每一个人的FirstName 和 LastName。
//flatMap 把 每个人的FirstName 和 LastName 封闭成集合。
persons.stream()
.flatMap(person -> Arrays.asList(person.getFirstName(),person.getLastName())
.stream())
.forEach(a-> System.out.println(a));


/**

* @see #flatMap(Function)

*/

IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper);


/**

* @see #flatMap(Function)

*/

LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper);


/**

* @see #flatMap(Function)

*/

DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper);


/**

* 过滤掉重复数据

* @return the new stream

*/

Stream<T> distinct();


/**

* 给实现Comparable接口的对象排序 从低到高。

* 若对象类型没有实现 comparable 接口 见: Stream sorted(Comparator

persons.stream()
.sorted((p1,p2)->p1.getFirstName().length()-p2.getFirstName().length())
.forEach(e-> System.out.println(e.getFirstName()));


/**

* 对元素进行操作 但不转换元素类型。

* @return the new stream

*/

Stream<T> peek(Consumer<? super T> action);


persons.stream()
.peek(person -> person.setAge(10))
.forEach(e-> System.out.println(e));


/**

* 返回 Stream 的前面 maxSize 个元素;

* @return the new stream

* @throws IllegalArgumentException if {@code maxSize} is negative

*/

Stream<T> limit(long maxSize);


/**

* 扔掉前 n 个元素

* @return the new stream

* @throws IllegalArgumentException if {@code n} is negative

*/

Stream<T> skip(long n);


/**

* 循环操作集合每一个元素

*/

void forEach(Consumer<? super T> action);


/**

* @see #forEach(Consumer)

*/

void forEachOrdered(Consumer<? super T> action);


/**

*把stream 转换为object类型数组

* @return an array containing the elements of this stream

*/

Object[] toArray();


/**

*把stream 转换为A类型数组

*/

<A> A[] toArray(IntFunction<A[]> generator);


Person[] ps = persons.stream()
.limit(2)
.toArray(Person[]::new);
//或
Person[] ps1 = persons.stream()
.limit(2)
.toArray(index->new Person[index]);
//或
Person[] ps2 = persons.stream()
.limit(2)
.toArray(new IntFunction<Person[]>() {
@Override
public Person[] apply(int value) {
return new Person[value];
}
});
Arrays.asList(ps).forEach(e-> System.out.println(e));
Arrays.asList(ps1).forEach(e-> System.out.println(e));
Arrays.asList(ps2).forEach(e-> System.out.println(e));


/**

* 把 Stream 元素组合起来。它提供一个起始值(种子),

* 然后依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合。

* 从这个意义上说,字符串拼接、数值的 sum、min、max、average 都是特殊的 reduce。

* 例如 Stream 的 sum 就相当于

* Integer sum = integers.reduce(0, (a, b) -> a+b); 或

* Integer sum = integers.reduce(0, Integer::sum);

* @return the result of the reduction

*/

T reduce(T identity, BinaryOperator<T> accumulator);


/**

* 依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合。

* @return the result of the Optional

*/

Optional<T> reduce(BinaryOperator<T> accumulator);


Optional<String> sum = persons.stream().map(person -> person.getFirstName()).reduce((a,b)->a+","+b);
System.out.println(sum.get());


/**

*

* @return the result of the reduction

* @see #reduce(BinaryOperator)

* @see #reduce(Object, BinaryOperator)

*/

<U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator<U> combiner);


/**

*

* @return the result of the reduction

*/

<R> R collect(Supplier<R> supplier,BiConsumer<R, ? super T> accumulator,BiConsumer<R, R> combiner);


/**

*

* @return the result of the reduction

* @see #collect(Supplier, BiConsumer, BiConsumer)

* @see Collectors

*/

<R, A> R collect(Collector<? super T, A, R> collector);


/**

*

*/

Optional<T> min(Comparator<? super T> comparator);


/**

*

* @return an {@code Optional} describing the maximum element of this stream,

* or an empty {@code Optional} if the stream is empty

* @throws NullPointerException if the maximum element is null

*/

Optional<T> max(Comparator<? super T> comparator);


Optional<Integer> max = persons.stream().map(person -> person.getAge()).max((a,b)->a-b);


/**

*

* @return the count of elements in this stream

*/

long count();


/**

*Stream 中只要有一个元素符合传入的 predicate,返回 true

*/

boolean anyMatch(Predicate<? super T> predicate);


/**

* Stream 中全部元素符合传入的 predicate,返回 true ,stream 为empty 返回false

*/

boolean allMatch(Predicate<? super T> predicate);


/**

*Stream 中没有一个元素符合传入的 predicate,返回 true

*/

boolean noneMatch(Predicate<? super T> predicate);


/**

* 返回第一个元素

*/

Optional<T> findFirst();


/**

* 返回第一个元素

* @throws NullPointerException if the element selected is null

* @see #findFirst()

*/

Optional<T> findAny();


/**

* Returns a builder for a {@code Stream}.

*

* @param type of elements

* @return a stream builder

*/

public static<T> Builder<T> builder();


Stream.Builder<Person> builder = Stream.builder();
builder.add(new Person("Sindy", "Jonie", "uc", "female", 32, 2));
builder.add(new Person("Sindy1", "Jonie", "uc ", "female", 32, 2));
builder.add(new Person("Sindy1", "Jonie", "uc ", "female", 32, 2));
builder.build().forEach(person -> System.out.println(person.getFirstName()));


/**

* Returns an empty sequential {@code Stream}.

*

* @param the type of stream elements

* @return an empty sequential stream

*/

public static<T> Stream<T> empty();


/**

* Returns a sequential {@code Stream} containing a single element.

*

* @param t the single element

* @param the type of stream elements

* @return a singleton sequential stream

*/

public static<T> Stream<T> of(T t);


Stream<List<Person>> stream1 = Stream.of(persons);
stream1.flatMap(ps -> ps.stream()).forEach(psi -> System.out.println(psi));


/**

* Returns a sequential ordered stream whose elements are the specified values.

*

* @param the type of stream elements

* @param values the elements of the new stream

* @return the new stream

*/

public static<T> Stream<T> of(T... values);


Stream<List<Person>> stream1 = Stream.of(persons, persons);
stream1.flatMap(ps -> ps.stream()).forEach(psi -> System.out.println(psi));


/**

* iterate 跟 reduce 操作很像,接受一个种子值,和一个 UnaryOperator(例如 f)。

* 然后种子值成为 Stream 的第一个元素,f(seed) 为第二个,f(f(seed)) 第三个,以此类推。

* @return a new sequential {@code Stream}

*/

public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f);


Stream.iterate(0, n -> n + 3).limit(10). forEach(x -> System.out.print(x + " "));.
//输出结果:0 3 6 9 12 15 18 21 24 27


/**

*实现 Supplier 接口,控制流的生成。默认是串行(相对 parallel 而言)但无序的(相对 ordered 而言)。

*由于它是无限的,在管道中,必须利用 limit 之类的操作限制 Stream 大小。

* @param the type of stream elements

* @param s the {@code Supplier} of generated elements

* @return a new infinite sequential unordered {@code Stream}

*/

public static<T> Stream<T> generate(Supplier<T> s);


//样例1:
Supplier<Integer> random = ()->new Random().nextInt();//等同 new Random()::nextInt;
Stream.generate(random).limit(10).forEach(System.out::println);
//样例2:
IntStream.generate(() -> (int) (System.nanoTime() % 100)).limit(10).forEach(System.out::println);

//样例3:
Stream.generate(new PersonSupplier()).
limit(10).
forEach(p -> System.out.println(p.getName() + ", " + p.getAge()));

private class PersonSupplier implements Supplier<Person> {
private int index = 0;
private Random random = new Random();
@Override
public Person get() {
return new Person(index++, "StormTestUser" + index, random.nextInt(100));
}
}


/**

* 拼接两个流

*/

public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b);


参考资料:

Java 8 中的 Streams API 详解:http://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/

深入理解Java 8 Lambda(语言篇——lambda,方法引用,目标类型和默认方法):

http://www.cnblogs.com/figure9/archive/2014/10/24/4048421.html

Java8初体验(二)Stream语法详解: http://ifeve.com/stream/

Java 8 特性 – 终极手册:http://ifeve.com/java-8-features-tutorial/

Java8初体验(一)lambda表达式语法:http://ifeve.com/lambda/

Java 8新特性探究(一)通往lambda之路_语法篇:http://my.oschina.net/benhaile/blog/175012

IntStream api

//生一个从 start 到 end 的集合, 步长为1

public static IntStream range(int startInclusive, int endExclusive)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java lambda java8