java8新增的新特性
1. 方法、lambda可作为值进行传递
2. 流(支持并行)
3. 接口能提供默认方法
lambda表达式
一、lambda表达式特点:
- 匿名——我们说匿名,是因为它不像普通的方法那样有一个明确的名称:写得少而想得多
- 函数——我们说它是函数,是因为Lambda函数不像方法那样属于某个特定的类。但和方法一样,Lambda有参数列表、函数主体、返回类型,还可能有可以抛出的异常列表
- 传递——Lambda表达式可以作为参数传递给方法或存储在变量中
- 简洁——无需像匿名类那样写很多模板代码
二、在哪里、如何使用lambda表达式
在哪里使用:
- 在函数式接口中使用lambda:
函数式接口就是只定义一个抽象方法的接口,如:
public interface Comparator<T> {
int compare(T o1, T o2);
}
public interface Runnable{
void run();
}
public interface Callable<V>{
V call();
}
- 如何在函数式接口中使用lambda表达式?
Runnable r = () -> System.out.println("new thread");
public static void process(Runnable r){
r.run();
}
process(r1);
java8在java.util.Function中提供了几种典型的函数式接口
Precidate:用于接收泛型且返回boolean的情况
@FunctionalInterface public interface Predicate<T> { boolean test(T t); }
Consumer:用于接收泛型且没有返回的情况
@FunctionalInterface public interface Consumer<T> { void accept(T t); }
Function:用于接收泛型T且返回泛型R的情况
@FunctionalInterface public interface Function<T, R> { R apply(T t); }
Supplier:用于不接收参数且返回泛型R的情况,同Callable
@FunctionalInterface public interface Supplier<T> { T get(); }
使用实例见:http://blog.youkuaiyun.com/kevindai007/article/details/78982692
tips:lamdba表达式中使用局部变量时,局部变量应显式申明为final,或只是用一次
//正确 String str = "sss"; Supplier<String> supplier = () -> str;
//编译报错 String str = "sss"; Supplier<String> supplier = () -> str; str = str.replace("a","b");
2.在方法引用中使用lambda
方法引用的可以理解为lamdba表达式的一种快捷写法//Function<String,Integer> valueOf = (s) -> Integer.valueOf(s); Function<String,Integer> valueOf = Integer::valueOf; //Function<String,Integer> length = (s) -> s.length(); Function<String,Integer> length = String :: length;
方法引用主要有四种
指向静态方法的方法引用
(args) ->ClassName.staticMethod(args) ClassName::staticMethod
指向任意类型实例方法的方法引用
(arg0,rest) -> arg0.instanceMethod(rest) ClassName.instanceMethod
指向现有对象的实例方法的方法引用
(args) -> instance.instanceMethod(args) instance::instanceMethod
构造方法引用
//Supplier<Apple> supply = () -> new Apply(); Supplier<Apple> supply = Apple::new; Apple a1 = supply.get(); //Function<Integer, Apple> c2 = (weight) -> new Apple(weight); Function<Integer, Apple> c2 = Apple::new; Apple a2 = c2.apply(110);
流
一、什么是流
流就是”从支持数据处理操作的源生成的元素序列”
下面一步步进行剖析:
- 元素序列——就像集合一样,流也提供了一个接口,可以访问特定元素类型的一组有序值
- 源——流会使用一个提供数据的源,如集合、数组或输入/输出资源
- 数据处理操作——流的数据处理功能支持类似于数据库的操作,以及函数式编程语言中的常用操作,如filter、map、reduce、find、match、sort等
二、流与集合
集合与流之间的差异就在于什么时候进行计算。
集合是一个内存中的数据结构,它包含数据结构中目前所有的值(集合中的每个元素都是放在内存里的)——集合中的每个元素都得先算出来才能添加到集合中.
流则是在概念上固定的数据结构(不能添加或删除元素),其元素则是按需计算的。
用一句话来概括:集合关注的是数据与数据存储本身;流关注的则是对数据的计算
最重要的:流只能消费一次
List<String> title = Arrays.asList("HELLO", "WORLD");
Stream<String> s = title.stream();
s.forEach(System.out::println);
s.forEach(System.out::println);
三、如何使用流
流的使用一般包括三件事:
- 个数据源(如集合)来执行一个查询;
- 个中间操作链,形成一条流的流水线;
- 个终端操作,执行流水线,并能生成结果
流的几种操作:
1、筛选
filter
Streams接口支持filter方法.该操作会接受一个谓词(一个返回boolean的函数)作为参数,并返回一个包括所有符合谓词的元素的流List<Dish> vegetarianMenu = menu.stream().filter(Dish::isVegetarian) .collect(toList());
distinct
流支持distinct的方法,会返回一个元素各异(根据流所生成元素的hashCode和equals方法实现)的流List<Integer> list1 = Arrays.asList(800,23434,2933,800,900,800); list1.stream().distinct().forEach(System.out :: println);
limit
流支持limit(n)方法,该方法会返回一个不超过给定长度的流。所需的长度作为参数传递 给limitList<Integer> list1 = Arrays.asList(800,23434,2933,800,900,800); list1.stream().limit(2).collect(toList());
skip
流支持skip(n)方法,返回一个去掉了前n个元素的流.如果流中元素不足n个,则返回一个空流List<Integer> list1 = Arrays.asList(800,23434,2933,800,900,800); list1.stream().skip(2).forEach(System.out :: println);
2、映射
map
流支持map方法,它会接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映 射成一个新的元素List<String> list2 = Arrays.asList("a","b","c","d","e","kevin","black","cat","dog","et"); List<Integer> collect = list2.stream().map(String::length).collect(toList());
flatmap
//查找数组中相同的字母 List<String> list2 = Arrays.asList("a","b","c","d","e","kevin","black","cat","dog","et"); list2.stream().map(s -> s.split("")) .flatMap(Arrays::stream) .distinct() .collect(Collectors.toList()).forEach(System.out::println);
3、查找和匹配
anyMatch
anyMatch方法用于检查流中是否有一个元素能匹配指定的条件.返回boolean,是终端操作List<String> list2 = Arrays.asList("a","b","c","d","e","kevin","black","cat","dog","et"); if(list2.stream().anyMatch(s -> "kevin".equals(s))){ System.out.println("kevin is in the team"); }
- allMatch
allMath方法用于检查流中是否所有元素都能匹配指定条件.返回boolean,是终端操作
List<String> list2 = Arrays.asList("a","b","c","d","e","kevin","black","cat","dog","et");
if(list2.stream().allMatch(s -> s.length() > 3)){
System.out.println("all items are more than 3 letters");
}else{
System.out.println("not all items are more than 3 letters");
}
noneMath
noneMath方法用于检查流中是否所有元素都不能匹配指定条件.返回boolean,是终端操作List<String> list2 = Arrays.asList("a","b","c","d","e","kevin","black","cat","dog","et"); if(list2.stream().noneMatch(s -> "naruto".equals(s))){ System.out.println("naruto is not in the team"); }
findAny
findAny方法将返回当前流中的任意元素.ps.findAny方法返回Optional对象,后面咱们会详细讲Optional对象
List<String> list2 = Arrays.asList("a","b","c","d","e","kevin","black","cat","dog","et"); System.out.println(list2.stream().findAny().get());
findFirst
findFirst方法返回当前流中的第一个元素.List<String> list2 = Arrays.asList("a","b","c","d","e","kevin","black","cat","dog","et"); System.out.println(list2.stream().findFirst().get());
可以看到findFirst方法好像与findAny方法十分类似,那区别在哪里呢?答案是并行.findFirst在并行上限制较多.如果不关心返回的元素是哪个,推荐使用findAny,它在使用并行流时限制较少
4、归约
reduce
reduce方法用来表达复杂的查询,此类查询需要将流中元素反复结合起来得到一个值.List<Integer> list3 = Arrays.asList(1,2,3,4,5,6,7); //初始值为0,累加整个list System.out.println(list3.stream().reduce(0,(a,b) -> a + b)); //System.out.println(list3.stream().count()); //初始值为1,累乘整个list System.out.println(list3.stream().reduce(1,(a,b) -> a * b));
reduce也可以不传入初始值,但会返回一个Optional对象
List<Integer> list3 = Arrays.asList(1,2,3,4,5,6,7); System.out.println(list3.stream().reduce((a,b) -> a + b).get());
5、数值流
Java 8引入了三个原始类型特化流:IntStream、DoubleStream和LongStream,分别将流中的元素特化为int、long和double
把其他流转化为数值流
List<String> list4 = Arrays.asList("1","2","3","4","5","6","7"); int sum = list4.stream().mapToInt(s -> new Integer(String.valueOf(s))).sum(); System.out.println(sum);
数值流转化会对象流
List<String> list4 = Arrays.asList("1","2","3","4","5","6","7"); IntStream intStream = list4.stream().mapToInt(s -> new Integer(String.valueOf(s))); Stream<Integer> boxed = intStream.boxed();
数值范围
//取[0,100]之间的偶数求和 int sum1 = IntStream.rangeClosed(1, 100).filter(i -> i % 2 == 0).sum(); System.out.println(sum1);
四、构建流
1、由值创建
```
Stream<String> my = Stream.of("my", "name", "is", "kevindai");
my.map(String::toUpperCase).forEach(System.out::print);
//创建空流
Stream<Object> empty = Stream.empty();
```
2、由数值创建流
```
int[] nums = {1,2,3,4,5,6};
System.out.println(Arrays.stream(nums).sum());
```
3、由文件生成流
```
Path path = Paths.get("/Users/daiwenkai/Downloads/ttt.html");
try(Stream<String> lines = Files.lines(path)) {
lines.limit(10).forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
```
4、由函数生成流
Stream API提供了两个静态方法来从函数生成流:Stream.iterate和Stream.generate.这两个操作可以创建无限流:不像从固定集合创建的流那样有固定大的流
一般来说,应该使用limit(n)来对这种流加以限制,避免产生无限多个值
iterate
//生成从0开始每次加2,取前10个数 Stream.iterate(0, n -> n + 2).limit(10).forEach(System.out::println); //生成斐波那契数列 Stream.iterate(new int[]{0,1}, n -> new int[]{n[1],n[0] + n[1]}).limit(20).forEach(u -> System.out.println(u[0] + "," + u[1]));
generate
Stream.generate(Math::random).limit(5).forEach(System.out::println); //生成斐波那契数列 IntSupplier intSupplier = new IntSupplier(){ private int preValue = 0; private int nextValue = 1; @Override public int getAsInt() { int oldValue = preValue; preValue = nextValue; nextValue = oldValue + preValue; return oldValue; } }; IntStream.generate(intSupplier).limit(10).forEach(System.out::println);
到这里JDK新特性的基本用法已经学习完毕,后面还会写一些介绍其他特性的文章.这篇文章前前后后也写了一个多月了(其实可能还不止),中间因为各种各样的原因中断了无数次,但好歹赶在年前写完了,哈哈哈,2018给自己这种拖拖拉拉的性格喊声加油!