前言
Jdk1.8提出了很多新特性,比如增加新的语言(Lambda表达式)、Stream API,Optional类。jdk8增加的新特性,使得程序效率会更快,提升程序员的编程体验。
Lambda表达式
Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码。使得写出更简洁、灵活的代码。
举例:(o1,o2)->Integer.compare(o1,o2)
,其中->
:Lambda操作符或箭头操作符;->左边
:Lambda形参列表(接口中抽象方法的形参列表);->右边
:Lambda体(重写接口抽象方法的方法体)。使用情况介绍,主要分为6种语法格式:
- 语法格式一:无参,无返回值
@Test
public void test(){
Runnable runnable=new Runnable() {
@Override
public void run() {
System.out.println("Runnable ...");
}
};
runnable.run();
System.out.println("*************Lambda************");
Runnable runnable1=()->{
System.out.println("Runnable ...");
};
runnable1.run();
}
- 语法格式二:有一个参数,无返回值
@Test
public void test2(){
Consumer<String>consumer=new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
consumer.accept("helo.xf");
System.out.println("*************Lambda************");
//由于泛型指明<String>,这里s不用指明类型,编译器会自动进行类型推断
Consumer<String>consumer1=(s)->{
System.out.println(s);
};
consumer1.accept("hello,xf");
}
- 语法格式三:需要有一个参数,参数的小括号可以省略
@Test
public void test3(){
Consumer<String>consumer=new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
consumer.accept("helo.xf");
System.out.println("*************Lambda************");
//参数s不用带小括号
Consumer<String>consumer1=s->{
System.out.println(s);
};
consumer1.accept("hello,xf");
}
- 语法格式四:两个或两个以上参数,方法体有多条执行语句,并有返回值
@Test
public void test4(){
Comparator<Integer>comparator=new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println("o1:"+o1);
System.out.println("o2:"+o2);
return o1.compareTo(o2);
}
};
int compare = comparator.compare(12, 20);
System.out.println(compare);
System.out.println("*************Lambda************");
Comparator<Integer>comparator1=(o1,o2)->{
System.out.println("o1:"+o1);
System.out.println("o2:"+o2);
return o1.compareTo(o2);
};
int compare1 = comparator1.compare(20, 34);
System.out.println(compare1);
}
- 语法格式五:Lambda体只有一条语句,return与大括号若有,都可以省略
@Test
public void test5(){
Comparator<Integer>comparator=new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
};
int compare = comparator.compare(12, 20);
System.out.println(compare);
System.out.println("*************Lambda************");
Comparator<Integer>comparator1=(o1,o2)-> o1.compareTo(o2);
int compare1 = comparator1.compare(20, 34);
System.out.println(compare1);
}
Lambda总结得到:->左边
:Lambda形参列表类型可以省略(类型推断),若形参只有一个,这对()也可以省略;->右边
:Lambda体使用一对{}包住,若体内只有一条语句(也包括return语句),这对{}和return关键字都可以省略,并且{}省略了return关键字也必须省略。其实Lambda本质也是一个借口的实例,看=号右边就能明白了。
函数式接口
FunctionalInterface:若一个接口中只声明一个抽象方法,则此接口称为函数式借口,可以在接口上加上@FunctionalInterface
注解,验证该接口为函数式接口。例如:
/**
* 函数式接口
*
*/
@FunctionalInterface
public interface Animal {
String eat(String food);
}
//Lambda表达式实现函数式接口
Animal catAnimal=(s)->{
System.out.println(s);
return s;
};
String s = catAnimal.eat("fish"); //fish
而且函数式接口的实现可以用Lambda表达式
四大函数式接口
在java.util.function包下,为我们提供了常用的四大函数式接口:Predicate<T>
、Supplier<T>
、Function<T, R>
、Consumer<T>
、
Predicate<T>
:
断定型接口,参数类型为T,根据传入的参数是否满足某些约束并返回boolean值,包含方法: boolean test(T t)
Predicate<Integer>predicate=(t->{
if (t>10) {
return true;
}else {
return false;
}
});
boolean test = predicate.test(1); //false
Supplier<T>
:
供给型接口,不接受任何参数,根据泛型T,返回T类型的对象,包含 T get()方法
Supplier<Student>supplier=(()->{
return new Student(1,"xf",24);
});
Student student = supplier.get();
Consumer<T>
:
消费型接口,参数类型为T,对传入的参数进行操作,无返回值,包含void accept(T t)方法
Consumer<String>consumer=((s)->{
System.out.println("s:"+s);
});
consumer.accept("xfnihao"); //xfnihao
Function<T, R>
:
函数型接口,对参数类型为T的对象操作后返回R类型结果,包含 R apply(T t)方法
Function<String, Boolean>function=((s)->{
if (s.equals("xf")){
return true;
}else {
return false;
}
});
Boolean result = function.apply("nihao"); //false
Stream API
Jdk8重大改变一个是Lambda表达式,另一个就是Stream API。Stream API实现函数式编程,使得程序代码更简洁、高效。常用用于对集合数据的计算,包括排序、查找、过滤等。
Stream的操作三个步骤:
1.创建Stream
一个数据源(如:集合、数组),获取一个流
2.中间操作
对数据源的数据进行处理
3.终止操作(终端操作)
一旦执行终止操作,就执行中间操作,并产生结果。
- 代码演示
/**
* 1.创建流
*/
@Test
public void test1() {
//顺序流
List<Student> listData = Student.getStudents();
Stream<Student> studentStream = listData.stream();
//并行流
Stream<Student> parallelStream = listData.parallelStream();
int[] array = new int[]{1, 2, 3, 4, 5};
//数据
IntStream stream = Arrays.stream(array);
}
@Test
/**
* 2.中间操作->筛选与切片:类似操作SQL,通过where或聚合函数来进行条件查询
*/
public void test2() {
//顺序流
List<Student> listData = Student.getStudents();
//过滤 filter
Stream<Student> stream = listData.stream();
stream.filter((s)->s.getId()>1).forEach(System.out::println);
//限流 limit
Stream<Student> stream1 = listData.stream();
stream1.limit(3).forEach(System.out::println);
//跳过 skip
Stream<Student> stream2 = listData.stream();
stream2.skip(3).forEach(student -> System.out.println(student));
//去重 distinct,要重写hashCode和equals方法
Stream<Student> stream3 = listData.stream();
stream3.distinct().forEach(student -> System.out.println(student));
}
/**
* 中间操作->映射:map(function f)接收一个函数作为参数,将元素转为其他形式或提取元素
*/
@Test
public void test3() {
List<Student> list = Student.getStudents();
//将学生姓名转为大写
list.stream().map(student -> student.getName().toUpperCase()).forEach(s -> System.out.println(s));
}
/**
* 中间操作->排序
*/
@Test
public void test4(){
//自然排序: Stream<T> sorted();
List<Integer> list = Arrays.asList(0, 12, -90, 23, 100, 56, 2);
list.stream().sorted().forEach(i -> System.out.println(i) );
//定制排序: Stream<T> sorted(Comparator<? super T> comparator);按学号从大到小排序
List<Student> listData = Student.getStudents();
listData.stream().sorted((s1,s2)->{
return -s1.getId().compareTo(s2.getId());
}).forEach(item-> System.out.println(item));
}
/**
* 3.终止操作->匹配与查找
*/
@Test
public void test5(){
List<Integer> list = Arrays.asList(0, 12, -90, 23, 100, 56, 2);
//findFirst:返回第一个元素
Integer i = list.stream().sorted().findFirst().get();
System.out.println(i);
//findAny:返回任意元素
Optional<Integer> any = list.parallelStream().findAny();
System.out.println(any);
//count:返回元素个数
long count = list.stream().count();
System.out.println(count);
//max:返回最大的元素
Optional<Integer> max = list.stream().max((s1, s2) -> {
return s1.compareTo(s2);
});
System.out.println(max);
}
/**
*
* 终止操作->规约:reduce,将流中的元素反复结合起来,得到新值并返回
*/
@Test
public void test6(){
List<Integer> list = Arrays.asList(0, 12, -90, 23, 100, 56, 2);
//reduce(T identity, BinaryOperator<T> accumulator);
//求数组总和
Integer sum = list.stream().reduce(0, (a, b) -> {
return Integer.sum(a, b);
});
System.out.println(sum);
}
/**
* 终止操作->收集:collect(Collectors ...)将处理后的流转为其他集合形式(Set/Map/List)
*/
@Test
public void test7(){
List<Student> students = Student.getStudents();
//返回有序List
List<Student> list = students.stream().filter(
student -> student.getId() > 1)
.collect(Collectors.toList());
//无序Set
Set<Student> set = students.stream().filter
(student -> student.getId() > 1).collect(Collectors.toSet());
}
练习
/**
* @author xfnihao
* 根据Stream流条件查询
*/
public class StreamDemo {
public static void main(String[] args) {
Student s1=new Student(1,"a",12);
Student s2=new Student(2,"b",8);
Student s3=new Student(3,"c",18);
Student s4=new Student(4,"d",24);
Student s5=new Student(5,"e",20);
Student s6=new Student(6,"f",20);
//id为偶数&age>18&名字大写&倒排序获取一个
//select name from tb where id %2==0 and age>18 func(name) order by ..
List<Student>list=Arrays.asList(s1,s2,s3,s4,s5,s6);
list.stream().filter((s -> {
return s.getId() % 2 == 0;
})).filter((s -> {
return s.getAge() > 18;
})).map((student -> {
return student.getName().toUpperCase();
})).sorted((o1, o2) -> {
return -o1.compareTo(o2);
}).limit(1).forEach(System.out::println);
}
}
这样,我们就学会并使用Stream API,简化了代码,使得编程更高效。要想更熟练这套API,需要大量的实践。
总结
Jdk8中的常用新特性主要是Lambda表达式和Stream API,目前只需掌握这两个就够了。简化代码的编写和提升集合操作的效率。