Java8新特性
Lambda表达式
例子:
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("common");
}
};
r1.run();
====================================================
Runnable r2 = ()-> System.out.println("Lambda");
Comparator<Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
};
int compare1 = com1.compare(12,21);
System.out.println(compare1);
Comparator<Integer> com2 = (o1, o2) ->Integer.compare(o1,o2);
int compare2 = com2.compare(12,21);
System.out.println(compare2);
Comparator<Integer> com3 = Integer::compare;
int compare3 = com3.compare(12,21);
System.out.println(compare3);
举例:Runnable r2 = (o1, o2) -> Integer.compare(o1,o2);
接口名 引用名 = (参数列表) -> {语句块};
格式:
? ->:Lambda操作符(箭头操作符)
? ->:左边:lambda的形参列表(其实就是接口中的抽象中抽象方法的形参列表)
? ->:右边:lambda体(其实就是重写的抽象方法的方法体)
lambda表达式的使用(6种情况)
总结:
-
->左边:lambd形参列表的类型可以省略,如果只有一个参数可以省略括号; -
->右边:lambda体应该使用一对{}包裹;如果lambda体只有一条执行语句(可能是return语句),可以省略这一对{}和return关键字;
lambda表达式的本质:函数式接口的’'实例"
(如果一个接口中只声明了一个抽象方法,那么我们称为函数式接口[functional interface])
语法格式1:无参数,无返回值;
Runnable r2 = ()-> System.out.println("Lambda");
语法格式2:有一个参数,但是没有返回值;
public static void main(String[] args) {
Consumer <String> con1 = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
con1.accept("aha!");
===========================================================
Consumer <String> con2 = (String s) -> {
System.out.println(s);
};
con2.accept("Well done!");
}
这里如果只有一条语句的话,是可以省略掉{}的;
语法格式3:数据类型可以省略(前提是:编译器可以推断出数据类型,称为“类型推断”
如果前面已经明确泛型了,那么参数列表的String也是可以省略的;
Consumer <String> con2 = (s) -> System.out.println(s);
语法格式4:Lambda若只需要一个参数时,参数的小括号可以省略;
Consumer <String> con2 = s -> System.out.println(s);
语法格式5:Lambda需要两个或以上的参数,多条执行语句,并且可以有返回值;
Comparator<Integer> com2 = (o1, o2) -> {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
};
语法格式6:当Lambda体只有一条语句时,return与大括号若有,都可以省略;
Comparator<Integer> com2 = (o1, o2) -> o1.compareTo(o2);
函数式接口
Java内置四大核心函数式接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|
Consumer 消费型接口 | T | void | 对类型为T的对象应用操作,包含方法:void accept(T t) | Supplier 供给型接口 | 无 | T | 返回类型为T的对象,包含方法:T get() | Function<T,R> 函数型接口 | T | R | 对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t) | Predicate 断定型接口 | T | boolean | 确定类型为T的对象是否满足其约束,并返回boolean值。包含方法:boolean test(T t) |
以后可以选择合适的接口,写对应函数的lambda来解决;
public static void main(String[] args) {
Consumer<Integer> con = times -> System.out.println("?(′???`?)"+times+"times!");
test1(3000,con);
}
public static void test1(int times, Consumer<Integer> con){
con.accept(times);
}
上面这段还可以进一步继续简化:
public static void main(String[] args) {
test1(3000,times -> System.out.println("?(′???`?)"+times+"times!"));
}
public static void test1(int times, Consumer<Integer> con){
con.accept(times);
}
直接当做参数传入方法(因为本质就是接口实例化)
方法引用
-
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用! -
要求:实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致! -
格式:使用操作符“::”将类(或对象)与方法名分割开来; **类(或对象) :: 方法名**
-
三种主要使用情况:
-
情况一: 对象 :: 非静态方法 Consumer中的void accept(T t) PrintStream 中的void println(T t) 参数列表相同,返回类型相同,符合使用条件
public static void main(String[] args) { PrintStream ps = System.out; Consumer con = ps :: println; con.accept(“夜色哪里都是美…”); }
Supplier中的T get()
Integer中的String toString()
```java
public static void main(String[] args) {
Integer integer = new Integer(1);
Supplier<String> sup = () -> integer.toString();
System.out.println(sup.get());
}
- 情况二: 类 :: 静态方法
Comparator中的int compare(T t1, T t2) Integer中的int compare(T t1, T t2) 参数列表和返回类型两个条件都符合,可以使用; public static void main(String[] args) {
Comparator<Integer> com = Integer :: compare;
System.out.println(com.compare(12,21));
}
Function中的R apply(T t) Math中的Long round(Double d) public static void main(String[] args) {
Function<Double,Long> func = Math::round;
System.out.println(func.apply(10.0));
}
-
情况三:类 :: 实例方法 Comparator中的int compare(T t1, T t2) String 中的int t1.compareTo(t2) compare的第一个参数t1 正好 作为调用String实例方法的对象t1,那我们就可以使用了; public static void main(String[] args) {
Comparator<String> com = String::compareTo;
System.out.println(com.compare("a","b"));
}
Function中的R apply(T t); Person中的String getName(); apply的第一个参数t正好是调用getName()的对象,所以符合条件可以使用; public class test {
public static void main(String[] args) {
Function<Person,String> func = Person :: getName;
System.out.println(func.apply(new Person()));
}
}
class Person{
private String name = "Nobody";
public String getName() {
return name;
}
}
构造器引用
和方法引用类似,抽象方法返回的值就是类的对象
示例:
Supplier中的get()
Employee的空参构造器:Employee()
构造器没有参数,get方法也没有参数,所以可以把无参构造器塞给get();
public static void main(String[] args) {
Supplier<Person> supplier = () -> new Person();
System.out.println(supplier.get());
=================================
Supplier<Person> supplier = Person::new;
System.out.println(supplier.get());
}
注意这里 Peson::new ,表示调用的是默认的无参构造器;如果要调用其他有参构造器就不能使用Supplier了,得用其他有参数的接口方法;
比如:
Function中的R apply(T t)
Person的有参构造器Person(String s)
两个方法参数个数一致,类型一致(可通过泛型指定Function的类型),那么符合条件;
public static void main(String[] args) {
Function<String,Person> func = Person::new;
System.out.println(func.apply("Year!!!"));
}
...
public Person(String name) {
this.name = name;
}
不用显势声明调用哪个构造器,只要保证参数列表一致即可;
数组引用
可以把数组看作是一个特殊的类,则写法与构造器引用一致。
public static void main(String[] args) {
Function<Integer,String[]> func1 = length -> new String[length] ;
String[] arr1 = func1.apply(10);
System.out.println(arr1.length);
Function<Integer,String[]> func2 = String[] :: new;
String[] arr2 = func2.apply(20);
System.out.println(arr2.length);
}
Stream API
注意:只有进行了 终止操作,中间一系列的操作才会执行(有点像flush,所以中间的操作并不是即调即用的)
创建Stream:
方式1:通过集合
public static void main(String[] args) {
List<Person> businessmen = Person.getBusinessmen();
Stream<Person> stream = businessmen.stream();
Stream<Person> parallelStream = businessmen.parallelStream();
}
注意两种不同的流;
方式2:通过数组
public static void main(String[] args) {
int []arr = {1,2,3,4,5,6};
IntStream intStream = Arrays.stream(arr);
double []doubles = {1.0,2.0,3.0};
DoubleStream doubleStream = Arrays.stream(doubles);
Person xiaomi = new Person("小米");
Person apple = new Person("苹果");
Person[] people = new Person[]{xiaomi,apple};
Stream<Person> stream = Arrays.stream(people);
}
Arrays类的static Stream stream(T[] array):返回一个(类型正确匹配的)流
方式3:通过Stream的of()
public static void main(String[] args) {
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5);
Stream<? extends Number> stream = Stream.of(1.0, 2, 3);
Stream<Double> doubleStream = Stream.of(1.0, 2.0, 3.0);
}
注意一下这边2,3行,如果集合内类型不统一,那么会使用通配符;
方式4:创建无限流(用的较少,了解即可)
迭代:
public static void main(String[] args) {
Stream.iterate(0,t->t+2).limit(10).forEach(System.out::println);
}
生成:
public static void main(String[] args) {
Stream.generate(Math::random).limit(10).forEach(System.out::println);
}
limit和foreach是用来操作和终止的,不是创建流的;流的创建是iterate和generate;
中间操作
筛选与切片
这些操作对流本身不会有影响,流本身不变(有点像String的一系列api,并不会对String本身有改变)
public static void main(String[] args) {
List<Person> businessmen = Person.getBusinessmen();
"filter筛选流,从流中排除某些元素,接收lambda"
Stream<Person> stream1 = businessmen.stream();
stream1.filter(e ->e.getName().length()>2 ).forEach(System.out::println);
"limit截断流,使其元素不超过n(只处理前n个元素)"
Stream<Person> stream2 = businessmen.stream();
stream2.limit(3).forEach(System.out::println);
"skip跳过流,从第n+1个元素开始处理(跳过前n个元素,注意n为long)"
Stream<Person> stream3 = businessmen.stream();
stream3.skip(3L).forEach(System.out::println);
"distinct去重流,去掉相同的元素(根据hashCode和equals)"
Stream<Person> stream4 = businessmen.stream();
stream4.distinct().forEach(System.out::println);
}
注意:skip的数如果超过流中元素总数(太大了),那么会返回一个空流。
(每次终结操作之后,这个流就不能用了,所以我们要再重新开流)
映射
map(Function f)—接受一个函数作为参数,将元素转换成其他形式或提取信息,该函数被应用到每一个元素上,并将其映射成一个新的元素。
public static void main(String[] args) {
List<String> list = Arrays.asList("aa","bb","c");
list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);
}
使用map得到一个新的流(map里面写一个函数,返回一个新的流,这个流就是原始流经过函数映射后得到的新流)
public static void main(String[] args) {
List<Person> businessmen = Person.getBusinessmen();
Stream<String> stringStream = businessmen.stream().map(Person::getName);
stringStream.filter(name->name.length()>2).forEach(System.out::println);
}
flatMap(Function f)—接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有的流打散,连接成一个流。
先看看两者的不同:
public void test() {
List list1 = new ArrayList<>();
list1.add(1);
list1.add(2);
list1.add(3);
List list2 = new ArrayList<>();
list2.add(10);
list2.add(11);
list2.add(12);
list1.addAll(list2);
list1.add(list2);
}
先看看add和addAll的不同吧,
list1: [1,2,3]
list2: [10,11,12]
add后的结果:[1,2,3, [10,11,12] ](未拆散)——————>相当于map
addAll后的结果:[1,2,3,10,11,12](拆散)———————>相当于flatMap
map参数将 字符串转换成流
public static void main(String[] args) {
List<Person> businessmen = Person.getBusinessmen();
Stream<String> stringStream = businessmen.stream().map(Person::getName);
Stream<Stream<Character>> streamStream = stringStream.map(test::fromStringToStream);
streamStream.forEach(s -> s.forEach(System.out::println));
}
public static Stream<Character> fromStringToStream(String str) {
ArrayList<Character> list = new ArrayList<>();
for (Character c : str.toCharArray()) {
list.add(c);
}
return list.stream();
}
因为未打散,所以便利的时候要两个foreach;
flatMap(Function f)打散了,直接一层foreach就好了;
public static void main(String[] args) {
List<Person> businessmen = Person.getBusinessmen();
Stream<String> stringStream = businessmen.stream().map(Person::getName);
Stream<Character> streamStream = stringStream.flatMap(test::fromStringToStream);
streamStream.forEach(System.out::println);
}
public static Stream<Character> fromStringToStream(String str) {
ArrayList<Character> list = new ArrayList<>();
for (Character c : str.toCharArray()) {
list.add(c);
}
return list.stream();
}
排序
sorted()——自然排序
public static void main(String[] args) {
List<Integer> list = Arrays.asList(12,43,65,7,21,11,-69,8);
list.stream().sorted().forEach(System.out::println);
}
sorted(Comparator com)——定制排序
public static void main(String[] args) {
List<Integer> list = Arrays.asList(12,43,65,7,21,11,-69,8);
list.stream().sorted((o1, o2) -> o2.compareTo(o1)).forEach(System.out::println);
}
终止操作
匹配与查找
allMatch(Predicate p )——检查是否匹配所有元素;(括号内参数写boolean表达式,判断条件)
public static void main(String[] args) {
List<Person> businessmen = Person.getBusinessmen();
boolean allMatch = businessmen.stream().allMatch(person -> person.getName().length() > 0);
}
anyMatch(Predicate p)——检查是否至少有一个元素匹配条件
public static void main(String[] args) {
List<Person> businessmen = Person.getBusinessmen();
boolean anyMatch = businessmen.stream().anyMatch(person -> person.getName().length() < 0);
}
noneMatch(Predicate p)——检查是否没有匹配的元素
public static void main(String[] args) {
List<Person> businessmen = Person.getBusinessmen();
boolean noneMatch = businessmen.stream().noneMatch(person -> person.getName().equals("马云"));
}
有“马云”就false,没有就true;
findFirst()——返回第一个元素
public static void main(String[] args) {
List<Person> businessmen = Person.getBusinessmen();
Optional<Person> first = businessmen.stream().findFirst();
}
返回的类型是Optinal;
findAny()——返回流中的任意元素(取决于cpu)
public static void main(String[] args) {
List<Person> businessmen = Person.getBusinessmen();
Optional<Person> any = businessmen.stream().findAny();
}
返回的类型是Optinal;
count——返回流中元素的总个数
public static void main(String[] args) {
List<Person> businessmen = Person.getBusinessmen();
long count = businessmen.stream().count();
}
返回类型是long;
max(Comparator com)——返回流中最大值;
public static void main(String[] args) {
List<Person> businessmen = Person.getBusinessmen();
Optional<Person> max = businessmen.stream().max((o1, o2) -> o1.getName().compareTo(o2.getName()));
}
min(Comparator com)——返回流中最小值
public static void main(String[] args) {
List<Person> businessmen = Person.getBusinessmen();
Optional<Person> min = businessmen.stream().min((o1, o2) -> o1.getName().compareTo(o2.getName()));
}
foreach(Cosumer c)——内部迭代
public static void main(String[] args) {
List<Person> businessmen = Person.getBusinessmen();
businessmen.stream().forEach(System.out::println);
}
归约
reduce(T identity,BinaryOperator)——可以将流中元素反复结合起来,得到一个值。
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
Integer sum = list.stream().reduce(0, Integer::sum);
}
reduce(BinaryOperator)——可以将流中的元素反复结合起来,得到一个值。返回Optional
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
Optional<Integer> sum = list.stream().reduce(Integer::sum);
}
这两种reduce最大的区别就是在于参数和返回类型:如果没有identity,那么返回的是Optional;如果有identity,那么返回的是具体的类型;
收集
collect(Collector c)——将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
public static void main(String[] args) {
ArrayList<Person> businessmen = Person.getBusinessmen();
List<Person> collect = businessmen.stream().filter(person -> person.getName().length() > 2).collect(Collectors.toList());
}
重点是(Collectors.toList()),这个api要记住;
Optional
常用api
of和ofNullable:创建一个Optional实例,
public static void main(String[] args) {
Person person = new Person("Obama");
Optional<Person> optionalPerson = Optional.of(person);
person = null;
Optional<Person> personOptional = Optional.ofNullable(person);
}
Optional.of()不可以接收null指针,Optional.ofNullable()可以接收null;
Optional.empty()创建一个空的Optional实例;
public static void main(String[] args) {
Optional<Person> optionalPerson = Optional.empty();
}
orElse(T t)和ofNullable(T t)
public String getGirlName(Boy boy){
Optional<Boy> boyOptional = Optional.ofNullable(boy);
Boy boy1 = boyOptional.orElse(new Boy(new Girl("Monica")));
Girl girl = boy1.getGirl();
Optional<Girl> girlOptional = Optional.ofNullable(girl);
Girl girl1 = girlOptional.orElse(new Girl("Irene"));
return girl1.getName();
}
|