??本系列文章: ????Java基础(一)基本数据类型、变量类型、修饰符、表达式、数组、Math类、分支循环、关键字、调用方式、JVM/JDK与JRE ????Java基础(二)字符串、Java常用包、四种引用、内存泄漏、深克隆与浅克隆、语法糖、IO ????Java基础(三)面向对象、类和对象、封装继承多态、重写和重载、构造方法、内部类、包装类、对象实例化的四种方式 ????Java基础(四)异常、final/finally/finalize、可变参数、枚举、日期、反射、泛型 ????Java基础(五)Lambda表达式、Stream
一、Lambda表达式
1.1 Lambda语法
?Lambda表达式是 Java8 中最重要的新功能之一。使用 Lambda 表达式可以替代只有一个抽象函数的接口实现,可以替代匿名内部类 ,代码看起来更简洁易懂。Lambda表达式同时还提升了对集合的迭代、遍历、过滤数据的操作。 ?Lambda 表达式的语法:
(parameters) -> expression
或
(parameters) ->{ statements; }
?Lambda表达式的重要特征:
1、可选类型声明 :不需要声明参数类型,编译器可以统一识别参数值。 2、可选的参数圆括号 :一个参数无需定义圆括号,但多个参数需要定义圆括号。 3、可选的大括号 :如果主体包含了一个语句,就不需要使用大括号。 4、可选的返回关键字 :如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定表达式返回了一个数值。
?看一些简单的例子:
() -> 5
x -> 2 * x
(x, y) -> x – y
(int x, int y) -> x + y
(String s) -> System.out.print(s)
?Lambda表达式应用场景:任何有函数式接口 的地方。只有一个抽象方法(Object 类中的方法除外)的接口是函数式接口 。就像Runnable接口中,只有一个run方法。
1.2 Lambda使用
1.2.1 Runnable
?最常见的匿名内部类之一应该就是Runnable,在JDK1.8之前要这样写:
new Thread(new Runnable() {
public void run() {
System.out.println("The runable is using!");
}
}).start();
?在JDK1.8之后可以使用lambda表达式:
new Thread(() -> System.out.println("It's a lambda function")).start();
1.2.2 排序
?再看个例子,对集合元素进行排序:
List<String> list = Arrays.asList("java","javascript","scala","python");
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length()-o2.length();
}
});
for(String str:list){
System.out.println(str);
}
Collections.sort(list,(a,b)->a.length()-b.length());
1.2.3 遍历
?遍历集合中的元素也是 lambda表达式的常见场景之一,示例:
List<String> languages = Arrays.asList("Java","Python","C++");
for(String language:languages) {
System.out.println(language);
}
languages.forEach(x -> System.out.println(x));
1.2.4 过滤
?用 lambda表达式充当过滤条件,筛选出一部分元素,也是常见的操作。示例:
public class Student {
private String name;
private int age;
private int score;
public Student() {
}
public Student(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public int getScore() { return score; }
public void setScore(int score) { this.score = score; }
@Override
public String toString() {
return "student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
}
public interface StudentFilter {
boolean compare(Student student);
}
public class Test {
public static void main(String[] args) {
ArrayList<Student> list = new ArrayList<Student>();
list.add(new Student("zhangsan",14,67));
list.add(new Student("lisi",13,89));
list.add(new Student("wangwu",15,97));
list.add(new Student("maliu",12,63));
list.add(new Student("zhaoqi",17,75));
getByFilter(list,(e)->e.getAge()>14 );
getByFilter(list, (e)->e.getScore()>75);
System.out.println("-------------------");
getByFilter(list, (e)->e.getName().length()>5);
}
public static void getByFilter(ArrayList<Student> students, StudentFilter filter){
ArrayList<Student> list = new ArrayList<>();
for(Student student:students){
if(filter.compare(student)){
list.add(student);
}
}
printStudent(list);
}
public static void printStudent(ArrayList<Student> students){
for(Student student:students){
System.out.println(student);
}
}
}
1.3 Lambda表达式的多种写法
public class Java8Tester {
public static void main(String args[]){
Java8Tester tester = new Java8Tester();
MathOperation addition = (int a, int b) -> a + b;
MathOperation subtraction = (a, b) -> a - b;
MathOperation multiplication = (int a, int b) -> { return a * b; };
MathOperation division = (int a, int b) -> a / b;
System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
System.out.println("10 / 5 = " + tester.operate(10, 5, division));
GreetingService greetService1 = message ->
System.out.println("Hello " + message);
GreetingService greetService2 = (message) ->
System.out.println("Hello " + message);
greetService1.sayMessage("Runoob");
greetService2.sayMessage("Google");
}
interface MathOperation {
int operation(int a, int b);
}
interface GreetingService {
void sayMessage(String message);
}
private int operate(int a, int b, MathOperation mathOperation){
return mathOperation.operation(a, b);
}
}
1.4 一些代表输入输出的函数式接口
?Supplier:代表一个输出 ?Consumer: 代表一个输入 ?BiConsumer :代表两个输入 ?Function :代表一个输入,一个输出(一般输入和输出是不同类型的) ?UnaryOperator :代表一个输入,一个输出(输入和输出是相同类型的) ?BiFunction :代表两个输入,一个输出(一般输入和输出是不同类型的) ?BinaryOperator :代表两个输入,一个输出(输入和输出是相同类型的)
- 消费(Consumer)型接口:
public void test01(){
Consumer<Integer> consumer = (x) -> System.out.println("消费型接口" + x);
consumer.accept(100);
}
- 提供(Supplier)型接口
List<Integer> list = new ArrayList<>();
Supplier<Integer> supplier = () -> (int)(Math.random() * 10);
list.add(supplier.get());
System.out.println(supplier);
for (Integer integer : list) {
System.out.println(integer);
}
- 函数(Function)型接口:
String oldStr = "abc123456xyz";
Function<String, String> function = (s) -> s.substring(1, s.length()-1);
System.out.println(function.apply(oldStr));
1.5 Lambda表达式的方法引用
?方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法,方法引用提供了一种引用而不执行方法的方式,如果抽象方法的实现恰好可以使用调用另外一个方法来实现,就有可能可以使用方法引用。 ? lambda表达式方法引用的分类:
- 1、静态方法引用
?如果函数式接口的实现恰好可以通过调用一个静态方法来实现,那么就可以使用静态方法引用。 ?静态方法引用的lambda写法:
Comparator<Integer> com1 = (x, y) -> Integer.compare(x, y);
System.out.println(com1.compare(1, 2));
Comparator<Integer> com2 = Integer::compare;
System.out.println(com2.compare(2, 1));
- 2、实例方法引用
?如果函数式接口的实现恰好可以通过调用一个实例的实例方法来实现,那么就可以使用实例方法引用。 ?实例方法引用的lambda写法:
PrintStream ps = System.out;
Consumer<String> con1 = (s) -> ps.println(s);
con1.accept("aaa");
Consumer<String> con2 = ps::println;
con2.accept("bbb");
- 3、对象方法引用
?抽象方法的第一个参数类型刚好是实例方法的类型,抽象方法剩余的参数恰好可以当做实例方法的参数。如果函数式接口的实现能由上面说的实例方法调用来实现的话,那么就可以使用对象方法引用。 ? - 4、构造方法引用
?如果函数式接口的实现恰好可以通过调用一个类的构造方法来实现,那么就可以使用构造方法引用。 ?构造器方法引用:
Supplier<List> sup1 = () -> new ArrayList();
Supplier<List> sup2 = ArrayList::new;
???数组构造器方法引用:
interface test {
public String[] run(int length);
}
public class blog {
public static void main(String[] args) {
test t2 = String[]::new;
String[] arr = t2.run(5);
}
}
???多种构造引用的例子:
public class Test5 {
public static void main(String[] args) {
Supplier<Person> s1 = ()->new Person();
s1.get();
Supplier<Person> s2 = Person::new;
s2.get();
Supplier<List> s3 = ArrayList::new;
Supplier<Set> s4 = HashSet::new;
Supplier<Thread> s5 = Thread::new;
Supplier<String> s6 = String::new;
Consumer<Integer> c1 = (age)->new Account(age);
Consumer<Integer> c2 = Account::new;
c1.accept(123);
c2.accept(456);
Function<String,Account> f1 = (str)->new Account(str);
Function<String,Account> f2 = Account::new;
f1.apply("abc");
f2.apply("def");
}
}
class Account{
public Account(){
System.out.println("调用无参构造方法");
}
public Account(int age){
System.out.println("age 参数构造" +age);
}
public Account(String str){
System.out.println("str 参数构造" +str);
}
}
class Person{
public Person(){
System.out.println("调用无参的构造方法");
}
}
二、Stream
2.1 Stream简介
?Stream是一组用来处理数组、集合 的API。 ? Java 8引入函数式编程,原因有二:
- 代码简洁,函数式编程写出的代码简洁且意图明确,使用 stream 接口让你从此
告别 for 循环 。 多核友好 ,Java函数式编程使得编写并行程序从未如此简单,你需要的全部就是调用一下parallel()方法。
? Stream特性:
不是数据结构,没有内部存储 ;- 不支持索引访问;
- 惰性求值,
流在中间处理过程中,只是对操作进行了记录,并不会立即执行,需要等到执行终止操作的时候才会进行实际的计算 ; 支持并行 ;- 很容易生成数组或集合(List,Set);
- 支持过滤,查找,转换,汇总,聚合等操作;
不会修改原来的数据源,它会将操作后的数据保存到另外一个对象中 (保留意见:毕竟peek方法可以修改流中元素);
? Stream运行机制: ??1)Stream分为 源source,中间操作,终止操作 ; ??2)流的源可以是一个数组、一个集合、一个生成器方法,一个I/O通道等等; ??3)一个流可以有零个和或者多个中间操作,每一个中间操作都会返回一个新的流,供下一个操作使用。一个流只会有一个终止操作 ; ??4)Stream只有遇到终止操作,它的源才开始执行遍历操作 。
2.2 Stream的创建
??获取流有以下几种方式。
- 1、所有的 Collection 集合都可以通过 stream 默认方法获取流
??根据Collection获取流, java.util.Collection 接口中加入了default方法 stream 用来获取流,所以其所有实现类均可获取流。示例:
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
Set<String> set = new HashSet<>();
Stream<String> stream2 = set.stream();
Vector<String> vector = new Vector<>();
Stream<String> stream3 = vector.stream();
??Map 接口不是 Collection 的子接口,且其K-V数据结构不符合流元素的单一特征,所以获取对应的流需要分key、value或entry等情况。示例:
Map<String, String> map = new HashMap<>();
Stream<String> keyStream = map.keySet().stream();
Stream<String> valueStream = map.values().stream();
Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();
- 2、Stream 接口的静态方法 of 可以获取数组对应的流
??示例:
String[] array = { "张三", "李四", "王五", "赵六" };
Stream<String> stream = Stream.of(array);
Stream<String> stream1 = Stream.of(1, 2, 3, 4, 5);
2.3 Stream的中间操作
??Stream的中间操作有如下几种:
1.过滤 filter 2.去重 distinct 3.排序 sorted 4.截取 limit 5.跳跃 skip 6.转换map/flatMap 7.其他 peek
2.3.1 filter
??过滤一些元素,示例:
Arrays.asList(1, 2, 3, 4, 5)
.stream()
.filter(x->x%2==0)
.forEach(System.out::println);
??上述代码的功能是:获取1~5之间的偶数,并输出。结果:
2 4
2.3.2 distinct
??根据元素的 hashCode() 和 equals() 去除重复元素,示例:
Arrays.asList(1,2,3,4,5,6,7,7,7,7)
.stream()
.distinct()
.forEach(System.out::println);
??上述代码的功能是:去除List中的重复数,并输出。结果:
1 2 3 4 5 6 7
2.3.3 sorted
??对集合中的元素排序,顺序是倒序,示例:
Arrays.asList(1,2,3,4,5,6)
.stream()
.sorted((a,b)->b-a)
.forEach(System.out::println);
??结果:
6 5 4 3 2 1
2.3.4 limit
??截断流,使其元素不超过给定数量,示例:
Arrays.asList(1,2,3,4,5,6,7)
.stream()
.limit(1)
.forEach(System.out::println);
??上述代码的功能是:取集合中的第一条数据。
2.3.5 skip
??跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流,示例:
Arrays.asList(1,2,3,3,4,5)
.stream()
.skip(1)
.forEach(System.out::println);
??上述代码的功能是:跳过集合中的第一条数据。结果:
2 3 3 4 5
2.3.6 map
??接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。示例:
Arrays.asList("1","2","3","5")
.stream()
.map(x->Integer.valueOf(x))
.forEach(System.out::println);
??上述代码的功能是:String转Integer。结果:
1 2 3 5
2.3.7 peek
??类似于打印日志的功能在进行操作时查看当前值,示例:
Arrays.asList("1","2","3","5")
.stream()
.peek(System.out::println)
.forEach(System.out::println);
??上述代码的功能是:在遍历集合元素时,打印当前值。结果:
1 1 2 2 3 3 5 5
2.4 Stream的终止操作
??终止操作有如下几种:
1、循环 forEach 2、计算 min、max、count、 average 3、匹配 anyMatch、 allMatch、 noneMatch、findFirst、 findAny 4、汇聚 reduce 5、收集器 toArray collect
2.4.1 forEach
??循环,之前已有示例,不再赘述。
2.4.2 min
??最小值,示例:
Integer min = Arrays.asList(1, 3, 4, 5, 2)
.stream()
.min((a, b) -> a - b)
.get();
System.out.println(min);
2.4.3 max
??最大值,示例:
Integer max = Arrays.asList(1, 3, 4, 61, 1)
.stream()
.max((a, b) -> a - b)
.get();
System.out.println(max);
2.4.4 count
??统计集合中元素的数量,示例:
long count = Arrays.asList(1, 2,3, 4, 5, 6)
.stream()
.count();
System.out.print(count);
2.4.5 average
??求平均值,示例:
double avg = Arrays.asList(1, 3, 4, 61, 1)
.stream()
.mapToLong(x -> Long.valueOf(x + ""))
.average()
.getAsDouble();
System.out.print(avg);
2.4.6 findFirst
??返回第一个匹配到的元素,示例:
Optional<Integer> first= Arrays.asList(1, 2, 3, 4, 5)
.stream()
.filter(x -> x % 2 == 0)
.findFirst();
System.out.print(first.get());
2.4.7 reduce
??根据一定的规则将Stream中的元素进行计算后返回一个唯一的值,示例:
Integer reduce = Arrays.asList(1, 2, 3, 4, 5)
.stream()
.reduce((a, b) -> a + b)
.get();
System.out.print(reduce);
2.4.8 collect
??将Stream中的元素处理后,存储到固定的集合中。Collectors实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表: ??示例:
List<Integer> collect = Arrays.asList(1, 2, 3, 4, 5)
.stream()
.filter(x -> x % 2 == 0)
.collect(Collectors.toList());
System.out.print(collect.toString());
??上述代码的作用是:将过滤后的元素存储到List中。
|