Stream和方法引用
一,Stream流
1.1. Stream流引入
Stream流完全不是I/O流,按照流水线处理方式来考虑代码中的思想。
JDK1.8 之后,我们拥有了Lambda表达式,让代码的中心偏向解决实际问题,直到重点,可以提高效率。
Stream流中使用了大量Lambda表达式,利用Lambda操作方式,提供开发效率
1.2 Stream流对应的思想
Stream流有一些特征:
1. 带有很多Stream流操作的方法, filter,limit,map,sorted,skip…这些方法大多是都会使用到函数式接口,有lambda表达式
2. 整个Stream流模型操作过程中,只有执行到count,foreach这些方法,操作真正的执行中的模型,如果不存在结果导向,中间的所有操作是无效的,这里得益于Lambda表达式的延后性
3. Stream流是存在一定的管道性 Pipelining 流水线
1.3 获取Stream流
java.util.stream.Stream<T> JDK1.8的新特征
1. 所有的Collection<T>集合都有对应的Stream();
2. 可以通过Stream类中的static Stream of()获取
static Stream<T> of(T... t);
static Stream<T> of(T t);
代码演示
import java.util.*;
import java.util.stream.Stream;
/**
* Stream流获取方式
* 1. Collection集合
* 2. Map双边队列
* 3. 数组
*
* @author Anonymous 2020/3/12 11:16
*/
public class Demo1 {
public static void main(String[] args) {
// List接口获取对应的Stream流类对象
List<String> list = new ArrayList<>();
// 根据list保存元素的类型来约束对应的Stream流对象操作所需类型
Stream<String> stream = list.stream();
// 通过Set集合获取对应Stream流对象
HashSet<String> set1 = new HashSet<>();
Stream<String> stream1 = set1.stream();
HashMap<String, String> map = new HashMap<>();
// Map双边队列中所有键对应的Set集合
Set<String> keySet = map.keySet();
Stream<String> stream2 = keySet.stream();
// Map双边队列中所有value对应的Collection集合
Collection<String> values = map.values();
Stream<String> stream3 = values.stream();
// map中有一个方法,可以获取所有键值对类型的Set集合
// Entry ==> 键值对,是Map接口的一个成员接口
Set<Map.Entry<String, String>> entrySet = map.entrySet();
// 获取Map键值对集合的Stream流对象
Stream<Map.Entry<String, String>> stream4 = entrySet.stream();
// 使用不定长参数获取对应的Stream流对象
// Stream类内的静态方法of,更多的是用于数组操作提供Stream流对象
Stream<String> stringStream = Stream.of("酱牛肉", "羊肉抓饭", "羊肉汤", "羊蝎子");
String[] arr = {"黄河大鲤鱼", "方中山胡辣汤", "萧记烩面", "蔡记蒸饺", "葛记焖饼"};
Stream<String> arrStream = Stream.of(arr);
// 不建议这样使用!!!
ArrayList<String> list1 = new ArrayList<>();
Stream<ArrayList<String>> list11 = Stream.of(list1);
}
}
1.4 Stream常用方法
延迟方法:
返回值类型依然是Stream接口本身,并没有影响我们操作真正的资源
允许链式操作,
例如
filter(XXX).limit(XXX).sorted(XXX).
终结方法:
返回值类型不是Stream接口本身,要么处理数据,要么返回其他类型数据,并且不再支持Stream流对象链式操作,count,foreach
1.4.1 foreach方法【终结方法】
void foreach(Consumer<? super T> action);
/*
终结方法:
需要一个Consumer接口进行操作处理,消耗一个数据
Consumer接口是一个【函数式接口】那就可以使用Lambda表达式
Consumer接口中方法是
void accept(T t);
*/
代码演示
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Stream;
/**
* Stream流foreach方法使用
*
* @author Anonymous 2020/3/12 11:39
*/
public class Demo1 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("蛋炒饭");
list.add("虎皮青椒");
list.add("手撕包菜");
list.add("鲱鱼罐头");
// 得到List对应的Stream流对象
Stream<String> stream = list.stream();
/*
Stream<String> Stream流,这里操作的是String类型
匿名内部类的匿名对象作为方法的参数,曾经多么厉害,现在是有点low
*/
stream.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
// 这里需要的参数是Consumer函数式接口,完成可以使用Lambda表达式完成
stream.forEach(string -> System.out.println(string));
// 我们在哪里见过??? 方法引用
stream.forEach(System.out::println);
}
}
1.4.2 filter方法
Stream<T> filter(Predicate<? super T> condition);
/*
filter是过滤方式,需要的参数是Predicate接口,Predicate是一个函数式接口,可以直接使用Lambda表达运行。
这里返回值类型是Stream类对象,是经过过滤之后的Stream类型,可以进行链式操作
Predicate接口中需要实现的方法
boolean test(T t);
*/
代码演示
package com.qfedu.c_streamfunction;
import java.util.ArrayList;
import java.util.stream.Stream;
/**
* Stream流对象filter方法演示
*
* @author Anonymous 2020/3/12 11:50
*/
public class Demo2 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("擀面皮");
list.add("肉夹馍");
list.add("冰峰");
list.add("水盆羊肉");
Stream<String> stringStream = list.stream().filter(s -> s.length() >= 3);
stringStream.forEach(System.out::println);
}
}
1.4.3 map方法
<R> Stream<R> map(Function<? super T, ? super R> fun);
/*
类型转换操作,得到的一个转换之后数据类型的Stream流对象
这里需要的参数是Functtion函数式接口,
R apply(T t);
T类型的数据转换成R类型数据
*/
代码演示
package com.qfedu.c_streamfunction;
import java.util.ArrayList;
import java.util.stream.Stream;
/**
* Stream流 map映射方法演示
*
* @author Anonymous 2020/3/12 14:39
*/
public class Demo3 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("1,骚磊,16");
list.add("2,骚杰,66");
list.add("3,老黑,46");
list.add("4,老付,36");
list.add("5,污云,56");
list.add("6,帅栋,26");
System.out.println(list);
Stream<Person> personStream = list.stream().map(s -> {
String[] split = s.split(",");
Person person = new Person();
person.setId(Integer.parseInt(split[0]));
person.setName(split[1]);
person.setAge(Integer.parseInt(split[2]));
return person;
});
personStream.forEach(System.out::println);
}
}
1.4.4 count方法【终结方法】
long count();
/*
返回当前Stream流对象中有多少个元素
类似有Collection接口下的size(). String的length();
【终结方法】
一旦执行Stream流对象被关闭
*/
代码演示
package com.qfedu.c_streamfunction;
import java.util.ArrayList;
/**
* Stream流对象 count方法演示【终结方法】
*
* @author Anonymous 2020/3/12 14:50
*/
public class Demo4 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("擀面皮");
list.add("肉夹馍");
list.add("冰峰");
list.add("水盆羊肉");
// 当前Stream流对象中有多少个元素,终结方法
long count = list.stream().filter(s -> s.length() >= 3).count();
System.out.println(count);
}
}
1.4.5 limit方法
Stream<T> limit(long maxSize);
/*
对于当前Stream流对象操作的数据进行限制操作,限制个数到maxSize
例如:
Stream流中保存的有10个元素,limit 5 ==> 前五个元素
*/
代码演示
import java.util.ArrayList;
import java.util.stream.Stream;
/**
* Stream流对象 limit方法
*
* @author Anonymous 2020/3/12 15:03
*/
public class Demo5 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("红旗");
list.add("领克");
list.add("吉利");
list.add("比亚迪");
list.add("长安");
list.add("五菱宏光");
Stream<String> stream = list.stream();
// @throws IllegalArgumentException if {@code maxSize} is negative
stream.limit(5).forEach(System.out::println);
}
}
1.4.6 skip方法
Stream<T> skip(long n);
/*
返回值依然是一个Stream流对象,这里跳过当前Stream流对象前n个元素
*/
代码演示
package com.qfedu.c_streamfunction;
import java.util.ArrayList;
/**
* Stream流对象 skip方法
*
* @author Anonymous 2020/3/12 15:10
*/
public class Demo6 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("陆巡");
list.add("高R");
list.add("RS7");
list.add("s4");
list.add("霸道");
list.add("道奇");
list.stream().skip(2).forEach(System.out::println);
}
}
1.4.7 concat方法
static Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
/*
拼接两个Stream流对象,是一个静态方法,得到新的Stream流对象
*/
代码演示
package com.qfedu.c_streamfunction;
import java.util.ArrayList;
import java.util.stream.Stream;
/**
* Stream流 静态成员方法 concat方法
*
* @author Anonymous 2020/3/12 15:15
*/
public class Demo7 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("红旗");
list.add("领克");
list.add("吉利");
list.add("比亚迪");
list.add("长安");
list.add("五菱宏光");
ArrayList<String> list1 = new ArrayList<>();
list1.add("陆巡");
list1.add("高R");
list1.add("RS7");
list1.add("s4");
list1.add("霸道");
list1.add("道奇");
// 获取到两个Stream流,其中保存的数据类型都是String类型
Stream<String> stream = list.stream();
Stream<String> stream1 = list1.stream();
Stream<String> concat = Stream.concat(stream, stream1);
concat.forEach(System.out::println);
}
}
二,方法引用
2.1 Lambda冗余问题以及方法引用初识
package com.qfedu.d_methodreference;
/**
* 函数式接口
*
* @author Anonymous
*/
@FunctionalInterface
interface PrintMethod {
void print(String str);
}
/**
* Lambda冗余问题
*
* @author Anonymous 2020/3/12 15:53
*/
public class Demo1 {
public static void main(String[] args) {
/*
使用Lambda表达式来展示字符串
IDEA有一个提示,这里可以继续优化
目标:
testPrint方法,需要将String str交给 PrintMethod进行处理
PrintMethod处理的方式是System.out.println
PrintMethod是一个函数式接口,可以使用Lambda表达式完成代码
但是貌似和这个接口,这里Lambda不够接近目标
明确:
1. 我要用System.out对象
2. 需要使用println方法
3. 需要处理的数据是明确的
*/
testPrint("郑州加油!!!", str -> System.out.println(str));
// 利用方法引用来处理当前代码
/*
调用对象:
System.out
执行方法:
println方法
需要处理的数据(可推导,可省略)
"中国加油!!!祖国万岁!!!"
::
Java中【方法引用】使用的运算符,标记
*/
testPrint("中国加油!!!祖国万岁!!!", System.out::println);
}
/**
* 该方法是一个使用函数式接口作为方法参数的一个方法
*
* @param str String类型的字符串
* @param pm PrintMethod函数式接口
*/
public static void testPrint(String str, PrintMethod pm) {
pm.print(str);
}
}
2.2 方法引用小要求
testPrint("郑州加油!!!", str -> System.out.println(str));
testPrint("郑州加油!!!", System.out::println);
1. 明确对象
对象 ==> 调用者
类对象,类名,super,this,构造方法,数组构造方法
2. 明确的执行方法
该方法只有名字不需要显式出现参数
3. 需要处理的数据
【联想,推导,省略】
4. :: 方法引用格式
2.3 通过类对象来执行方法引用
1. 明确对象
类对象
2. 明确执行的方法
自定义
3. 处理的数据
简单要求为String类型
代码演示
package com.qfedu.e_objectmethodreference;
/**
* 函数式接口
* 明确约束方法
* 方法类型是
* 参数是String类型参数
* 没有返回值
*/
@FunctionalInterface
public interface Printable {
void method(String str);
}
------------------------------------------------------------------------------------------
package com.qfedu.e_objectmethodreference;
/**
* 自定义类
*/
public class ObjectMethodReference {
/**
* ObjectMethodReference类内的【成员方法】
* 1. 参数是String类型
* 2. 没有返回值
* @param str 参数
*/
public void print(String str) {
System.out.println(str.toLowerCase());
}
/**
* ObjectMethodReference类内的【成员方法】
* 1. 参数是String类型
* 2. 没有返回值
* @param str 参数
*/
public void printSubstring(String str) {
System.out.println(str.substring(3));
}
public void saolei() {
System.out.println("无参数方法");
}
}
---------------------------------------------------------------------------------------------
package com.qfedu.e_objectmethodreference;
/**
* 类对象使用方法引用展示
*
* @author Anonymous 2020/3/12 16:18
*/
public class Demo2 {
public static void main(String[] args) {
/*
test方法需要使用一个Printable接口,执行对应的print方法
这里需要引用ObjectMethodReference类对象对应的print方法,该方法有展示能力
*/
ObjectMethodReference obj = new ObjectMethodReference();
/*
执行的对象:
ObjectMethodReference的类对象
明确执行的方法:
print方法
执行的目标:
"ABCDE" 可省略,可推导
*/
test(obj::printSubstring);
}
/**
*
* @param printable 函数式接口,约束的是方法类型
*/
public static void test(Printable printable) {
printable.method("ABCDE");
}
}
2.4 通过类名来执行方法引用
package com.qfedu.f_classmethodreference;
import java.util.List;
/**
* 函数式接口
*/
@FunctionalInterface
public interface PrintList {
/**
* 方法参数为List集合
* 没有返回值
* @param list List集合
*/
void print(List<?> list);
}
-----------------------------------------------------------------------------------------
package com.qfedu.f_classmethodreference;
import java.util.List;
/**
* 通过列明调用的静态方法,演示类
*/
public class ClassMethodReference {
/**
* 该方法符合要求
* 没有返回值
* @param list list集合
*/
public static void showListInfo(List<?> list) {
for (Object o : list) {
System.out.println(o);
}
}
}
--------------------------------------------------------------------------------------
package com.qfedu.f_classmethodreference;
import java.util.ArrayList;
/**
* 通过类名来执行方法引用
*
* @author Anonymous 2020/3/12 16:39
*/
public class Demo3 {
public static void main(String[] args) {
/*
明确调用对象
当前方法是一个静态成员方法,需要通过类名调用
明确调用方法
showListInfo
明确的数据
ArrayList<String> list = new ArrayList<>();
可省略,可推导
*/
testClass(ClassMethodReference::showListInfo);
/*
Lambda表达式
*/
testClass(list -> {
for (Object o : list) {
System.out.println(o);
}
});
}
/**
* 利用了一个函数式接口作为方法的参数
*
* @param printList 函数式接口
*/
public static void testClass(PrintList printList) {
ArrayList<String> list = new ArrayList<>();
list.add("BMW");
list.add("Audi");
printList.print(list);
}
}
2.5 通过super关键字执行方法引用
package com.qfedu.g_supermethodreference;
/**
* @author Anonymous 2020/3/12 16:52
*/
public interface SaySomeThing {
void say();
}
--------------------------------------------------------------------------------
package com.qfedu.g_supermethodreference;
/**
* @author Anonymous 2020/3/12 16:53
*/
public class Father {
public void sayHello() {
System.out.println("你好 Java");
}
}
-----------------------------------------------------------------------------------------
package com.qfedu.g_supermethodreference;
import java.util.Scanner;
/**
* @author Anonymous 2020/3/12 16:53
*/
public class Son extends Father {
public static void main(String[] args) {
//lambda
testSay(() -> System.out.println("你好"));
new Son().sonMethod();
}
/**
* 这里的参数是一个函数式接口,这里需要提供给一个符合要求的方法
* @param sst 函数式接口参数
*/
public static void testSay(SaySomeThing sst) {
sst.say();
}
public void sonMethod() {
/*
父类中有一个无参数无返回值的sayHello
满足当前SaySomeThing函数式接口,方法要求
执行的对象
super关键字,因为该方法在父类内
执行的方法:
sayHello
无需参数
*/
testSay(super::sayHello);
}
}
2.6 通过this关键字执行方法引用
package com.qfedu.h_thismethodreference;
/**
* @author Anonymous 2020/3/12 17:00
*/
public interface ORM {
/**
* int ==> String
* @param i int类型
* @return String类型
*/
String toStringType(int i);
}
-------------------------------------------------------------------------------------
package com.qfedu.h_thismethodreference;
/**
* @author Anonymous 2020/3/12 17:01
*/
public class ThisDemo {
public void test() {
String s = testThis(i -> i + ":");
System.out.println(s);
/*
调用方法的对象
this
执行的方法:
turn方法
处理的数据
10 (可省略,可联想)
*/
String s1 = testThis(this::turn);
}
public String turn(int i) {
return i + ":字符串";
}
public static String testThis(ORM orm) {
return orm.toStringType(10);
}
public static void main(String[] args) {
new ThisDemo().test();
}
}
2.7 通过构造方法执行方法引用
/**
* Perosn类
*
* @author Anonymous 2020/3/13 9:56
*/
public class Person {
private String name;
public Person() {}
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
------------------------------------------------------------------------------
/**
* 函数式接口
*
* @author Anonymous 2020/3/13 9:56
*/
public interface PersonMethodReference {
/**
* 该方法是获取Person类对象,形式参数是Stringname
* @param name String类型
* @return Person类对象
*/
Person getPerosnObject(String name);
}
------------------------------------------------------------------------------
/**
* 方法引用构造方法演示
*
* @author Anonymous 2020/3/13 9:54
*/
public class Demo1 {
public static void main(String[] args) {
/*
这里使用利用Lambda表达式完成对象创建
这里可以使用方法引用完成,引用的方法是Person类的构造方法
*/
printPerson("骚磊", name -> new Person(name));
/*
Person::new
Person是数据类型,是类名
new 引用的关键字(方法)
new --> 对应参数的构造方法
*/
printPerson("航海中路彭于晏", Person::new);
}
/**
* 方法参数使用到一个函数式接口的引用,这里可以是引用Lambda表达式来完成
*
* @param name String类型
* @param method 函数式接口
*/
public static void printPerson(String name, PersonMethodReference method) {
System.out.println(method.getPerosnObject(name));
}
}
2.8 通过数组构造方法执行方法引用
/**
* 函数式结构
*
* @author Anonymous 2020/3/13 10:07
*/
@FunctionalInterface
public interface ArrayConstructorReference {
/**
* 限制指定容量,返回int类型数组
* @param capacity 要求数组的初始化容量
* @return 返回一个int类型数组
*/
public int[] createIntArray(int capacity);
}
----------------------------------------------------------------------------------
/**
* 演示数组构造方式方法引用
*
* @author Anonymous 2020/3/13 10:08
*/
public class Demo1 {
public static void main(String[] args) {
/*
利用Lambda表达式完成函数式接口使用,创建数组对象
*/
int[] intArray = getIntArray(10, capacity -> new int[capacity]);
System.out.println(intArray);
/*
数组构造方法方法引用
明确数据类型
int 类型数组
[] 数组标记
:: new 需要申请空间,创建对应的数组
*/
int[] intArray1 = getIntArray(20, int[]::new);
System.out.println(intArray1);
}
/**
* 这里是创建Int类型数组返回的方法
*
* @param capacity 指定的数组容量
* @param ref 函数式接口
* @return 指定容量的int类型数组
*/
public static int[] getIntArray(int capacity, ArrayConstructorReference ref) {
return ref.createIntArray(capacity);
}
}