目录
1. 函数式接口
1.1 接口的增强
接口中可以放默认方法
@FunctionalInterface
public interface InterfaceTest {
void f1();
default void f2() {
System.out.println("aaaa");
}
}
1.2 定义(Functional)
- 只包含一个抽象方法的接口称为函数式接口
- 可以在此接口上使用注解
@FunctionalInterface
说明此接口为一个函数式接口 - 可以通过lambda表达式来创建该接口对象。
- lambda表达式是对象,不是函数,他必须依附于函数式接口
- 只要一个对象是函数式接口的实例,那么该对象就可以使用Lambda表达式来表示
1.3 函数式接口举例
@FunctionalInterface
public interface MyInterface {
void method1();只能是一个抽象方法
}
1.4 内置函数式接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer< T> 消费接口 | T | void | 对类型为 void accept(T t) T的对象应用操作,包含方法 |
Supplier< T> 供给型接口 | 无 | T | 返回类型为T的对象,包含方法: T get() |
Function<T,R> 函数接口 | T | R | 对类型为 果是R类型的对象。包含 T的对象应用操作,并返回结果。结 方法: R apply(T t) |
Predicate< T> 断定型接口 | T | boolean | 确定类型为 boolean 值。包含 T的对象是否满足某约束,并返回 方法: boolean test(T t) |
1.5 案例展示
Consumer
public static void main(String[] args) {
List<User> list = new ArrayList<>();
list.add(new User(1, "mickey", 22));
//在Stream中使用Consumer
Consumer<User> c = (s) -> {
System.out.println(s);
};
list.forEach(c);
}
@Test
public void test3() {
//使用Consumer处理金额
formatMoney((c) -> {
DecimalFormat df = new DecimalFormat("#,###.##");
System.out.println(df.format(c));
}, 1212.1213);
System.out.println("-------------");
formatMoney((c2) -> {
DecimalFormat df = new DecimalFormat("0,000.000");
System.out.println(df.format(c2));
}, 12345623434.789755555);
}
public void formatMoney(Consumer<Double> c, Double money) {
c.accept(money);
}
}
predicate
public static void main(String[] args) {
List<User> list = new ArrayList<>();
list.add(new User(1, "mickey", 22));
list.add(new User(2, "john", 33));
list.add(new User(3, "doc", 19));
list.add(new User(4, "cat", 20));
list.add(new User(5, "tom", 20));
list.add(new User(6, "tomcat", 70));
list.add(new User(7, "mysql", 73));
list.add(new User(8, "java", 84));
//删除年龄为20的元素
list.removeIf((s) -> {
return s.getAge() == 20;
});
System.out.println(list);
//查找大于等于5的用户
list.stream().filter(s -> {
return s.getId() >= 5;
}).forEach(u -> {
System.out.println(u);
});
}
public static List<String> filterList(List<String> list, Predicate<String> p) {
List<String> result = new ArrayList<>();
for (String s : list) {
if (p.test(s)) {
result.add(s);
}
}
return result;
}
@Test
public void test4() {
List<String> list = new ArrayList<>();
list.add("hello abc");
list.add("welcome abc");
list.add("hello world");
list.add("你好 abc");
list.add("大家好 abc");
list.add("hello mickey");
//打印包含hello的元素
filterList(list, s -> {
return s.contains("hello");
}).forEach(s -> {
System.out.println(s);
});
System.out.println("-----------");
//打印包含abc元素
filterList(list, s -> {
return s.contains("abc");
}).forEach(s -> {
System.out.println(s);
});
}
}
Function
public static void main(String[] args) {
List<User> list = new ArrayList<>();
list.add(new User(1, "mickey", 18));
list.add(new User(2, "john", 18));
list.add(new User(3, "doc", 18));
list.add(new User(4, "cat", 18));
list.add(new User(5, "tom", 18));
list.add(new User(6, "tomcat", 18));
list.add(new User(7, "mysql", 18));
list.add(new User(8, "java", 18));
//得到list中的id
//方式1
List<Integer> ids = new ArrayList<>();
for (User user : list) {
ids.add(user.getId());
}
System.out.println(ids);
//方式2
List<Integer> ids2 = list.stream().map(u -> {
return u.getId();
}).collect(Collectors.toList());
System.out.println(ids2);
}
public static Object performUser(Function<User, Object> f, User user) {
return f.apply(user);
}
@Test
public void test2() {
User user = new User(1, "mickey", 18);
//得到学员的名字
System.out.println(performUser(u -> {
return u.getName();
}, user));
//得到学员的名字加id
System.out.println(performUser(u -> {
return u.getName() + " " + u.getId();
}, user));
}
Supplier
public static void main(String[] args) {
printBean(() ->{
return new User(1, "mickey", 18);
});
printBean(()->{
return "hello.Stream";
});
}
public static void printBean(Supplier s){
System.out.println(s.get());
}
2. 方法引用
- 方法引用是lambda表达式的一个语法
- 注意:实现接口抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致即:形参是什么就用什么,不可改变。
- 格式
- 对象::实例方法名
- 类:: 静态方法名
- 类:: 实例方法名
public static void main(String[] args) {
List<User> list = new ArrayList<>();
list.add(new User(1, "mickey", 22));
list.add(new User(2, "john", 33));
list.add(new User(3, "doc", 19));
list.add(new User(4, "cat", 20));
list.add(new User(5, "tom", 20));
list.add(new User(6, "tomcat", 70));
list.add(new User(7, "mysql", 73));
list.add(new User(8, "java", 84));
//匿名内部类
list.forEach(new Consumer<User>() {
@Override
public void accept(User user) {
System.out.println(user);
}
});
System.out.println("--------------");
//lambda表达式
list.forEach(u -> {
System.out.println(u);
});
System.out.println("--------------");
//方法引用
list.forEach(System.out::println);
}
@Test
public void test2() {
//匿名内部类
Integer i1 = f1(new BiFunction<Integer, Integer, Integer>() {
@Override
public Integer apply(Integer d1, Integer d2) {
return Integer.max(d1, d2);
}
}, 12, 15);
//lambda表达式
Integer i2 = f1((d1, d2) -> {
return Integer.max(d1, d2);
}, 12, 15);
//方法引用
Integer i3 = f1(Integer::max, 12, 15);
System.out.println(i1 + "|" + i2 + "|" + i3);
}
public Integer f1(BiFunction<Integer, Integer, Integer> f, Integer d1, Integer d2) {
return f.apply(d1, d2);
}
}
3. StreamAPI
3.1 Stream简介
集合讲的是数据, Stream讲的是计算!
- stream不会自己存储数据
- Stream不会改变源对象,相反,他们会返回新的Stream
- Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行
3.2 Stream使用
● 创建:通过一个数据源(如:集合、数组)等获取一个流
● 中间操作:一个中间操作链,对数据源的数据进行处理
● 终止操作(终端操作):一旦执行该操作 就执行中间操作链,并产生结果。之后,不会再被使用
3.2 API
- 创建Stream方式
default Stream< E> stream()
: 返回一个顺序流 - 中间操作
方法 | 描述 |
---|---|
filter(Predicate p) | 接收 Lambda , 从流中排除某些元素 |
map(Function f) | 接收一个函数作为参数,该函数会被应用到每个元 素上,并将其映射成一个新的元素。 |
sorted( Comparator com) | 产生一个新流,其中按定制顺序排序 |
- 终止操作
方法 | 描述 |
---|---|
allMatch(Predicate p) | 检查是否匹配所有元素 |
forEach(Consumer c) | 内部迭代器 |
reduce(T iden, BinaryOperator b) | 可以将流中元素反复结合起来,得到一 个值。返回 T |
collect(Collector c) | 将流转换为其他形式。接收一个 Collector 接口的实现,用于给Stream中元素做汇总 的方法 |
案例
public static void main(String[] args) {
/**
* 收集:stream的最终操作
* - forEach循环打印
* - allMatch 是否匹配所有元素
* - collect 收集元素到List Set Map
*/
List<User> list = new ArrayList<>();
list.add(new User(1, "mickey", 22));
list.add(new User(2, "johna", 33));
list.add(new User(3, "doca", 19));
list.add(new User(4, "cat", 20));
list.add(new User(5, "toma", 20));
list.add(new User(6, "tomcat", 70));
list.add(new User(7, "mysqla", 73));
list.add(new User(8, "java", 84));
//得到年龄30以上的用户id组成的set
Set<Integer> collect = list.stream().filter(user -> {
return user.getAge() > 30;
}).map(user -> {
return user.getId();
}).collect(Collectors.toSet());
// System.out.println(collect);
//allMatch 检查是否匹配
boolean a = list.stream().allMatch(u -> {
return u.getName().contains("a");
});
// System.out.println(a);
// 对age进行排序
list.stream().sorted(new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
return Integer.compare(o1.getAge(),o2.getAge());
}
}).forEach(System.out::println);
//使用lambda表达式
list.stream().sorted((o1,o2)->{
return Integer.compare(o1.getAge(), o2.getAge());
}).forEach(user -> {
System.out.println(user);
});
//这里不能使用方法引用,因为形参为o1,o2;方法使用为o1.getAge(),o2.getAgr();
//得到上面id大于等于4的用户的年龄,得到年龄总和
System.out.println(list.stream().filter(u->{
return u.getId()>=4;
}).map(u->{
return u.getAge();
}).reduce((o1,o2)->{
return o1 + o2;
}).get());
//将id小于6的用户的年龄存放在List,并相加
Integer integer = list.stream().filter(u -> {
return u.getId() <= 6;
}).map(u -> {
return u.getAge();
}).collect(Collectors.toList()).stream().reduce((o1, o2) -> {
return o1 + o2;
}).get();
System.out.println(integer);
}
4. Optional类(了解)
解决空指针异常
- 到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。以前,为了解决空指针异常, Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到Google Guava的启发, Optional类已经成为Java 8类库的一部分。
- Optional 类(java.util.Optional) 是一个容器类, 它可以保存类型T的值, 代表这个值存在。或者仅仅保存null,表示这个值不存在。原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。
- Optional类的Javadoc描述如下:这是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
- Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
- 创建Optional类对象的方法:
- Optional.of(T t) : 创建一个 Optional 实例, t必须非空;
- Optional.empty() : 创建一个空的 Optional 实例
- Optional.ofNullable(T t): t可以为null
- 判断Optional容器中是否包含对象:
- boolean isPresent() : 判断是否包含对象
- void ifPresent(Consumer<? super T> consumer) : 如果有值,就执行Consumer接口的实现代码,并且该值会作为参数传给它。
- 获取Optional容器的对象:
- T get(): 如果调用对象包含值,返回该值,否则抛异常
- T orElse(T other) : 如果有值则将其返回,否则返回指定的other对象。
- T orElseGet(Supplier<? extends T> other) : 如果有值则将其返回,否则返回由Supplier接口实现提供的对象。
- T orElseThrow(Supplier<? extends X> exceptionSupplier) : 如果有值则将其返回,否则抛出由Supplier接口实现提供的异常。
案例
public class Test1 {
/*
Optional.of(T t) : 创建一个 Optional 实例,t必须非空;
Optional.empty() : 创建一个空的 Optional 实例
Optional.ofNullable(T t):t可以为null
*/
@Test
public void test1() {
Girl girl = new Girl();
girl = null;
//of(T t):保证t是非空的
Optional<Girl> optionalGirl = Optional.of(girl);
}
@Test
public void test2() {
Girl girl = new Girl();
girl = null;
//ofNullable(T t):t可以为null
Optional<Girl> optionalGirl = Optional.ofNullable(girl);
System.out.println(optionalGirl);
//orElse(T t1):如果单前的Optional内部封装的t是非空的,则返回内部的t.
//如果内部的t是空的,则返回orElse()方法中的参数t1.
Girl girl1 = optionalGirl.orElse(new Girl("赵丽颖"));
System.out.println(girl1);
}
public String getGirlName(Boy boy) {
return boy.getGirl().getName();
}
@Test
public void test3() {
Boy boy = new Boy();
// boy = null;
String girlName = getGirlName(boy);
System.out.println(girlName);//null,报错
}
//优化以后的getGirlName():
public String getGirlName1(Boy boy) {
if (boy != null) {
Girl girl = boy.getGirl();
if (girl != null) {
return girl.getName();
}
}
return null;
}
@Test
public void test4() {
Boy boy = new Boy();
boy = null;
String girlName = getGirlName1(boy);
System.out.println(girlName);
}
//使用Optional类的getGirlName():
public String getGirlName2(Boy boy) {
Optional<Boy> boyOptional = Optional.ofNullable(boy);
//此时的boy1一定非空
Boy boy1 = boyOptional.orElse(new Boy(new Girl("迪丽热巴")));
Girl girl = boy1.getGirl();
Optional<Girl> girlOptional = Optional.ofNullable(girl);
//girl1一定非空
Girl girl1 = girlOptional.orElse(new Girl("古力娜扎"));
return girl1.getName();
}
@Test
public void test5() {
Boy boy = null;
boy = new Boy();
boy = new Boy(new Girl("貂蝉"));
String girlName = getGirlName2(boy);
System.out.println(girlName);
}
}
class Boy {
private Girl girl;//Boy中有一个Girl类型的属性
@Override
public String toString() {
return "Boy{" +
"girl=" + girl +
'}';
}
public Girl getGirl() {
return girl;
}
public void setGirl(Girl girl) {
this.girl = girl;
}
public Boy() {
}
public Boy(Girl girl) {
this.girl = girl;
}
}
class Girl {
private String name;
@Override
public String toString() {
return "Girl{" +
"name='" + name + '\'' +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Girl() {
}
public Girl(String name) {
this.name = name;
}
}