java8中为了支持函数式编程,新增了Lambda表达式。
函数式编程的三大特性
immutable data不可变数据
像Clojure一样,默认上变量是不可变的,如果你要改变变量,你需要把变量copy出去修改。这样一来,可以让你的程序少很多Bug。因为,程序中的状态不好维护,在并发的时候更不好维护。(你可以试想一下如果你的程序有个复杂的状态,当以后别人改你代码的时候,是很容易出bug的,在并行中这样的问题就更多了)
first class functions
这个技术可以让你的函数就像变量一样来使用。也就是说,你的函数可以像变量一样被创建,修改,并当成变量一样传递,返回或是在函数中嵌套函数。这个有点像Javascript的Prototype(参看Javascript的面向对象编程)
尾递归优化
我们知道递归的害处,那就是如果递归很深的话,stack受不了,并会导致性能大幅度下降。所以,我们使用尾递归优化技术——每次递归时都会重用stack,这样一来能够提升性能,当然,这需要语言或编译器的支持。Python就不支持。
map & reduce
这个技术不用多说了,函数式编程最常见的技术就是对一个集合做Map和Reduce操作。这比起过程式的语言来说,在代码上要更容易阅读。(传统过程式的语言需要使用for/while循环,然后在各种变量中把数据倒过来倒过去的)这个很像C++中的STL中的foreach,find_if,count_if之流的函数的玩法。
pipeline
这个技术的意思是,把函数实例成一个一个的action,然后,把一组action放到一个数组或是列表中,然后把数据传给这个action list,数据就像一个pipeline一样顺序地被各个函数所操作,最终得到我们想要的结果。
recursing 递归
递归最大的好处就简化代码,他可以把一个复杂的问题用很简单的代码描述出来。注意:递归的精髓是描述问题,而这正是函数式编程的精髓。
currying
把一个函数的多个参数分解成多个函数, 然后把函数多层封装起来,每层函数都返回一个函数去接收下一个参数这样,可以简化函数的多个参数。在C++中,这个很像STL中的bind_1st或是bind2nd。
higher order function 高阶函数
所谓高阶函数就是函数当参数,把传入的函数做一个封装,然后返回这个封装函数。现象上就是函数传进传出,就像面向对象对象满天飞一样。
parallelization并行
所谓并行的意思就是在并行环境下,各个线程之间不需要同步或互斥
lazy evaluation惰性求值
这个需要编译器的支持。表达式不在它被绑定到变量之后就立即求值,而是在该值被取用的时候求值,也就是说,语句如x:=expression; (把一个表达式的结果赋值给一个变量)明显的调用这个表达式被计算并把结果放置到 x 中,但是先不管实际在 x 中的是什么,直到通过后面的表达式中到 x 的引用而有了对它的值的需求的时候,而后面表达式自身的求值也可以被延迟,最终为了生成让外界看到的某个符号而计算这个快速增长的依赖树。
determinism 确定性
所谓确定性的意思就是像数学那样 f(x) = y ,这个函数无论在什么场景下,都会得到同样的结果,这个我们称之为函数的确定性。而不是像程序中的很多函数那样,同一个参数,却会在不同的场景下计算出不同的结果。所谓不同的场景的意思就是我们的函数会根据一些运行中的状态信息的不同而发生变化。
lambda表达式是一个匿名函数,我们可以把lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更加简洁、更加灵活的代码。作为一种更紧凑的代码风格,使java的语言表达能力得到了提升。
Lambda表达式常见形式
()->{
return 0;} 无输入参数,一个返回结果
()->{
System.out.print(0);} 无输入参数,无返回结果
()->{
return true;} 无输入参数,Boolean返回结果
(int i)->{
return 0;} 一个输入参数,一个返回结果
(int i)->{
System.out.print(0);} 一个输入参数,无返回结果
(int i)->{
return true;} 一个输入参数,Boolean返回结果
等等还有多个输入参数的情况,每一种lambda表达式的写法都对应特定的函数式接口,在java.util.function包中有大量的函数式接口提供使用,让程序员编写lambda表达式更加方便,如果遇到特殊的情况还需要自定义函数式接口进行lambda表达式的编写。
LambdaExample
package cn.wideth.util.other;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
public class LambdaExample {
public static void main(String[] args) {
//不使用lambda表达式
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hello,I am lambda");
}
}).start();
//使用lambda表达式
new Thread(() -> System.out.println("hello,I am lambda")).start();
//lambda的执行代码有多行
new Thread( () -> {
try {
TimeUnit.SECONDS.sleep(5);
System.out.println("I am also lambda");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
//有参数的lambda表达式
Consumer<Integer> consumer = (i) -> System.out.println(i);
consumer.accept(666);
}
}
package cn.wideth.util.other;
public class Main {
public static void main(String[] args) {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
};
runnable.run();
}
}
Runnable是实现多线程的一个常用方法。但是我们现在用到的不是他的这个特性。我们知道,接口是不能够被直接实例化的。所以我们如果想对一个接口进行实例化的时候,必须要给他一个实现类。那么我们转化后的代码可以看出,我们在 “ = ” 的后面实现实现了一个匿名实现类,用来实现Runnable接口的类。然后将这个实现类赋给了runnable对象。
package cn.wideth.util.other;
public class Main {
public static void main(String[] args) {
Runnable runnable = () -> System.out.println(Thread.currentThread().getName());
runnable.run();
}
}
让我们回到上面的代码。我们可以看出Lambda所做的事情,和转化后的代码所做的事是一样的。所以个人理解:Lambda表达式可以用来为接口简便的生成一个实现类。在Lambda表达式中我们可以看出方法体就是代表着Runnable接口中run方法的方法体。那么这个时候包括我自己刚刚学习的时候也会有一个疑问,凭什么它就代表run的方法体呢?如果有其他的方法呢?这个时候Lambda表达式的一个特性,或者说是要求完美的解决了这个问题:Lambda表达式只能用来实例化单抽象方法的接口 —> “函数式接口”。这样问题就解决了,我的接口里只有一个方法,那么当然,方法体就是写给他的啊。那么接下来我简单的说一下Lambda表达式的语法。
Lambda表达式一般形式如下:
()->{}
<函数式接口> <变量名> = (参数1,参数2…) -> {
//方法体
}
()->{}
():参数
->:固定箭头写法
{}:方法体
(参数1,参数2…)表示参数列表;->表示连接符;{}内部是方法体
1、=右边的类型会根据左边的函数式接口类型自动推断;
2、如果形参列表为空,只需保留();
3、如果形参只有1个,()可以省略,只需要参数的名称即可;
4、如果执行语句只有1句,且无返回值,{}可以省略,若有返回值,则若想省去{},则必须同时省略return,且执行语句也保证只有1句;
5、形参列表的数据类型会自动推断;
6、lambda不会生成一个单独的内部类文件;
7、lambda表达式若访问了局部变量,则局部变量必须是final的,若是局部变量没有加final关键字,系统会自动添加,此后在修改该局部变量,会报错;
如同上方的写法,我们知道run方法是无参的,所以括号里只需要置空即可。
Consumer consumer = (x) -> {
};
//只有一个参数,可以省略参数的括号
Consumer consumer1 = x -> {
};
//两个参数
BiConsumer biConsumer = (x, y) -> {
};
JDK1.8之后为了我们使用Lambda表达式方便,官方增加了许多这样的单方法接口。上方代码的两个类都是单方法接口,只不过一个是接单参,一个是接双参。但是这里有一点:如果方法仅有一个参数的时候,括号可以被省略。另外,如上图的x,y,参数类型是可以被省略的。如果方法体只有一行语句,并且此语句为返回值时,方法体的大括号可以被省略,例子如下:
Function function = (x) -> {
return "abc";
};
//只有一行一句,并且其为返回值,方法体大括号可省略
Function function1 = (x) -> "abc";
方法的引用大致分为如下几类:(这些类型的前一个代码均是未优化的,后一个代码均是优化后的)。
<函数式接口> <变量名> = <实例>::<实例方法名>
//调用
<变量名>.接口方法([实际参数…])
将调用方法时的传递的实际参数,全部传递给引用的方法,执行引用的方法;
语法
<函数式接口> <变量名> = <类>::<类方法名称>
//调用
<变量名>.接口方法([实际参数…])
package cn.wideth.util.other;
import java.util.function.Consumer;
public class Example {
public static void main(String[] args) {
//常规写法
Consumer<String> consumer = x -> {
Example.say(x);
};
consumer.accept("abc");
}
private static void say(String o) {
System.out.println(o);
}
}
package cn.wideth.util.other;
import java.util.function.Consumer;
public class Example {
public static void main(String[] args) {
//在上方代码中,我们的Lambda表达式的内容仅仅是用来调用Example的say静态方法
// 另外我们Lambda的参数正好是say所需要的参数
//那么我们就可以改成如下这样
Consumer<String> consumer = Example::say;
consumer.accept("abc");
}
private static void say(String o) {
System.out.println(o);
}
}
语法
//定义接口
interface <函数式接口>{
<返回值> <方法名>(<类><类名称>,[其他参数…]);
}
<函数式接口> <变量名> = <类>::<类实例方法名>
//调用
<变量名>.接口方法(类的实例,[实际参数…])
package cn.wideth.util.other;
import java.util.function.Consumer;
public class Example {
public static void main(String[] args) {
Example e = new Example();
//未经过转变的代码
Consumer<String> consumer = x -> {
e.say(x);
};
consumer.accept("abc");
}
private void say(String o) {
System.out.println(o);
}
}
package cn.wideth.util.other;
import java.util.function.Consumer;
public class Example {
public static void main(String[] args) {
Example e = new Example();
//我们仅仅调用了e实例的say方法,并且lambda接受的参数与方法相同
//可以转化为如下代码
Consumer<String> consumer = e::say;
consumer.accept("abc");
}
private void say(String o) {
System.out.println(o);
}
}
package cn.wideth.util.other;
import java.util.function.BiConsumer;
public class Example {
public static void main(String[] args) {
BiConsumer<Example, String> consumer = (Example e, String x) -> {
e.say(x);
};
consumer.accept(new Example(), "abc");
}
private void say(String o) {
System.out.println(o);
}
}
package cn.wideth.util.other;
import java.util.function.BiConsumer;
public class Example {
public static void main(String[] args) {
//当我们对Lambda表达式传递多个参数的时候
//我们如果想通过Lambda表达式第二个及以后的参数作为
//Lambda表达式第一个参数所持有的方法的参数的话,可以作出如下变化。
BiConsumer<Example, String> consumer = Example::say;
consumer.accept(new Example(), "abc");
}
private void say(String o) {
System.out.println(o);
}
}
package cn.wideth.util.other;
import java.util.function.Consumer;
public class Example {
private String content;
public static void main(String[] args) {
//未经过转化的代码
Consumer<String> consumer = (x) -> {
new Example(x);
};
}
Example(String content){
this.content = content;
}
}
package cn.wideth.util.other;
import java.util.function.Consumer;
public class Example {
private String content;
public static void main(String[] args) {
//如果这个方法仅仅是用来调用一个构造函数的话,并且lambda表达式的参数与构造方法相同
//则可以转化为如下代码
Consumer<String> consumer = Example::new;
}
Example(String content){
this.content = content;
}
}
从中我们可以看出,使用方法引用的最关键的地方就是调用的方法参数与我们Lambda表达式的参数相同。只有这样我们才可以将Lambda表达式与方法引用结合在一起,编写出更简洁的代码(当然也更难以理解)。
如果一个接口只有一个抽象方法,则该接口称之为函数式接口,因为 默认方法 不算抽象方法,所以你也可以给你的函数式接口添加默认方法。函数式接口可以使用Lambda表达式,lambda表达式会被匹配到这个抽象方法上。我们可以将lambda表达式当作任意只包含一个抽象方法的接口类型,确保你的接口一定达到这个要求,你只需要给你的接口添加 @FunctionalInterface注解,编译器如果发现你标注了这个注解的接口有多于一个抽象方法的时候会报错的。
示例代码
package cn.wideth.util.other;
@FunctionalInterface
interface Converter<F, T> {
T convert(F from);
}
package cn.wideth.util.other;
public class Example {
public static void main(String[] args) {
Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
Integer converted = converter.convert("123");
System.out.println(converted); // 123
}
}
在lambda表达式中访问外层作用域和老版本的匿名对象中的方式很相似。你可以直接访问标记了final的外层局部变量,或者实例的字段以及静态变量。
我们可以直接在lambda表达式中访问外层的局部变量,但是该局部变量必须是final的,即使没有加final关键字(也会字自动添加final),之后我们无论在哪(lambda表达式内部或外部)修改该变量,均报错。
lambda内部对于实例的字段以及静态变量是即可读又可写。该行为和匿名对象是一致的;
示例代码:
static int outerStaticNum;
int outerNum;
void testScopes() {
Converter<Integer, String> stringConverter1 = (from) -> {
outerNum = 23;
return String.valueOf(from);
};
Converter<Integer, String> stringConverter2 = (from) -> {
outerStaticNum = 72;
return String.valueOf(from);
};
}
Predicate 接口只有一个参数,返回boolean类型。该接口包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如:与,或,非):
package cn.wideth.util.other;
import java.util.Objects;
import java.util.function.Predicate;
public class Main {
public static void main(String[] args) {
Predicate<String> predicate = (s) -> s.length() > 0;
System.out.println(predicate.test("foo")); // true
System.out.println(predicate.negate().test("foo")); // false
Predicate<Boolean> nonNull = Objects::nonNull;
Predicate<Boolean> isNull = Objects::isNull;
Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNotEmpty = isEmpty.negate();
System.out.println(nonNull.test(null));
System.out.println(isNull.test(null));
System.out.println(isEmpty.test("sss"));
System.out.println(isNotEmpty.test(""));
}
}
运行结果
Function 接口有一个参数并且返回一个结果,并附带了一些可以和其他函数组合的默认方法(compose, andThen):
package cn.wideth.util.other;
import java.util.function.Function;
public class Main {
public static void main(String[] args) {
Function<String, Integer> toInteger = Integer::valueOf;
System.out.println(toInteger.apply("123").getClass());
Function<String, Object> toInteger2 = toInteger.andThen(String::valueOf);
System.out.println(toInteger2.apply("123").getClass());
}
}
运行结果
Supplier接口返回一个任意范型的值,和Function接口不同的是该接口没有任何参数。
package cn.wideth.util.other;
import cn.wideth.entity.other.Person;
import java.util.function.Supplier;
public class Main {
public static void main(String[] args) {
Supplier<Person> personSupplier = Person::new;
personSupplier.get(); // new Person
}
}
Consumer 接口表示执行在单个参数上的操作。接口只有一个参数,且无返回值
Supplier<LambdaClassTest> personSupplier = LambdaClassTest::new;
Consumer<LambdaClassTest> greeter = (lt) -> System.out.println("Hello, " + lt.getTest());
greeter.accept(personSupplier.get());
Comparator 是老Java中的经典接口, Java 8在此之上添加了多种默认方法:
Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName);
Person p1 = new Person("John", "Doe");
Person p2 = new Person("Alice", "Wonderland");
comparator.compare(p1, p2); // > 0
comparator.reversed().compare(p1, p2); // < 0
Optional 不是函数是接口,这是个用来防止NullPointerException异常的辅助类型,这是下一届中将要用到的重要概念,现在先简单的看看这个接口能干什么:Optional 被定义为一个简单的容器,其值可能是null或者不是null。在Java 8之前一般某个函数应该返回非空对象但是偶尔却可能返回了null,而在Java 8中,不推荐你返回null而是返回Optional。
Optional<String> optional = Optional.of("bam");
optional.isPresent(); // true
optional.get(); // "bam"
optional.orElse("fallback"); // "bam"
optional.ifPresent((s) -> System.out.println(s.charAt(0))); // "b"
创建stream通过of方法
Stream<Integer> integerStream = Stream.of(1, 2, 3, 5);
Stream<String> stringStream = Stream.of("taobao");
创建stream–通过generator方法
生成一个无限长度的Stream,其元素的生成是通过给定的Supplier(这个接口可以看成一个对象的工厂,每次调用返回一个给定类型的对象)
Stream.generate(new Supplier<Double>() {
@Override
public Double get() {
return Math.random();
}
});
Stream.generate(() -> Math.random());
Stream.generate(Math::random);
三条语句的作用都是一样的,只是使用了lambda表达式和方法引用的语法来简化代码。每条语句其实都是生成一个无限长度的Stream,其中值是随机的。这个无限长度Stream是懒加载,一般这种无限长度的Stream都会配合Stream的limit()方法来用。
创建stream–通过iterate方法
也是生成无限长度的Stream,和generator不同的是,其元素的生成是重复对给定的种子值(seed)调用用户指定函数来生成的。其中包含的元素可以认为是:seed,f(seed),f(f(seed))无限循环。Stream.iterate(1, item -> item + 1).limit(10).forEach(System.out::println);
这段代码就是先获取一个无限长度的正整数集合的Stream,然后取出前10个打印。千万记住使用limit方法,不然会无限打印下去。
通过Collection子类获取Stream
public interface Collection<E> extends Iterable<E> {
//其他方法省略
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
}
java.util.Stream 表示能应用在一组元素上一次执行的操作序列。Stream操作分为中间操作或者最终操作两种,最终操作返回一特定类型的计算结果,而中间操作返回Stream本身,这样你就可以将多个操作依次串起来。Stream 的创建需要指定一个数据源,比如 java.util.Collection的子类,List或者Set, Map不支持。Stream的操作可以串行执行或者并行执行。
Java 8扩展了集合类,可以通过 Collection.stream() 或者 Collection.parallelStream() 来创建一个Stream。Stream有串行和并行两种,串行Stream上的操作是在一个线程中依次完成,而并行Stream则是在多个线程上同时执行。
下面的例子展示了是如何通过并行Stream来提升性能:
首先我们创建一个没有重复元素的大表:
串行排序
package cn.wideth.util.other;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class Main {
public static void main(String[] args) {
//一百万
int max = 1000000;
List<String> values = new ArrayList<>(max);
for (int i = 0; i < max; i++) {
UUID uuid = UUID.randomUUID();
values.add(uuid.toString());
}
long t0 = System.nanoTime();
long count = values.stream().sorted().count();
System.out.println(count);
long t1 = System.nanoTime();
long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
System.out.println(String.format("sequential sort took: %d ms", millis));
}
}
程序结果
package cn.wideth.util.other;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class Main {
public static void main(String[] args) {
//一百万
int max = 1000000;
List<String> values = new ArrayList<>(max);
for (int i = 0; i < max; i++) {
UUID uuid = UUID.randomUUID();
values.add(uuid.toString());
}
long t0 = System.nanoTime();
long count = values.parallelStream().sorted().count();
System.out.println(count);
long t1 = System.nanoTime();
long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
System.out.println(String.format("sequential sort took: %d ms", millis));
}
}
程序结果
上面两个代码几乎是一样的,但是并行版的快了很多,唯一需要做的改动就是将stream()改为parallelStream();
stream的其他应用:
package cn.wideth.util.other;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> collection = new ArrayList<Integer>();
collection.add(14);
collection.add(5);
collection.add(43);
collection.add(89);
collection.add(64);
collection.add(112);
collection.add(55);
collection.add(55);
collection.add(58);
//list长度
System.out.println("总数 --> " + collection.parallelStream().count());
//求最大值,返回Option,通过Option.get()获取值
System.out.println("最大值 --> " + collection.parallelStream().max(Comparator.comparingInt(a -> a)).get());
//求最小值,返回Option,通过Option.get()获取值
System.out.println("最小值 --> " + collection.parallelStream().min(Comparator.comparingInt(a -> a)).get());
}
}
过滤通过一个predicate接口来过滤并只保留符合条件的元素,该操作属于中间操作。
package cn.wideth.util.other;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> collection = new ArrayList<>();
collection.add(14);
collection.add(5);
collection.add(43);
collection.add(89);
collection.add(64);
collection.add(112);
collection.add(55);
collection.add(55);
collection.add(58);
//值不等于null并且大于50,最下面的6个
Long count = collection.stream().filter(num -> num!=null).filter(num -> num.intValue()>50).count();
System.out.println(count);
}
}
去除重复
package cn.wideth.util.other;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> collection = new ArrayList<>();
collection.add(14);
collection.add(5);
collection.add(43);
collection.add(89);
collection.add(64);
collection.add(112);
collection.add(55);
collection.add(55);
collection.add(58);
collection.stream().distinct().forEach(System.out::println);;
}
}
排序是一个中间操作,返回的是排序好后的Stream。如果你不指定一个自定义的Comparator则会使用默认排序。
package cn.wideth.util.other;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> stringCollection = new ArrayList<>();
stringCollection.add("ddd2");
stringCollection.add("aaa2");
stringCollection.add("bbb1");
stringCollection.add("aaa1");
stringCollection.add("bbb3");
stringCollection.add("ccc");
stringCollection.add("bbb2");
stringCollection.add("ddd1");
stringCollection.stream().sorted().filter((s) -> s.startsWith("a"))
.forEach(System.out::println);
}
}
程序结果
需要注意的是,排序只创建了一个排列好后的Stream,而不会影响原有的数据源,排序之后原数据stringCollection是不会被修改的:
package cn.wideth.util.other;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> stringCollection = new ArrayList<>();
stringCollection.add("ddd2");
stringCollection.add("aaa2");
stringCollection.add("bbb1");
stringCollection.add("aaa1");
stringCollection.add("bbb3");
stringCollection.add("ccc");
stringCollection.add("bbb2");
stringCollection.add("ddd1");
stringCollection.stream().sorted().filter((s) -> s.startsWith("a"))
.forEach(System.out::println);
System.out.println(stringCollection);
}
}
程序结果
对于Stream中包含的元素使用给定的转换函数进行转换操作,新生成的Stream只包含转换生成的元素。这个方法有三个对于原始类型的变种方法,分别是:mapToInt,mapToLong和mapToDouble。这三个方法也比较好理解,比如mapToInt就是把原始Stream转换成一个新的Stream,这个新生成的Stream中的元素都是int类型。之所以会有这样三个变种方法,可以免除自动装箱/拆箱的额外消耗;
package cn.wideth.util.other;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> collection = new ArrayList<>();
collection.add("14");
collection.add("5");
collection.add("43");
collection.add("89");
collection.add("64");
collection.add("112");
collection.add("55");
collection.add("55");
collection.add("58");
//将String转化为Integer类型
collection.stream().mapToInt(Integer::valueOf).forEach(System.out::println);
//或
collection.stream().mapToInt(a->Integer.parseInt(a)).forEach(System.out::println);
}
}
对一个Stream进行截断操作,获取其前N个元素,如果原Stream中包含的元素个数小于N,那就获取其所有的元素;
返回一个丢弃原Stream的前N个元素后剩下元素组成的新Stream,如果原Stream中包含的元素个数小于N,那么返回空Stream;
Stream提供了多种匹配操作,允许检测指定的Predicate是否匹配整个Stream。所有的匹配操作都是最终操作,并返回一个boolean类型的值。
package cn.wideth.util.other;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> stringCollection = new ArrayList<>();
stringCollection.add("ddd2");
stringCollection.add("aaa2");
stringCollection.add("bbb1");
stringCollection.add("aaa1");
stringCollection.add("bbb3");
stringCollection.add("ccc");
stringCollection.add("bbb2");
stringCollection.add("ddd1");
boolean anyStartsWithA = stringCollection.stream().anyMatch((s) -> s.startsWith("a"));
System.out.println(anyStartsWithA); // true
boolean allStartsWithA = stringCollection.stream().allMatch((s) -> s.startsWith("a"));
System.out.println(allStartsWithA); // false
boolean noneStartsWithZ = stringCollection.stream().noneMatch((s) -> s.startsWith("z"));
System.out.println(noneStartsWithZ); // true
}
}
程序结果
计数是一个最终操作,返回Stream中元素的个数,返回值类型是long。
package cn.wideth.util.other;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> stringCollection = new ArrayList<>();
stringCollection.add("ddd2");
stringCollection.add("aaa2");
stringCollection.add("bbb1");
stringCollection.add("aaa1");
stringCollection.add("bbb3");
stringCollection.add("ccc");
stringCollection.add("bbb2");
stringCollection.add("ddd1");
long startsWithB = stringCollection.stream().filter((s) -> s.startsWith("b")).count();
System.out.println(startsWithB); // 3
}
}
程序结果
这是一个最终操作,允许通过指定的函数来讲stream中的多个元素规约为一个元素,规越后的结果是通过Optional接口表示的:
package cn.wideth.util.other;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class Main {
public static void main(String[] args) {
List<String> stringCollection = new ArrayList<>();
stringCollection.add("ddd2");
stringCollection.add("aaa2");
stringCollection.add("bbb1");
stringCollection.add("aaa1");
stringCollection.add("bbb3");
stringCollection.add("ccc");
stringCollection.add("bbb2");
stringCollection.add("ddd1");
Optional<String> reduced = stringCollection.stream().sorted().reduce((s1, s2) -> s1 + "#" + s2);
reduced.ifPresent(System.out::println);
// "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2"
}
}
程序结果
本文详细介绍了java8的Lambda表达式相关的知识。