Java8学习笔记

一、Lamda表达式

传统写法

  1. package cn.com;  
  2.   
  3. interface Message {// 这适宜个一个接口  
  4. public void print(String str) ;// 里面只有一个抽象方法  
  5. }  
  6. public class TestDemo {  
  7. public static void main(String[] args) {  
  8.     Message msg= new Message() {  
  9.     @Override  
  10.     public void print(String str) {  
  11.         System.out.println(str);  
  12.     }  
  13.     } ;// 匿名内部类定义完成了  
  14.         msg.print("Hello word");  
  15.     }  
  16. }  

使用Lamda后的写法

 Message msg = (x) -> System.out.println(x);这里只有一个参数(x)括号可以不要

Lamda表达式的用法:

.单行语句如上示例(params) -> 单行语句;

.单行语句如上示例(params) -> 表达式

.多行语句(a, b) -> {多条语句};

.单行语句带返回值 (a, b) -> a + b; 单行语句返回不用写return

二、接口可以扩充默认的方法实现和静态方法

扫描二维码关注公众号,回复: 703336 查看本文章

  1. package cn.com.java;  
  2. @FunctionalInterface  
  3. interface Message {  
  4.     public int print(int a, int b);  
  5.     default String getInfo(String str){  
  6.         return str;  
  7.     }  
  8.     static String fun(String str){  
  9.         return str;  
  10.     }  
  11. }  
  12.   
  13. public class TestDemo {  
  14.     public static void main(String[] args) {  
  15.         Message msg = (a, b) ->{  
  16.             int sum = a+b;  
  17.             return sum;  
  18.         };  
  19.         msg.print(12);  
  20.         msg.getInfo("Hello Word");//普通方法由对象调用  
  21.         Message.fun("Hello Word");//静态方法由类名称调用  
  22.     }  
  23. }  

san、方法引用

所谓的方法引用实际上指的是将一个特定类的方法功能映射过来,而后通过接口中的方法,利用lamda表达式实现方法体的定义,当然这种定义的形式一共分为四种

1、类之中构造方法的引用:类名称:: new

[java]  view plain  copy
  1. package cn.com.java;  
  2. class Book{  
  3.     private String name;  
  4.     private int price;  
  5.     public Book(String name,int price){  
  6.         this.name = name;  
  7.         this.price = price;  
  8.     }  
  9.     @Override  
  10.     public String toString() {  
  11.         // TODO Auto-generated method stub  
  12.         return "书名:"+this.name+";价格:"+this.price;  
  13.     }  
  14. }  
  15. @FunctionalInterface  
  16. interface Message<T extends Book> {  
  17.     public T print(String name,int price);// 如果想引用Book类之中的构造方法,需参数向Book中的保持一致  
  18. }  
  19.   
  20. public class TestDemo {  
  21.     public static void main(String[] args) {
  22.         // 实现为接口方法的参数传给Book类的构造函数创建一个book实例  
  23.         Message<Book> mes = Book::new;  //实际就是接口中那个独一无二的方法的实现,同时定义了接口的实现类
  24.         System.out.println(mes.print("Java开发"30));  
  25.     }  
  26. }  

2、类中静态方法的引用:类名称:: 静态方法名称

[java]  view   copy
  1. package cn.com.java;  
  2.   
  3. interface Message {  
  4.     public Integer print(String msg);  
  5.   
  6. }  
  7.   
  8. public class TestDemo {  
  9.     public static void main(String[] args) {  
  10.         // TODO Auto-generated method stub  
  11.         Message mes = Integer::parseInt;  
  12.         System.out.println(mes.print("123456"));  
  13.   
  14.     }  
  15. }  

3、类中普通方法的引用:实例化对象名称:: 普通方法
[java]  view plain  copy
  1. package cn.com.java;  
  2.   
  3. interface Message {  
  4.     public Integer print(String msg);  
  5.   
  6. }  
  7.   
  8. public class TestDemo {  
  9.     public static void main(String[] args) {  
  10.         // TODO Auto-generated method stub  
  11.         String a = "a";  
  12.         Message mes = a::compareTo;  
  13.         System.out.println(mes.print("b"));  
  14.   
  15.     }  
  16.   
  17. }  

4、特定类型的任意方法引用:类名称:: 方法名称。

在String类里面有一个不区分大小写判断内容大小的方法:public int compareToIgnoreCase(String str);

[java]  view plain  copy
  1. package cn.com.java;  
  2.   
  3. import java.util.Arrays;  
  4. import java.util.Comparator;  
  5.   
  6. public class TestDemo {  
  7.     public static void main(String[] args) {  
  8.         String[] data = new String[] { "test""hello""eclipse""java",  
  9.                 "Oracle" };
  10.         //Comparator<String> cmp = (a, b) -> b.compareToIgnoreCase(a);  
  11.         Comparator<String> cmp = String::compareToIgnoreCase;// 特定的类型的方法  
  12.         Arrays.sort(data, cmp);  
  13.         System.out.println(Arrays.toString(data));  
  14.     }  
  15.   
  16. }  

三、系统提供的函数式接口

从JDK 1.8开始为了方便用户开发专门提供了一个新的包:java.util.function,在这个包里面针对于用户有可能出现的函数式接口做了一个公共定义。

在java.util.function包之中最为核心的只有四个接口:

·功能型接口:Function;

·消费型接口:Consumer;

·供给型接口:Supplier;

·断言型接口:Predicate。

1、功能型接口 Function

@FunctionalInterface
public interface Function<T, R> {
publicR apply(T t) ;// 接收数据而后返回处理结果
}

范例:实现功能型接口的引用

·本次引用Integer类的parseInt()方法,这个方法要求接收String型数据,而后返回int型数据。

[java]  view plain  copy
  1. package cn.com.java;  
  2.   
  3. import java.util.function.Function;  
  4. public class TestDemo {  
  5.     public static void main(String[] args) {  
  6.         Function<String, Integer> fun= Integer::parseInt; // parseInt()方法为static型  
  7.         int num= fun.apply("100");  
  8.         System.out.println(num* 2);  
  9.         }  
  10.     }  

也就是说这种既能接收数据,又能返回结果的方法,都用Function接口定义

2、消费型接口 Consumer

@FunctionalInterface
public interface Consumer<T> {
publicvoidaccept(T t); // 只是接收数据,并没有返回值存在
}

范例:使用消费型接口,引用System.out.println()这个方法,只接收数据但是没有返回值

[java]  view plain  copy
  1. package cn.com.java;  
  2.   
  3. import java.util.function.Consumer;  
  4. public class TestDemo {  
  5.     public static void main(String[] args) {  
  6.         Consumer<String> con= System.out::println;
  7.         con.accept("Hello World !"); 
  8.     }  
  9. }  
3、供给型接口 Supplier

@FunctionalInterface
public interface Supplier<T> {
publicT get();
}
本接口的方法没有参数,但是却可以返回数据。

范例:设置供给型方法引用,本次引用System.currentTimeMillis();

[java]  view plain  copy
  1. package cn.com.java;  
  2.   
  3. import java.util.function.Supplier;  
  4.   
  5. public class TestDemo {  
  6.     public static void main(String[] args) {  
  7.         Supplier<Long> sup= System :: currentTimeMillis;  
  8.         System.out.println(sup.get());  
  9.     }  
  10. }  
4、·断言型接口 Predicate

@FunctionalInterface
public interfacePredicate<T> {
public boolean test(T t);
}

现在是一个判断操作,那么如果是判断操作,就使用正则验证。

范例:引用String类中的matches()方法

[java]  view plain  copy
  1. package cn.com.java;  
  2.   
  3. import java.util.function.Predicate;  
  4. public class TestDemo {  
  5.     public static void main(String[] args) {  
  6.         String str= "100";// String类对象  
  7.         Predicate<String> pre= str::matches;  
  8.         System.out.println(pre.test("\\d+"));  
  9.     }  
  10. }  

以上是四个核心接口,实际上这四个接口会了,那么整个java.util.function包之中的接口就明白怎么使了。

例如,随便找一个BiFunction接口,这个接口与Function相似的,此接口定义如下:

@FunctionalInterface
public interface BiFunction<T, U, R> {
publicR apply(T t, U u);
}

虽然与Function接口不同,但是这里面可以设置两个参数。
范例:利用BiFunction接口引用一个方法
·引用方法,String类的replaceAll()方法。

[java]  view plain  copy
  1. package cn.com.java;  
  2.   
  3. import java.util.function.BiFunction;  
  4. public class TestDemo {  
  5.     public static void main(String[] args) {  
  6.         String str= "hello";  
  7.         BiFunction<String, String, String> bf= str::replaceAll;  
  8.         System.out.println(bf.apply("l""_"));  
  9.     }  
  10. }  

整个包之中的接口的功能都是类似的,实际上四个会了,所有的也就都会了。

之所以系统会提供内建的函数式接口,那么就会在大量的系统类库之中使用它。
在Collection接口里面新定义了一个forEach()方法:default void forEach(Consumer<? super T> action)
此方法是一个default方法可以直接利用接口对象调用,同时这个方法里面接收有一个消费型接口。

范例:List遍历输出

[java]  view plain  copy
  1. package cn.com.java;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5. public class TestDemo {  
  6.     public static void main(String[] args) {  
  7.         List<String> pro= new ArrayList<String>();  
  8.         pro.add("java");  
  9.         pro.add("android");  
  10.         pro.add("pl/sql");  
  11.         pro.add("ios");  
  12.         pro.add("python");  
  13.         pro.add("node.js");  
  14.         // pro.forEach((s) -> System.out.println(s));// 如果只有一个参数,直接编写也可以,不用写()了  
  15.         pro.forEach(s-> System.out.println(s));  
  16.     }  
  17. }  

在以后学习系统类的时候会大量的使用到在java.util.function包之中定义的函数式接口,所以掌握这四个接口就是掌握了整个包的使用。

四、数据流

范例:操作Stream

[java]  view plain  copy
  1. import java.util.ArrayList;  
  2. import java.util.List;  
  3. import java.util.stream.Stream;  
  4.   
  5. public class TestDemo3 {  
  6.     public static void main(String[] args) {  
  7.         List<String> all= new ArrayList<String>();  
  8.         all.add("hello");  
  9.         all.add("word");  
  10.         all.add("test");  
  11.         Stream<String> stream= all.stream(); // 将集合变为了数据流的形式  
  12.         System.out.println(stream.count());// 取得数据流的长度}}  
  13.     }  
  14. }  
通过本程序应该清楚一点:Collection集合里面已经支持了Stream接口对象的取得。这一点可以为随后要讲解的集合的数据分析带来帮助。除了Collection集合可以为Stream接口实例化之外,那么实际上也可以利用Stream接口里面提供的方法完成:

·取得Stream集合:static <T> Stream<T> of(T... values);

·支持forEach输出:public void forEach(Consumer<? super T> action)。

[java]  view plain  copy
  1. import java.util.stream.Stream;  
  2.   
  3. public class TestDemo3 {  
  4.     public static void main(String[] args) {  
  5.         Stream<String> stream= Stream.of("yootk""mldn""mldnjava"); // 准备好数据  
  6.         stream.forEach(System.out::println); // 方法引用  
  7.     }  
  8. }  
现在可以利用Collection接口实例化Stream接口,也可以利用Stream接口里面的of()方法,利用static方法取得Stream接口对象,但是折腾半天,感觉它就是一个集合个数(Collection接口的size())取得以及集合的输出(Iterator)。

Java8之后最大的特征是支持了数据的分析操作,所以有了Stream接口对象最大的好处是在于可以进行集合的处理。在函数式接口里面有一个Predicate,这个接口可以负责断言操作。

范例:新的判断模式

[java]  view plain  copy
  1. public static void main(String[] args) {  
  2.         List<String> pros= new ArrayList<String>();  
  3.         pros.add("java");  
  4.         pros.add("android");  
  5.         pros.add("ios");  
  6.         pros.add("python");  
  7.         pros.add("node.js");  
  8.         filter(pros, (str) -> str.contains("a"));  
  9.     }  
  10.     public static void filter(List<String> temp, Predicate<String> pre) {  
  11.         temp.forEach((s) -> {if(pre.test(s)) {  
  12.             System.out.println("data = "+ s);  
  13.         }  
  14.         });  
  15.     }  

感觉上和输出没有任何的区别,但是在这个时候是属于Java8的输出模式。但是现在有人觉得,这种代码看起来很乱,相当于结合两个函数式接口。

范例:执行集合数据过滤

·集合过滤操作:public Stream<T> filter(Predicate<? super T> predicate);

[java]  view plain  copy
  1. import java.util.ArrayList;  
  2. import java.util.List;  
  3.   
  4. public class TestDemo3 {  
  5.     public static void main(String[] args) {  
  6.         List<String> pros= new ArrayList<String>();  
  7.         pros.add("java");  
  8.         pros.add("android");  
  9.         pros.add("ios");  
  10.         pros.add("python");  
  11.         pros.add("node.js");  
  12.         pros.stream().filter((x) -> x.contains("a")).forEach(System.out::println);  
  13.     }  
  14. }  

此时虽然简化了过滤的操作,但是感觉到,这种操作还是完全可以利用Iterator输出实现。还是觉得没用。

例:取得过滤后的子集合

在Stream接口里面有一个收集器:public <R,A> R collect(Collector<? super T,A,R> collector);

  1. import java.util.ArrayList;  
  2. import java.util.List;  
  3. import java.util.stream.Collectors;  
  4.   
  5. public class TestDemo3 {  
  6.     public static void main(String[] args) {  
  7.         List<String> pros= new ArrayList<String>();  
  8.         pros.add("java");  
  9.         pros.add("android");  
  10.         pros.add("ios");  
  11.         pros.add("python");  
  12.         pros.add("node.js");  
  13.         List<String> subList= pros.stream().filter((x) -> x.contains("a")).collect(Collectors.toList()); // 将满足条件的集合变为了一个新的集合  
  14.         subList.forEach(System.out::println);  
  15.     }  
  16. }  

除了可以进行数据的判断之外,那么也可以进行数据的处理,逐个进行处理。

范例:将包含的字符串数据进行小写变大写

·如果要想针对于每个数据处理:public <R> Stream<R> map(Function<? super T,? extends R> mapper);

[java]  view plain  copy
  1. import java.util.ArrayList;  
  2. import java.util.List;  
  3. import java.util.stream.Collectors;  
  4.   
  5. public class TestDemo3 {  
  6.     public static void main(String[] args) {  
  7.         List<String> pros= new ArrayList<String>();  
  8.         pros.add("java");  
  9.         pros.add("android");  
  10.         pros.add("android");  
  11.         pros.add("ios");  
  12.         pros.add("ios");  
  13.         pros.add("python");  
  14.         pros.add("python");  
  15.         pros.add("python");  
  16.         pros.add("node.js");  
  17.         List<String> subList= pros.stream().map((x) -> x.toUpperCase()).collect(Collectors.toList()); // 将满足条件的集合变为了一个新的集合  
  18.         subList.forEach(System.out::println);  
  19.     }  
  20. }  

也就是说这个时候可以发现,map()方法可以将每一条数据分别进行处理,而后里面会包含原始的内容。

范例:消除重复数据

·在Stream接口里面提供了重复数据清除的方法:public Stream<T> distinct()。

[java]  view plain  copy
  1. import java.util.ArrayList;  
  2. import java.util.List;  
  3. import java.util.stream.Collectors;  
  4.   
  5. public class TestDemo3 {  
  6.     public static void main(String[] args) {  
  7.         List<String> pros= new ArrayList<String>();  
  8.         pros.add("java");  
  9.         pros.add("android");  
  10.         pros.add("android");  
  11.         pros.add("ios");  
  12.         pros.add("ios");  
  13.         pros.add("python");  
  14.         pros.add("python");  
  15.         pros.add("python");  
  16.         pros.add("node.js");  
  17.         List<String> subList= pros.stream().map((x) -> x.toUpperCase()).distinct().collect(Collectors.toList()); // 将满足条件的集合变为了一个新的集合  
  18.         subList.forEach(System.out::println);  
  19.     }  
  20. }  

这个时候已经可以成功的消除掉了重复的数据内容。

在Stream接口里面也提供了一些数据的处理方法:

·判断集合中的全部数据:public boolean allMatch(Predicate<? super T> predicate);

·判断集合中的任意一个数据:public boolean anyMatch(Predicate<? super T> predicate);

·不匹配:public boolean noneMatch(Predicate<? super T> predicate)。

if(pros.stream().allMatch((s) -> s.contains("a"))) {
            System.out.println("集合中的全部内容都包含有字母a!");
        }
        if(pros.stream().anyMatch((s) -> s.contains("a"))) {
            System.out.println("集合中的全部内容都包含有字母a!");
        }
在JDK  1.8之前,如果匿名内部类要想访问方法中的参数,则参数前必须加上final关键字,但是从JDK  1.8开始提供的新特性,匿名内部类访问方法参数的时候可以不加上final关键字了。

以上的判断你判断的全部都是单个条件,如果现在有多个条件呢?如果是判断,则一定使用断言式函数接口:Predicate,在这个接口里面提供有一些连接的操作方法:

·与操作:default Predicate<T> and(Predicate<? super T> other);

·或操作:default Predicate<T> or(Predicate<? super T> other);

[java]  view plain  copy
  1. Predicate<String> condA= (str) -> str.contains("a");  
  2. Predicate<String> condB= (str) -> str.length() == 3;  
  3. pros.stream().filter(condA.or(condB)).forEach(System.out::println);  
实际上数据流本身支持两类处理方式,一类是并行处理,另一类串行处理(默认),可以利用如下的方法改变处理:

·设置为并行处理:public S parallel();

·设置为串行处理:public S sequential()。

范例:采用并行处理

[java]  view plain  copy
  1. pros.stream().filter(condA.or(condB)).parallel().forEach(System.out::println);  
幸运的是在Java之中,数据流的处理里面所有的并发操作完全不需要用户来关心,都可以自己处理。并且最有意思的是可以并行和串行互相切换。

通过以上的讲解,实际上就应该清楚一点:所有的操作像map()、filter()这样操作都属于中间操作,而像collectors()、forEach()一定都属于结尾操作。

在BaseStream接口里面一共定义了四个子接口,以IntStream为例。

范例:使用IntStream接口

·生成整型的数据流:static IntStream range(int startInclusive, int endExclusive)。

[java]  view plain  copy
  1. IntStream stream= IntStream.range(030);stream.forEach(System.out::println);  
Stream接口可以保存各种类型,而IntStream里面只能够保存int型数据。

从JDK 1.8开始一些类里面也增加了改变,例如:java.util.Random类,在这个类里面增加了新的方法:

·返回一个整型数据流:public IntStream ints()。

[java]  view plain  copy
  1. newRandom().ints().limit(10).forEach(System.out::println);  
随着大数据的发展,基本上Java的开发也向大数据靠拢,所有的概念也都是相通的。

map和reduce函数的使用

范例:定义一个购物车的类

[java]  view plain  copy
  1. import java.util.ArrayList;  
  2. import java.util.List;  
  3. class Car {  
  4.     private String pname;  
  5.     private Integer amount;  
  6.     private Double price;  
  7.     public Car() {  
  8.         super();  
  9.     }  
  10.     public Car(String pname, Integer amount, Double price) {  
  11.         super();  
  12.         this.pname= pname;  
  13.         this.amount= amount;  
  14.         this.price= price;  
  15.     }  
  16.     public String getPname() {  
  17.         return pname;  
  18.     }  
  19.     public void setPname(String pname) {  
  20.         this.pname= pname;  
  21.     }  
  22.     public Integer getAmount() {  
  23.         return amount;  
  24.     }  
  25.     public void setAmount(Integer amount) {  
  26.         this.amount= amount;  
  27.     }  
  28.     public Double getPrice() {  
  29.         return price;  
  30.     }  
  31.     public void setPrice(Double price) {  
  32.         this.price= price;  
  33.     }  
  34. }  
  35. public class TestDemo3 {  
  36.     public static void main(String[] args) {  
  37.         List<Car> all= new ArrayList<Car>();  
  38.         all.add(new Car("Java开发"20079.8));  
  39.         all.add(new Car("Java WEB开发"50069.8));  
  40.         all.add(new Car("Android开发"70089.8));  
  41.         all.add(new Car("Oracle开发"30088.8));  
  42.         all.add(new Car("MongoDB开发"61098.8));  
  43.         all.stream().map((myCar) -> {  
  44.             System.out.print("书名:"+ myCar.getPname() + ",购买总价:");  
  45.             return myCar.getAmount() * myCar.getPrice();  
  46.         }).forEachOrdered(System.out::println); // 统计每本书的各自花费  
  47.     }  
  48. }  
现在可以发现map()方法的功能就是针对于集合的每个数据进行了处理。

如果说使用map()方法实现了数据的重新组合,那么reduce()就是将集合中的所有的数据变为一个结果,例如:类似于SQL中的sum()、avg()、count()函数的功能。

·reduce()方法:public T reduce(T identity, BinaryOperator<T> accumulator)。

[java]  view plain  copy
  1. double result= all.stream().map((myCar) -> {  
  2.             // System.out.print("书名:" + myCar.getPname() + ",购买总价:");  
  3.             return myCar.getAmount() * myCar.getPrice();  
  4.         }).reduce((sum, carPrice) -> sum+ carPrice).get();  
  5.             System.out.println("购买总金额:"+ result);  

如果要进行统计,可能会包含:总和、最大值、最小值、平均值、数量。

Stream.of("AAA","BBB","CCC").parallel().forEach(s->System.out.println("Output:"+s));
//区别于forEach的是并行的时候有序
Stream.of("AAA","BBB","CCC").parallel().forEachOrdered(s->System.out.println("Output:"+s)); 

在Stream接口里面提供了相应的操作:

·处理double数据:public DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);

·处理int数据:public IntStream mapToInt(ToIntFunction<? super T> mapper);

·处理long数据:public LongStream mapToLong(ToLongFunction<? super T> mapper)。

在每个返回的接口里面都提供了如下的统计操作方法:

·double数据统计(DoubleStream):public Double SummaryStatistics summaryStatistics()·

int数据的统计(IntStream):public IntSummaryStatistics summaryStatistics()

·long数据的统计(LongStream):public LongSummaryStatistics summaryStatistics()

这些类里面都提供有一系列的getXxx()方法用于统计相关信息。

范例:进行reduce功能实现

[java]  view plain  copy
  1. DoubleSummaryStatistics result= all.stream().mapToDouble((myCar) -> {  
  2.             return myCar.getAmount() * myCar.getPrice();  
  3.         }).summaryStatistics();  
  4.         System.out.println("统计量:"+ result.getCount());  
  5.         System.out.println("最大值:"+ result.getMax());  
  6.         System.out.println("最小值:"+ result.getMin());  
  7.         System.out.println("总和:"+ result.getSum());  
  8.         System.out.println("平均值:"+ result.getAverage());  

猜你喜欢

转载自blog.csdn.net/wang252949/article/details/79800258