Stream
一.方法引用
1.引入案例
在学习lambda表达式之后,我们通常使用lambda表达式来创建匿名方法。然而,有时候我们仅仅是调用了一个已存在的方法。 在Java8中,我们可以直接通过方法引用来简写lambda表达式中已经存在的方法。
这种特性就叫做方法引用(Method Reference)。
案例
- 遍历集合中的元素并输出
List<String> list = Arrays.asList("bb", "aa", "ee", "dd", "cc");
list.forEach(e-> System.out.println(e));
list.forEach(System.out::println);
- 对一个字符串数组进行排序
String [] strArr={"bb", "AA", "ee", "DD", "cc"};
Arrays.sort(strArr,(s1,s2)->s1.compareToIgnoreCase(s2));
Arrays.sort(strArr, String::compareToIgnoreCase);
System.out.println(Arrays.toString(strArr));
练习
1.定义一个Person类,如下:
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
//定义一个通过年龄比较大小的静态方法
public static int compareByAge(Person a, Person b) {
return a.age-b.age;
}
}
2.假设我们有一个Person数组,并且想对它进行排序,这时候,我们可能会这样写
@Test
public void test() {
Person[] pArr = new Person[]{
new Person("003", 18),
new Person("001", 19),
new Person("002", 26),
new Person("004", 22)};
// 使用匿名类
Arrays.sort(pArr, new Comparator<Person>() {
@Override
public int compare(Person a, Person b) {
return a.getAge()-b.getAge();
}
});
System.out.println(Arrays.asList(pArr));
}
3.对上面的排序进行改进
//使用lambda表达式
Arrays.sort(pArr, (Person a, Person b) -> {
return a.getAge()-b.getAge();
});
4.再次改进
//使用lambda表达式和类的静态方法
Arrays.sort(pArr, (a ,b) -> Person.compareByAge(a, b));
5.使用方法引用
//使用方法引用,引用的是类的静态方法
Arrays.sort(pArr, Person::compareByAge);
总结
简单地说,就是一个Lambda表达式。在Java 8中,我们会使用Lambda表达式创建匿名方法,但是有时候,我们的Lambda表达式可能仅仅调用一个已存在的方法,而不做任何其它事,对于这种情况,通过一个方法名字来引用这个已存在的方法会更加清晰,Java 8的方法引用允许我们这样做。方法引用是一个更加紧凑,易读的Lambda表达式,注意方法引用是一个Lambda表达式,其中方法引用的操作符是双冒号"::"。
2.经典情形
类型 | 示例 |
---|---|
引用静态方法 | 类名: : 静态方法名 |
引用某个对象的实例方法 | 对象::实例方法名 |
引用某个类型的任意对象的实例方法 | 类名:: 实例方法名 |
引用构造方法 | 类名: : new |
一.普通引用
- 对象::实例方法名
@Test
public void test1(){
//原来实现
PrintStream ps=System.out;
Consumer<String> con=(x)->ps.println(x);
con.accept("Java天下第一");
//使用方法引用
Consumer<String>con2=ps::println;
con2.accept("奥利给");
}
@Test
public void test2(){
//原来实现
Emp emp = new Emp("hello");
Supplier<String>sup=()->emp.getName();
System.out.println(sup.get());
//使用方法引用
Emp emp2 = new Emp("world");
Supplier<String>sup2=emp2::getName;
System.out.println(sup2.get());
}
2.类::静态方法名
@Test
public void test3(){
//原来的实现
Comparator<Integer> com=(x,y)->Integer.compare(x,y);
System.out.println(com.compare(100, 99));
//使用方法的引用
Comparator<Integer> com2=Integer::compareTo;
System.out.println(com2.compare(100, 99));
}
3.类::实例方法名
@Test
public void test4(){
//原来的实现
BiPredicate<String,String> bp=(x,y)->x.equals(y);
//使用方法的引用
BiPredicate<String,String> bp2=String::equals;
}
注意:
- Lambda体中调用方法的参数列表与返回值类型,要和函数式接口中抽象方法的参数列表和返回值类型保持一致!
- 若Lambda参数列表中的第一参数是 实例方法的调用者,而第二个参数是实例方法的参数时,可以使用 ClassName::method
二.构造方法引用
1.示例代码
@Test//无参构造
public void test5(){
//原来的实现
Supplier<Emp> sup=()->new Emp();
Emp emp = sup.get();
//构造器引用
Supplier<Emp> sup2=Emp::new;
Emp emp1 = sup2.get();
}
@Test//有参构造
public void test6(){
//原来的实现
Function<String,Emp> fun=(x)->new Emp(x);
Emp emp = fun.apply("好家伙");
//构造器引用
Function<String,Emp> fun2=Emp::new;
Emp emp2 = fun2.apply("牛牛牛");
}
注意:
需要调用的构造器参数列表要与函数式接口中抽象方法的参数列表保持一致!
三.数组引用
1.示例代码
@Test
public void test7(){
//原来的实现
Function<Integer,String[]> fun=(x)->new String[x];
String[] arrs = fun.apply(10);
//构造器引用
Function<Integer,String[]> fun2=String[]::new;
String[] arrs2 = fun2.apply(20);
}
二.Stream 的三个操作步骤
1.理解流
流到底是什么呢?
是数据渠道,用于操作数据源(集合,数组)所生成的元素序列。集合讲的是数据,流讲的是计算!
注意:
1.Stream自己不会存储数据
2.Stream不会改变源对象,相反,它们会返回一个持有结果的新的Stream
3.Stream操作是延迟执行的,这意味着它们会等到需要结果的时候才执行。
2.使用流
1.创建Stream
通过一个数据源(数组或集合),获取一个流。
@Test//创建Stream
public void test1(){
//1.通过Collection系列集合提供的stream()
ArrayList<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
//2.通过Arrays 中的静态方法stream() 获取数组流
IntStream stream2 = Arrays.stream(new int[5]);
DoubleStream stream2_1 = Arrays.stream(new double[5]);
Stream<String> stream2_2 = Arrays.stream(new String[5]);
//3.通过Stream类的静态方法of()
Stream<String> stream3 = Stream.of("aa", "bb", "cc");
//4.创建无限流
//迭代
Stream<Integer> stream4 = Stream.iterate(0, x -> x + 2);
stream4.limit(10).forEach(System.out::println);
//生成
Stream.generate(()->Math.random()).limit(6).forEach(System.out::println);
}
2.中间操作
一个中间操作链,对数据源的数据进行处理。
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,这个称为“惰性求值”。
3.终止操作
一个终止操作,执行中间操作链,并产生结果
package stream;
public class TestStream2 {
List<Emp> emps=Arrays.asList(
new Emp("曹操",18,99.99),
new Emp("刘备",56,555.55),
new Emp("张飞",34,888.88),
new Emp("孙权",25,666.66),
new Emp("吕布",12,111.11),
new Emp("吕布",12,111.11),
new Emp("吕布",12,111.11)
);
/*
筛选与切片:
filter 从流中排除某些元素
limit 使其元素不超过给定数量
skip 跳过元素,返回一个扔掉了前n个元素的流
distinct 通过流所生成元素的hashCode()和equals()去重
*/
@Test
public void test1(){
//中间操作
Stream<Emp> steam = emps.stream().filter((e) -> {
System.out.println("Steam 的中间操作 ");
return e.getAge() > 35;
});
//终止操作
steam.forEach(System.out::println);
}
@Test
public void test2(){
//求工资大于300的前2名
emps.stream().filter((e) -> e.getSalary() > 300)
.limit(2).forEach(System.out::println);
}
@Test
public void test3(){
//求工资大于300的员工-跳过前2名
emps.stream().filter((e) -> e.getSalary() > 300)
.skip(2).forEach(System.out::println);
}
@Test
public void test4(){
//员工信息去重
emps.stream().distinct().forEach(System.out::println);
}
/*
映射:
map-接收Lambda,将元素转换成其它形式或提取信息。接收一个函数作为参数,
该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap --接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
*/
@Test
public void test5(){
List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
list.stream()
.map(s->s.toUpperCase())
.forEach(System.out::println);
System.out.println("-------------------------");
emps.stream()
.map(Emp::getName)
.forEach(System.out::println);
}
@Test//排序
public void test2(){
//自然排序
List<String> list = Arrays.asList("cc", "ee", "dd", "ff", "aa", "bb");
list.stream().sorted().forEach(System.out::println);
//外部比较器排序
emps.stream().sorted((e1,e2)->{
if(e1.getAge()==e2.getAge()){
return e1.getName().compareTo(e2.getName());
}else {
return e1.getAge()-e2.getAge();
}
}).forEach(System.out::println);
}
/*
* 查找与匹配
* allMatch 检查是否匹配所有元素
* anyMatch 检查是否至少匹配一个元素
* noneMatch 检查是否没有匹配所有元素
* findFirst 返回第一个元素
* findAny 返回当前流中任意元素
* */
@Test
public void test3(){
boolean b = emps.stream().allMatch(e -> e.getStatus().equals(Emp.Status.BUSY));
System.out.println(b);
boolean b2 = emps.stream().anyMatch(e -> e.getStatus().equals(Emp.Status.BUSY));
System.out.println(b2);
boolean b3 = emps.stream().noneMatch(e -> e.getStatus().equals(Emp.Status.BUSY));
System.out.println(b3);
Optional<Emp> op = emps.stream().sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())).findFirst();
System.out.println(op.get());
Optional<Emp> op2 = emps.parallelStream().filter(e -> e.getStatus().equals(Emp.Status.BUSY)).findAny();
System.out.println(op2.get());
}
@Test//count和 max ,min
public void test4(){
System.out.println(emps.stream().count());
Optional<Emp> op = emps.stream().max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(op.get());
Optional<Double> op2 = emps.stream().map(Emp::getSalary).min(Double::compareTo);
System.out.println(op2.get());
}
}