Day42 JDK1.8新特性
1.方法、构造方法和数组引用
方法、构造方法和数组引用就是Lambda的另一种表现形式
1.1 方法引用
若Lamdba表达式中的内容由方法已经实现了,可以使用方法引用这个技能
当你需要使用方法引用时,目标引用放在分隔符::前,方法的名称放在后面
1.1.1 对象::实例方法
Lambda表达式中调用方法的参数类型和返回值必须和函数式接口中的抽象方法一致
public class Test01 {
public static void main(String[] args) {
// I1 i1 = new I1() {
// @Override
// public void method(String str) {
// System.out.println(str);
// }
// };
// i1.method("用良心做教育");
// I1 i1 = (str)->System.out.println(str);
// i1.method("用良心做教育");
//println里的参数列表和返回值类型必须和method方法一致才行
I1 i1 = System.out::println;//对象::实例方法
i1.method("用良心做教育");
}
}
interface I1{
public void method(String str);
}
1.1.2 类名::静态方法
Lambda表达式中调用方法的参数类型和返回值必须和函数式接口中的抽象方法一致
public class Test01 {
public static void main(String[] args) {
// Comparator<Integer> comparator = new Comparator<Integer>() {
// @Override
// public int compare(Integer o1, Integer o2) {
// return Integer.compare(o1, o2);
// }
// };
// int compare = comparator.compare(10, 20);
// System.out.println(compare);
// Comparator<Integer> comparator = (o1,o2)-> Integer.compare(o1, o2);
// int compare = comparator.compare(10, 20);
// System.out.println(compare);
Comparator<Integer> comparator = Integer::compare;
int compare = comparator.compare(10, 20);
System.out.println(compare);
}
}
1.1.3 类名::实例方法
Lambda表达式参数列表中第一个参数必须是实例方法的调用者
Lambda表达式参数列表中第二个参数必须是实例方法的参数
public class Test01 {
public static void main(String[] args) {
// I1<String> i1 = new I1<String>() {
// @Override
// public boolean method(String t1, String t2) {
// return t1.equals(t2);
// }
// };
// boolean method = i1.method("abc", "abc");
// System.out.println(method);
// I1<String> i1 = (str1,str2)->str1.equals(str2);
// boolean method = i1.method("abc", "abc");
// System.out.println(method);
//重现method方法,该方法的第一个参数调用equals,第二个参数是传入equals里的数据
I1<String> i1 = String::equals;
boolean method = i1.method("abc", "abc");
System.out.println(method);
}
}
interface I1<T>{
public boolean method(T t1,T t2);
}
1.2 构造方法引用
类名::new
需要调用的构造方法的参数列表必须和函数式接口中抽象方法的参数列表一致
public class Test01 {
public static void main(String[] args) {
//需求:创建学生对象
// I1 i1 = new I1() {
// @Override
// public Student method() {
// return new Student();
// }
// };
// Student stu = i1.method();
// System.out.println(stu);
I1 i1 = Student::new;//调用无参构造去创建学生对象
Student stu1 = i1.method();
System.out.println(stu1);
I2 i2 = Student::new;
Student stu2 = i2.method("麻生希", 23, 12000, Course.JAVA);
System.out.println(stu2);
}
}
interface I1{
public Student method();
}
interface I2{
public Student method(String name, int age, double salary, Course course);
}
enum Course{//课程枚举
JAVA,HTML,PYTHON;
}
class Student{//学生类
private String name;
private int age;
private double salary;
private Course course;
...
}
1.3 数组引用
语法格式:type[]::new
public class Test01 {
public static void main(String[] args) {
// I1<String[]> i1 = (capacity)->new String[capacity];
// String[] ss = i1.method(3);
// System.out.println(Arrays.toString(ss));
I1<String[]> i1 = String[]::new;
String[] ss = i1.method(3);
System.out.println(Arrays.toString(ss));
}
}
interface I1<T>{
public T method(int capacity);
}
2.Stream
2.1 简介
Stream(流)是数据渠道,用于操作数据源(集合、数组等),生成元素序列。换言之,集合是存储数据的容器,流使用操作这些数据的
Stream可以对集合进行非常复杂的查找、过滤、映射数据等操作,类似于SQL执行数据库查询。Stream提供了一种高效且易于使用的处理数据的方式
注意:
- Stream不会存储数据
- Stream不会改变源数据,通过一系列操作数据源会返回一个持有结果的新Stream
- Stream操作是延迟执行的,意味着流会等到需要结果的时候才执行
2.2 执行步骤
- 创建Stream:通过数据源(集合、数组等)获取一个Stream
- 中间操作:中间操作链,对源数据的数据进行处理
- 终止操作:执行中间操作,并产生结果
2.3 创建Stream
public class Test01 {
@Test
public void test01(){
//获取集合的Stream对象
List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","eee");
Stream<String> stream = list.stream();
stream.forEach(System.out::println);
}
@Test
public void test02(){
//获取数组的Stream对象
String[] ss = {"aaa","bbb","ccc","ddd","eee"};
Stream<String> stream = Arrays.stream(ss);
stream.forEach(System.out::println);
}
@Test
public void test03(){
//通过Stream静态方法of()获取Stream对象
Stream<String> stream = Stream.of("aaa","bbb","ccc","ddd","eee");
stream.forEach(System.out::println);
}
@Test
public void test04(){
//通过Stream静态方法iterate()获取无限流对象
Stream<Integer> stream = Stream.iterate(10, (x)->x+10);//10,20,30,40,50,60....
Stream<Integer> newStream = stream.limit(10);
newStream.forEach(System.out::println);
}
@Test
public void test05(){
//通过Stream静态方法generate()获取无限流对象
Stream<Double> stream = Stream.generate(Math::random);
Stream<Double> newStream = stream.limit(10);
newStream.forEach(System.out::println);
}
}
注意:多个中间操作可以连接成一个流水线,除非流水线触发终止操作,否则中间操作不会执行任何的处理,而在终止操作时一次性全部处理,称为惰性求值/延迟加载
2.4 中间操作 - 筛选与切片
方法 | 描述 |
---|---|
filter(Predicate p) | 从流中排除元素,过滤 |
limit(long maxSize) | 设置限制数据条数 |
skip(long n) | 跳过元素,返回跳过n个元素的流,若流中不满足n个元素则返回空流。与limit()互补 |
distinct() | 筛选,流通过元素对象的hashCode()和equals()方法去除重复元素 |
如果没有终止操作,中间操作就不会被调用,终止操作时一次性全部处理,这种称为惰性求值/延迟加载
public class Test02 {
List<Student> stuList = Arrays.asList(
new Student("张三", 28, 4800,Course.JAVA),
new Student("李四", 36, 7200,Course.JAVA),
new Student("王五", 19, 9600,Course.HTML),
new Student("赵六", 42, 6100,Course.HTML),
new Student("孙七", 23, 9600,Course.PYTHON),
new Student("吴八", 31, 3000,Course.PYTHON),
new Student("李四", 36, 7200,Course.JAVA));
@Test
public void test01(){
//需求1:过滤掉工资小于5000的学生对象
stuList.stream().filter((stu)->{
double salary = stu.getSalary();
if(salary > 5000){
return true;
}
return false;
}).forEach(System.out::println);
//迭代输出流里的数据就等同于终止操作
//迭代功能在forEach()中完成,称为内部迭代(集合使用iterator()称为外部迭代)
}
@Test
public void test02(){
//需求2:过滤掉工资小于5000的学生对象,并显示3条
//注意:因为限制了数据条数,所以满足数据条数后,后续的操作就不再运行了,效率就提高了
stuList.stream().filter((stu)->{
double salary = stu.getSalary();
if(salary > 5000){
return true;
}
return false;
}).limit(3).forEach(System.out::println);
}
@Test
public void test03(){
//需求3:过滤掉工资小于5000的学生对象,并跳过第1个学生对象
stuList.stream().filter((stu)->{
double salary = stu.getSalary();
if(salary > 5000){
return true;
}
return false;
}).skip(1).forEach(System.out::println);
}
@Test
public void test04(){
//需求4:过滤掉小于5000的学生对象,并筛选掉重复元素
//注意:去重前提是元素所属类重写了hashCode和equals
stuList.stream().filter((stu)->{
double salary = stu.getSalary();
if(salary > 5000){
return true;
}
return false;
}).distinct().forEach(System.out::println);
}
}
enum Course{//课程枚举
JAVA,HTML,PYTHON;
}
class Student{//学生类
private String name;
private int age;
private double salary;
private Course course;
...
}
2.5 中间操作 - 映射
方法 | 描述 |
---|---|
map(Function<?, ? > mapper) 常用 | 将流中所有元素映射成一个新的元素或者提取信息 |
flatMap(Function<?, ? extends Stream<? >> mapper) | 将流中的流整合(整合成平面/平铺流) |
public class Test03 {
List<String> nameList = Arrays.asList("张三","李四","王五","赵六","孙七","吴八");
List<Student> stuList = Arrays.asList(
new Student("张三", 28, 4800,Course.JAVA),
new Student("李四", 36, 7200,Course.JAVA),
new Student("王五", 19, 9600,Course.HTML),
new Student("赵六", 42, 6100,Course.HTML),
new Student("孙七", 23, 9600,Course.PYTHON),
new Student("吴八", 31, 3000,Course.PYTHON),
new Student("李四", 36, 7200,Course.JAVA));
@Test
public void test01(){
//方式1:映射成一个新的元素
//需求1:nameList获取流对象,打印出所有学生的姓氏
nameList.stream().map((name)->name.charAt(0)).forEach(System.out::println);
}
@Test
public void test02(){
//方式2:映射成提取信息
//需求2:stuList获取流对象,把原来流中的学生对象替换成学生姓名
stuList.stream().map(Student::getName).forEach(System.out::println);
}
@Test
public void test03(){
//需求:将nameList里的字符串转换为字符输出
//解决方案1:使用map()完成需求,可以看到流里包含另外的流,非常麻烦
// nameList.stream().
// map(Test03::getCharacterStream).
// forEach((x)->{
// x.forEach(System.out::println);
// });
//flatMap平铺流 -- 可以遍历流里面的流(将流中的流整合(整合成平面/平铺流))
//解决方案2:使用flatMap()完成需求,将流中的流一并整合 nameList.stream().flatMap(Test03::getCharacterStream).forEach(System.out::println);
}
//将字符串拆分出字符转换为流的方法
public static Stream<Character> getCharacterStream(String str){
ArrayList<Character> list = new ArrayList<>();
for (Character c : str.toCharArray()) {
list.add(c);
}
return list.stream();
}
}
enum Course{//课程枚举
JAVA,HTML,PYTHON;
}
class Student{//学生类
private String name;
private int age;
private double salary;
private Course course;
...
}