包含java8函数编程案例的词条
今天给各位分享java8函数编程案例的知识,其中也会对进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!
本文目录一览:
用Java 8 增加的 Stream API 能实现哪些优雅的算法
Java 8引入了全新的Stream API。这里的Stream和I/O流不同,它更像具有Iterable的集合类,但行为和集合类又有所不同。
Stream API引入的目的在于弥补Java函数式编程的缺陷。对于很多支持函数式编程的语言,map()、reduce()基本上都内置到语言的标准库中了,不过,Java 8的Stream API总体来讲仍然是非常完善和强大,足以用很少的代码完成许多复杂的功能。
创建一个Stream有很多方法,最简单的方法是把一个Collection变成Stream。我们来看最基本的几个操作:
public static void main(String[] args) {
ListInteger numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
StreamInteger stream = numbers.stream();
stream.filter((x) - {
return x % 2 == 0;
}).map((x) - {
return x * x;
}).forEach(System.out::println);
}
集合类新增的stream()方法用于把一个集合变成Stream,然后,通过filter()、map()等实现Stream的变换。Stream还有一个forEach()来完成每个元素的迭代。
为什么不在集合类实现这些操作,而是定义了全新的Stream API?Oracle官方给出了几个重要原因:
一是集合类持有的所有元素都是存储在内存中的,非常巨大的集合类会占用大量的内存,而Stream的元素却是在访问的时候才被计算出来,这种“延迟计算”的特性有点类似Clojure的lazy-seq,占用内存很少。
二是集合类的迭代逻辑是调用者负责,通常是for循环,而Stream的迭代是隐含在对Stream的各种操作中,例如map()。
要理解“延迟计算”,不妨创建一个无穷大小的Stream。
如果要表示自然数集合,显然用集合类是不可能实现的,因为自然数有无穷多个。但是Stream可以做到。
自然数集合的规则非常简单,每个元素都是前一个元素的值+1,因此,自然数发生器用代码实现如下:
class NaturalSupplier implements SupplierLong {
long value = 0;
public Long get() {
this.value = this.value + 1;
return this.value;
}
}
反复调用get(),将得到一个无穷数列,利用这个Supplier,可以创建一个无穷的Stream:
public static void main(String[] args) {
StreamLong natural = Stream.generate(new NaturalSupplier());
natural.map((x) - {
return x * x;
}).limit(10).forEach(System.out::println);
}
对这个Stream做任何map()、filter()等操作都是完全可以的,这说明Stream API对Stream进行转换并生成一个新的Stream并非实时计算,而是做了延迟计算。
当然,对这个无穷的Stream不能直接调用forEach(),这样会无限打印下去。但是我们可以利用limit()变换,把这个无穷Stream变换为有限的Stream。
利用Stream API,可以设计更加简单的数据接口。例如,生成斐波那契数列,完全可以用一个无穷流表示(受限Java的long型大小,可以改为BigInteger):
class FibonacciSupplier implements SupplierLong {
long a = 0;
long b = 1;
@Override
public Long get() {
long x = a + b;
a = b;
b = x;
return a;
}
}
public class FibonacciStream {
public static void main(String[] args) {
StreamLong fibonacci = Stream.generate(new FibonacciSupplier());
fibonacci.limit(10).forEach(System.out::println);
}
}
如果想取得数列的前10项,用limit(10),如果想取得数列的第20~30项,用:
ListLong list = fibonacci.skip(20).limit(10).collect(Collectors.toList());
最后通过collect()方法把Stream变为List。该List存储的所有元素就已经是计算出的确定的元素了。
用Stream表示Fibonacci数列,其接口比任何其他接口定义都要来得简单灵活并且高效。
如何更好地使用Java 8的Optional
Java 8中的OptionalT是一个可以包含或不可以包含非空值的容器对象,在 Stream API中很多地方也都使用到了Optional。
java中非常讨厌的一点就是nullpoint,碰到空指针就会出错抛Exception,然后需要逐行检查是哪个对象为空,带来大量的不必要精力损耗,抛出NPE错误不是用户操作的错误,而是开发人员的错误,应该被避免,那么只能在每个方法中加入非空检查,阅读性和维护性都比较差。
如下面这个代码的手工非空检查:
public void addAddressToCustomer(Customer customer, Address newAddress){ if ( customer == null || newAddress == null) return; if ( customer.getAddresses() == null ){ customer.setAddresses ( new ArrayList()); } customer.addAddress(newAddress);}
另外还有一些开发人员喜欢通过非空检查来实现业务逻辑,空对象不应该用来决定系统的行为,它们是意外的Exceptional值,应当被看成是错误,而不是业务逻辑状态。
当我们一个方法返回List集合时,应该总是返回一个空的List,而不是Null,这就允许调用者能够遍历它而不必检查Null,否则就抛出NPE。
但是如果我们根据标识键ID查询数据库,没有查到,需要返回一个空对象怎么办?有人建议抛出Exception,其实这不符合函数方法一进一出的原则,变成一个函数方法有两个返回,一个是正常返回,一个出错Exception,函数式编程范式告诫我们不要轻易抛Exception。
这时Java 8的Optional就发挥作用了,允许我们返回一个空的对象。
OptionalT有方法 isPresent() 和 get() 是用来检查其包含的对象是否为空或不是,然后返回它,如:
OptionalSomeType someValue = someMethod();
if (someValue.isPresent()) { // check
someValue.get().someOtherMethod(); // retrieve and call
}
但是这种用法并不能体现Java 8的全部好处,你可以将Optional看成是需要使用某个T值的方法之间某种中间人或者协调者Mediator,而不只是一个普通对象的包装器。
如果你有一个值返回类型T,你有一个方法需要使用这个值,那么你可以让 OptionalT 处于中间,确保它们之间交互进行,而不必要人工干预。
这样,协调者OptionalT能够照顾T的值提供给你的方法作为输入参数,在这种情况下,如果T是空,可以确保不会出错,这样在T值为空时也可以让一切都正常运作,你也可以让OptionalT执行其他动作,如执行一段代码块等等,这样它就实际上是语言机制的很好的补充。
下面这个案例涉及到Lambda表达式 方法引用,是将单词流中第一个以"L"开始单词取出,作为返回结果是一个OptionalString。
使用ifPresent()
这个案例的代码如下:
Streamstring names = Stream.of("Lamurudu", "Okanbi", "Oduduwa");
Optionalstring longest = names
.filter(name - name.startsWith("L"))
.findFirst();
longest.ifPresent(name - {
String s = name.toUpperCase();
System.out.println("The longest name is "+ s);
});
这里ifPresent() 是将一个Lambda表达式作为输入,T值如果不为空将传入这个lambda。那么这个lambda将不为空的单词转为大写输出显示。在前面names单词流寻找结果中,有可能找不到开始字母为L的单词,返回为空,也可能找到不为空,这两种情况都传入lambda中,无需我们打开盒子自己编写代码来判断,它自动帮助我们完成了,无需人工干预。
使用map()
如果你想从OptionalT中返回一个值怎么办?使用 map(),如下:
Streamstring names = Stream.of("Lamurudu", "Okanbi", "Oduduwa");
Optionalstring longest = names
.filter(name - name.startsWith("L"))
.findFirst();
Optionalstring lNameInCaps = longest.map(String::toUpperCase);
使用OptionalT的map方法能够返回另外一个Optional,如上面的 LnameInCaps,因为传入map()的参数值也许会导致一个空值。
使用orElse()
如果在T可能空时你需要一个值的话,那么可以使用 orElse(),它能在T值存在的情况下返回这个值,否则返回输入值。
StreamString names = Stream.of("Lamurudu", "Okanbi", "Oduduwa");
OptionalString longest = names
.filter(name - name.startsWith("Q"))
.findFirst();
String alternate = longest.orElse("Nimrod");
System.out.println(alternate); //prints out "Nimrod"
使用orElseGet()
orElseGet() 方法类似于orElse(),但是不是直接返回输入参数,而是调用输入参数,返回调用的结果,这个输入参数通常是lambda:
StreamString names = Stream.of("Lamurudu", "Okanbi", "Oduduwa");
OptionalString longest = names
.filter(name - name.startsWith("Q"))
.findFirst();
String alternate = longest.orElseGet(() - {
// perform some interesting code operation
// then return the alternate value.
return "Nimrod";
});
System.out.println(alternate);
使用 orElseThrow()
orElseThrow()是在当遭遇Null时,决定抛出哪个Exception时使用:
StreamString names = Stream.of("Lamurudu", "Okanbi", "Oduduwa");
OptionalString longest = names
.filter(name - name.startsWith("Q"))
.findFirst();
longest.orElseThrow(NoSuchElementStartingWithQException::new);
总结,你能创建下面三种类型的OptionalT:
OptionalSomeType getSomeValue() {
// 返回一个空的Optional类型;
return Optional.empty();
}
OptionalSomeType getSomeValue() {
SomeType value = ...;
// 使用这个方法,值不可以为空,否则抛exception
return Optional.of(value);
}
OptionalSomeType getSomeValue() {
SomeType value = ...;
// 使用这个方法,值可以为空,如果为空返回Optional.empty
return Optional.ofNullable(value);
// usage
OptionalSomeType someType = getSomeValue();
Java8的函数式编程怎么样?
使用函数式代码的好处:
减少了可变量(Immutable Variable)的声明
能够更好的利用并行(Parallelism)
代码更加简洁和可读
函数式接口
函数式接口就是仅声明了一个方法的接口,比如我们熟悉的Runnable,Callable,Comparable等都可以作为函数式接口。当然,在Java 8中,新添加了一类函数式接口,如Function,Predicate,Consumer,Supplier等。
关于java8函数编程案例和的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。
发布于:2022-12-04,除非注明,否则均为
原创文章,转载请注明出处。