常用的几种新特性详解
Java 8 新特性的简介
- 速度更快
- 代码更少(增加了新的语法:Lambda表达式)强大的Stream API
- 便于并行
- 最大化减少空指针异常:Optional
- Nashorn引擎,允许在JVM上运行JS应用
1.Lambda表达式
Lambda表达式:特殊的匿名内部类,语法更简洁。
Lanbda表达式允许把函数作为一个方法的参数(函数作为方法参数传递),将代码像数据一样传递。
基本语法:
<函数式接口> <变量名> = (参数1,参数2...) ->{
//方法体
}
Lambda引入了新的操作符:->(箭头操作符),->将表达式分成两部分
- 左侧:(参数1,参数2…)表示参数列表。
- 右侧:{}内部是方法体
Lambda需要注意的事项:
- 形参列表的数据类型会自动推断
- 如果形参列表为空,只需保留()
- 如果形参只有1个,()可以省略,只需要参数的名称即可
- 如果执行语句只有一句,且无返回值,{}可以省略,若有返回值,则若想省去{},则必须同时省略return,且执行语句也保证只有一句
- Lambda不会生成一个单独的内部类文件
1.1.lambda代码说明
语法格式一:lambda 无参,无返回值
//如何使用lambda表达式
public class Demo1 {
public static void main(String[] args) {
//线程//匿名实现类对象---以前的写法
// Runnable runnable=new Runnable() {
// @Override
// public void run() {
// System.out.println("hello world!");
// }
// };
//lambda表达式写法
Runnable runnable1= ()->System.out.println("hello world!");
new Thread(runnable1).start();
//更简单的写法
new Thread(()->System.out.println("hello world!")).start();
}
}
语法格式二:lambda 有一个参,无返回值
@Test
public void test02(){
Consumer<String> con = new Consumer<String>() {
@Override
public void accept(String s){
System.out.println(s);
}
};
con.accept("hello world!");
System.out.println("*********************");
//Lambda表达式
Consumer<String> con1 = (String str)-> System.out.println(str);
con1.accept("hello world");
}
语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断”
@Test
public void test03(){
Consumer<String> con1 = (str) -> System.out.println(str);
con1.accept("hello world");
}
2.函数式接口
如果一个接口只有一个抽象方法,则该接口称之为函数式接口,函数式接口可以使用Lambda表达式,Lambda表达式会被匹配到这个抽象方法上。
@FunctionalInterface注解检测接口是否符合函数式接口
public class Demo1 {
public static void main(String[] args) {
USB usb = new USB() {
@Override
public void show() {
System.out.println("我是函数式接口");
}
};
USB usb1=()-> System.out.println("你好");
info(()-> System.out.println("你好嘿嘿嘿"));
}
public static void info(USB usb){
usb.show();
}
}
//函数式接口
interface USB{
public void show();
}
lambda新增了四个重要的函数式接口:
- 函数形接口
- 供给形接口
- 消费型接口
- 判断型接口
函数式接口说明
public class Demo2 {
public static void main(String[] args) {
// Consumer<Double> consumer=t-> System.out.println("吃饭消费掉:"+t);
// Consumer<Double> consumer=new Consumer<Double>() {
// @Override
// public void accept(Double aDouble) {
// System.out.println("你消费得金额:"+aDouble);
// }
// };
Consumer<Double> consumer1=t ->System.out.println("吃饭消费掉:"+t);
Consumer<Double> consumer2=t ->System.out.println("唱歌消费掉:"+t);
Consumer<Double> consumer3=t ->System.out.println("洗脚消费掉:"+t);
hobby(consumer1,1000);
hobby(consumer2,2000);
hobby(consumer3,4000);
Supplier<Integer> supplier=new Supplier<Integer>() {
@Override
public Integer get() {
return new Random().nextInt(10);
}
};
Supplier<Integer> supplier1=()->new Random().nextInt(10);
int[] arr = getArr(supplier1, 5);
System.out.println(Arrays.toString(arr));
System.out.println("~~~~~~~~~~~~~~~function~~~~~~~~~~~~~~~~~~~~~~~");
// Function<String,String> function1=new Function<String, String>() {
// @Override
// public String apply(String s) {
// return s.toUpperCase();
// }
// };
// Function<String,String> function=s->s.toUpperCase();
// System.out.println(toUpper(function, "hello"));
List<String> list=new ArrayList<>();
list.add("zhangsan");
list.add("lisi");
list.add("wangwu");
list.add("zhaoliu");
list.add("tianqi");
// Predicate<String> predicate=new Predicate<String>() {
// @Override
// public boolean test(String s) {
// return s.length()>5;
// }
// };
Predicate<String> predicate1=s->s.length()>5;
List<String> list1 = predicate(predicate1, list);
System.out.println(list1);
}
//断言型接口 返回true/false经常用于判断
public static List<String> predicate(Predicate<String> predicate,List<String> list){
List<String> newList=new ArrayList<>();
for(int i=0;i<list.size();i++){
if(predicate.test(list.get(i))){
newList.add(list.get(i));
}
}
return newList;
}
//函数型接口 有参数,且需要返回值
public static String toUpper(Function<String,String> function,String str){
return function.apply(str);
}
//消费性函数式接口 不需要返回值,有参数,经常用于迭代
public static void hobby(Consumer<Double> consumer,double money){
consumer.accept(money);
}
//供给型函数式接口 无参数,指定返回值类型,经常用于只注重过程的代码
public static int[] getArr(Supplier<Integer> supplier,int count){
int [] arr=new int[count];
for(int i=0;i<count;i++){
arr[i]=supplier.get();
}
return arr;
}
}
3.方法引用
方法引用式Lambda表达式的一种简写形式。如果Lambda表达式方法体中只是调用一个特定的已经存在的方法,则可以使用方法引用。
常见的方法引用:
(1)构造器引用
**格式:**类名::new
@Test
public void test04(){
//普通写法
Supplier<Emp> supplier=new Supplier<Emp>() {
@Override
public Emp get() {
return new Emp("刘德华");
}
};
//类名::new 方法引用
Supplier<Emp> supplier=Emp::new; //必须该类中存在无参构造函数。
System.out.println(supplier.get());
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Emp{
private String name;
}
(2)静态方法引用
**格式:**类名::静态方法名
//类::静态方法 int compare(T o1, T o2);
//Lambda表达式方法引用
Comparator<Integer> comparator=(o1,o2)->Integer.compare(o1,o2);
//静态方法引用Demo
Comparator<Integer> comparator=Integer::compare;
int compare = comparator.compare(18, 18);
System.out.println(compare);
(3)类的方法引用
**格式:**类名::实例方法名
//lambda匿名方法引用
Function<Emp,String> function=e->{
return e.getName();
};
//类名::实例方法 R apply(T t);
Function<Emp,String> function=Emp::getName;
System.out.println(function.apply(new Emp("刘德华")));
(4)实例对象的方法引用
**格式:**对象::实例方法名
//对象::实例方法
//这里System.out就是一个对象
Consumer<String> consumer2=System.out::println;
consumer2.accept("你是刘德华吗?");
4.Stream API
流(Stream)中保存对集合或数组数据的操作。和集合类似,但集合中保存的是数据。
特点:
- Stream自己不会存储元素。
- Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream。
- Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
步骤:
-
创建
创建一个流。
-
中间操作
在一个或多个步骤中,将初始化Stream转化到另一个Stream的中间操作。
-
终止操作
使用一个终止操作来产生一个结果。该操作会强制它之前的延迟操作立即执行。在这之后,该Stream就不能使用了。
要注意的是,对流的操作完成后需要进行关闭操作(或者用JAVA7的try-with-resources)。
获取Stream对象的方式
- 通过Collection对象的stream()或parallelStream()方法
- 通过Arrays类的stream()方法
- 通过Stream接口的of()、iterate()、generate()方法
- 通过IntStream、LongStream、DoubleStream接口中的of、range、rangeClosed方法
public class Demo1 {
public static void main(String[] args) {
//1.通过Collection得对象中stream方法或parallelStream
List<String> list=new ArrayList<>();
list.add("apple");
list.add("huawei");
list.add("xiaomi");
list.add("vivo");
Stream<String> stream = list.stream();//串行流
//遍历集合(T t)-{}; 方法引用
stream.forEach(e->{
System.out.println(e);
});
//方法引用
stream.forEach(System.out::println);
Stream<String> stringStream = list.parallelStream(); //并行流
//2.通过Arrays转化为流
int[] arr={2,34,5,6,7,2};
IntStream stream = Arrays.stream(arr);
stream.forEach(System.out::println);
//3.通过Stream中of,iterator,generator();
Stream<String> list = Stream.of("java01", "java02", "java03");
static <T> UnaryOperator<T> identity() {
return t -> t;
}
UnaryOperator<Integer> unaryOperator=new UnaryOperator<Integer>() {
@Override
public Integer apply(Integer x) {
return x+5;
}
};
UnaryOperator<Integer> unaryOperator=x->x+5;
Stream<Integer> list = Stream.iterate(0,unaryOperator);//0,5,10,15,20,25,30
list.limit(10).forEach(System.out::println);
Supplier<T> s
Supplier<Integer> s=new Supplier() {
@Override
public Integer get() {
return new Random().nextInt(50);
}
};
Supplier<Integer> s=()->new Random().nextInt(50);
Stream<Integer> stream = Stream.generate(s);
Stream<Integer> stream = Stream.generate(()->new Random().nextInt(50));
stream.limit(10).forEach(System.out::println);
//IntStream LongStream DoubleStream
IntStream stream = IntStream.rangeClosed(0, 90);
stream.forEach(System.out::println);
}
}
4.1.中间操作
中间操作.
1、filter、limit、skip、distinct、sorted
2、map
3、parallel
- filter:接收Lambda,从流中排除某些操作;
- limit:截断流,使其元素不超过给定对象
- skip(n):跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit(n)互补
- distinct:筛选,通过流所生成元素的hashCode()和equals()去除重复元素。
代码操作
public class test02 {
public static void main(String[] args) {
//personList.stream()是创建流,filter()属于中间操作,forEach、count()是终止操作。
List<Person> personList = new ArrayList<>();
personList.add(new Person("欧阳雪",18,"中国",'F'));
personList.add(new Person("Tom",24,"美国",'M'));
personList.add(new Person("Tom",24,"美国",'M'));
personList.add(new Person("Harley",22,"英国",'F'));
personList.add(new Person("向天笑",20,"中国",'M'));
personList.add(new Person("李康",22,"中国",'M'));
personList.add(new Person("小梅",20,"中国",'F'));
personList.add(new Person("何雪",21,"中国",'F'));
personList.add(new Person("李康",22,"中国",'M'));
//找出大于21岁的人
personList.stream().filter((person) -> person.getAge()>21).forEach(System.out::println);
System.out.println("----------------------");
//查询中国人有几个
long num = personList.stream().filter(p ->p.getCountry().equals("中国")).count();
System.out.println("中国人有:"+num+"个");
System.out.println("---------------------------");
//从Person列表中取出两个女性。
personList.stream().filter((p) -> p.getSex() == 'F').limit(2).forEach(System.out::println);
System.out.println("----------------------------");
//从Person列表中从第2个女性开始,取出所有的女性。
personList.stream().filter((p) -> p.getSex() == 'F').skip(1).forEach(System.out::println);
System.out.println("-------------------------------");
//去除掉了一个重复的数据
personList.stream().filter((p) -> p.getSex() == 'M').distinct().forEach(System.out::println);
}
测试结果
在这个例子中,personList.stream()是创建流,filter()、limit()、skip()、distinct()属于中间操作,forEach、count()是终止操作。
4.2.Stream中间操作–映射
- map–接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
- flatMap–接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
map举例
例:比如,我们用一个PersonCountry类来接收所有的国家信息:
System.out.println("**************************");
personList.stream().map((p) -> {
Person personName = new Person();
personName.setCountry(p.getCountry());
return personName;
}).distinct().forEach(System.out::println);
测试结果
4.3.Stream中间操作–排序
- sorted()–自然排序(Comparable)
- sorted(Comparator com)–定制排序(Comparator)
自然排序比较好理解,这里只讲一下定制排序,对前面的personList按年龄从小到大排序,年龄相同,则再按姓名排序:
final Stream<Person> sorted = personList.stream().sorted((p1, p2) -> {
if (p1.getAge().equals(p2.getAge())) {
return p1.getName().compareTo(p2.getName());
} else {
return p1.getAge().compareTo(p2.getAge());
}
});
sorted.forEach(System.out::println);
运行结果
4.4.终止操作
1、forEach、min、max、count
2、reduce、collect
- allMatch–检查是否匹配所有元素
- anyMatch–检查是否至少匹配一个元素
- noneMatch–检查是否没有匹配所有元素
- findFirst–返回第一个元素
- findAny–返回当前流中的任意元素
- count–返回流中元素的总个数
- max–返回流中最大值
- min–返回流中最小值
allMatch
判断personList中的人是否都是成年人:
final boolean adult = personList.stream().allMatch(p -> p.getAge() >= 18);
System.out.println("是否都是成年人:" + adult);
final boolean chinaese = personList.stream().allMatch(p -> p.getCountry().equals("中国"));
System.out.println("是否都是中国人:" + chinaese);
运行结果
max min
判断最大、最小的人信息
final Optional<Person> maxAge = personList.stream().max((p1, p2) -> p1.getAge().compareTo(p2.getAge()));
System.out.println("年龄最大的人信息:" + maxAge.get());
final Optional<Person> minAge = personList.stream().min((p1, p2) -> p1.getAge().compareTo(p2.getAge()));
System.out.println("年龄最小的人信息:" + minAge.get());
运行结果
求一个1到100的和
List<Integer> integerList = new ArrayList<>(100);
for(int i = 1;i <= 100;i++) {
integerList.add(i);
}
final Integer reduce = integerList.stream().reduce(0, (x, y) -> x + y);
System.out.println("结果为:" + reduce);
运行结果
求所有人的年龄之和
final Optional<Integer> reduce =
personList.stream().map(Person::getAge).reduce(Integer::sum);
System.out.println("年龄总和:" + reduce);
年龄总和:193
改写map举例中的的例子,将国家收集起来转换成List
final List<String> collect = personList.stream().map(p ->
p.getCountry()).distinct().collect(Collectors.toList());
System.out.println(collect);
输出结果:[中国, 美国, 英国]
计算出平均年龄
final Double collect1 =
personList.stream().collect(Collectors.averagingInt(p -> p.getAge()));
System.out.println("平均年龄为:" + collect1);
平均年龄为:21.444444444444443
注意流的关闭
try(final Stream<Integer> integerStream = personList.stream().map(Person::getAge)) {
final Optional<Integer> minAge = integerStream.collect(Collectors.minBy(Integer::compareTo));
System.out.println(minAge.get());
}