JDK8新特性
Lambda表达式
之前存在的问题
使用匿名内部类
1.定义了一个没有名字的类
2.这个类实现了runnable接口
3.创建了这个类的对象
存在的问题
语法冗余
Lambda表达式体现的是函数式编程思想,只需要将要执行的代码放到函数中(即方法中)
使用时直接使用lambda表达式
new Thread(new Runnable(){
@Override
public void run(){
System.out.println("线程执行")
}
}).start();
new Thread(() -> {
System.out.println("线程执行")
}).start();
标准格式
(参数列表) -> {}
省略格式
1.参数类型可以省略
2.小括号内只有一个参数,小括号省略
3.大括号内只有一条语句,省略大括号、return和分号
匿名内部类和Lambda表达式区别
1.所需类型不同
匿名内部类:需要的类型可以是类,接口,抽象类
Lambda表达式:只能是接口
2.抽象方法的数量不一样
匿名内部类所需的接口中抽象方法的数量随意
Lambda表达式所需的接口只能有一个抽象方法
3.实现原理不同
匿名内部类是在编译后形成class文件
Lambda表达式是在程序运行的时候动态生成class
常用内置函数式接口
JDK8接口新增的两个方法
jdk8之前的接口
interface 接口名{
静态常量;
抽象方法
}
jdk8接口
interface 接口名{
静态常量;
抽象方法;
默认方法; //接口名不能调用,只能继承类对象 调用
静态方法; //实现类不能继承
}
常用内置函数式接口
Supplier接口
==java.util.function.Supplier==接口,它意味着供给,对应的Lambda表达式需要"对外提供"一个符合泛型类型的对象
@FunctionalInterFace
public interface Supplier<T> {
public abstract T get();
}
供给型接口,通过Supplier接口中的get方法可以得到一个值,无参有返回的接口
使用Lambda表达式返回数组元素最大值
使用Supplier接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值,提示:接口的泛型请使用Integer类
public class DemoSupplier{
public static void main(String[] args) {
printMax(() -> {
int[] arr = {10, 20, 100, 30, 40, 50};
//先排序,最后就是最大值
Arrays.sort(arr);
return arr[arr.length - 1]; //最大值
});
}
private static void printMax(Supplier<Integer> supplier){
int maxNum = supplier.get();
System.out.prinln(maxNum)
}
}
Consumer接口
==java.util.function.Consumer==接口,不是生产一个数据,而是消费一个数据,其数据类型由泛型参数决定
@FunctionalInterface
public interface Consumer<T> {
public abstract void accept(T t);
}
使用Lambda表达式将一个字符串转成大写和小写的字符串
public class DemoConsumer{
public static void main(String[] args) {
test(s -> System.out.println(s.toLowerCase()));
s -> System.out.println(s.toUpperCase()));
}
public static void test(Consumer<String> consumer1, Consumer<String> consumer2) {
String str = "HelloWorld";
//consumer1.accept(str);
//consumer2.accept(str)
//先执行1在执行2
consumer1.andThen(consumer2).accept(str)l;
}
}
Function接口
==java.util.function.Function<T,R>==接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件,有参数有返回值
@FunctionalInterface
public interface Function<T,R> {
public abstract R apply(T t);
}
使用Lambda表达式将字符串转成数字
public class DemoFunction {
public static void main(String[] args) {
test(s -> Interger.parseInt(s));
}
public static void test(Function<String,Integer> function) {
Integer in = function.apply("10");
System.out.println(in);
}
}
andThen方法两次操作,同上
Predicate接口
==java.util.function.Predicate==接口返回boolean类型结果,可以对某种类型的数据进行判断
@FunctionalInterface
public interface Predicate<T> {
public abstract boolean test(T t);
}
使用Lambda判断一个人名如果超过3个字就认为是很长的名字
public class DemoPredicate {
public static void main(String[] args) {
test(s -> s.length() > 3, "迪丽热巴");
}
private static void test(Predicate<String> predicate, String str) {
boolean veryLong = predicate.test(str);
System.out.println(veryLong);
}
}
and(Predicate<T> predicate)函数 与函数
t1.and(t2).test(str)
or() 或函数
negate() 非函数 结果取反
方法引用
Lambda的冗余场景
使用Lambda表达式求一个数组的和
public class DemoMethodRefIntro{
//求和
public static void getMax(int[] arr) {
int sum = 0;
for(int n : arr){
sum += n;
}
System.out.println(sum);
}
public static void main(String[] args) {
/*printMax((int[] arr) -> {
int sum = 0;
for(int n : arr){
sum += n;
}
System.out.println(sum);
});*/
//方法引用
//让指定方法重写接口的抽象方法
printMax(DemoMethodRefIntro::getMax);
}
private static void printMax(Consumer<T> consumer) {
int[] arr = {10, 20, 30, 40, 50};
consumer.accept(arr);
}
}
方法引用的格式
符号表示 ::
应用场景:替代Lambda方法
常见引用方式:
1.instanceName::methodName 对象::方法名
2.ClassName::staticMethodName 类名::静态方法名
3.ClassName::methodName 类名::方法名
4.ClassName::new 类名::new调用构造器
5.TypeName[]::new String[]:new调用数组的构造器
对象名::方法名
@Test
public void test(){
Date now = new Date();
Supplier<Long> su1 = () -> {
return now.getTime();
};
Long aLong = su1.get();
System.out.println(aLong);
}
//方法引用
/*
1.被引用的方法,参数要和接口中抽象方法的参数一样
2.当接口的抽象方法有返回值时,被引用的方法也必须有返回值
*/
@Test
public void test(){
Date now = new Date();
Supplier<Long> su1 = now::getTime;
Long aLong = su1.get();
System.out.println(aLong);
}
类名::静态方法
@Test
public void test(){
Supplier<Long> Su = System::currentTimeMillis;
}
类名::实例方法
@Test
public void test(){
//类名::实例方法 实际上会将第一个参数作为方法的调用者
Function<String,Integer> f1 = String::length;
int length = f1.apply("hello");
System.out.println("length = " + length);
/*
BiFunction<T,U,R>
R apply(T, U);
*/
BiFunction<String, Integer, String> f2 = String::substring;
String str2 = f2.apply("helloworld", 3);
System.out.println(str2);
}
类名::new (自动调用构造器)
@Test
public void test(){
Supplier<Person> su1 = Person::new;
Person person = su1.get();
System.out.println("Person =" + person);
}
//有参
@Test
public void test(){
BiFunction<String, Integer, Person> bif = Person::new;
Person person = bif.apply("zhangsan", 18);
System.out.println(person);
}
数组::new (自动调用数组构造器)
@Test
public void test(){
Function<Integer, int[]> f1 = int[]::new;
int arr[] = f1.apply(10);
System.out.println(Arrays.toString(arr));
}
集合之Stream流式操作
集合处理数据的弊端
@Test
public void test(){
//ArrayList集合中存储数据
//需求
//1.拿到所有姓张的
//2.再拿到张姓里面名字长度3个字的
//3.打印
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"张无忌","周芷若","赵敏","张强","张三丰");
//1.拿到所有姓张的
ArrayList<String> zhangList = new ArrayList();
for(String name : list){
if(name.startsWith("张")){
zhangList.add(name);
}
}
//2.拿到名字长度为3个字的
ArrayList<String> threeList = new ArrayList();
for(String name : zhangList){
if(name.length == 3){
threeList.add(name);
}
}
//3.打印
for(String name : threeList){
System.out.println(name);
}
}
Stream流式思想概述
注意:Stream和IO流没有任何关系
Stream流式思想类似于工厂车间的生产流水线,Stream流不是一种数据结构,不保存数据,而是对数据进行加工处理。Stream可以看作是流水线上的一个工序,在流水线上,通过多个工序让一个原材料加工成一个商品
@Test
public void test(){
//ArrayList集合中存储数据
//需求
//1.拿到所有姓张的
//2.再拿到张姓里面名字长度3个字的
//3.打印
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"张无忌","周芷若","赵敏","张强","张三丰");
//1.拿到所有姓张的;2.拿到姓张的名字三个字的;3.打印
list.stream()
.filter((s) -> {
return s.startsWith("张");
})
.filter((s) -> {
return s.length() == 3;
})
.forEach((s) -> {
System.out.println(s);
})
}
获取Stream流的两种方式
根据Collection获取流
//根据Collection获取流
//Collection接口中有一个默认的方法,default Stream<E> stream()
List<String> list = new ArrayList();
Stream<String> stream1 = list.stream();
Set<String> set - new HashSet();
Stream<String> stream2 = set.stream();
Map<String, String> map = new HashMap();
Stream<String> stream3 = map.keySet().stream();
Stream<String> stream4 = map.values().stream();
Stream<Map.Entry<String, String>> stream5 = map.entrySet().stream();
Stream中的静态方法of获取流
由于数组对象不可能添加默认方法,所以Stream接口中提供了静态方法of
Stream<String> stream = Stream.of("aa","bb","cc");
String[] strs = {"aa","bb","cc"};
Stream<String> stream2 = Stream.of(strs);
//基本数据类型的数组不能使用Stream流,会将整个数组看作一个元素
Stream常用方法和注意事项
方法名 | 方法作用 | 返回值类型 | 方法种类 |
---|---|---|---|
count | 统计个数 | long | 终结 |
forEach | 逐一处理 | void | 终结 |
filter | 过滤 | Stream | 函数拼接 |
limit | 取用前几个 | Stream | 函数拼接 |
skip | 跳过前几个 | Stream | 函数拼接 |
map | 映射 | Stream | 函数拼接 |
concat | 组合 | Stream | 函数拼接 |
**终结方法:**返回值类型不再是Stream类型的方法,不再支持链式调用
**非终结方法:**返回值仍然是Stream类型的方法,支持链式调用
注意事项(重要)
1.Stream流调用终结方法后,就会被关闭
2.Stream非终结方法返回新的流
3.Stream不调用终结方法,中间的操作不会执行
常用方法
forEach
List<String> list = new ArrayList();
Collection.addAll(list,"","");
list.stream().forEach(s -> System.out.println(s));
count
List<String> list = new ArrayList<>();
Collections.addAll(list, "a", "b", "c");
long count = list.stream().count();
System.out.println(count);
filter
//过滤数据
List<String> list = new ArrayList<>();
Collections.addAll(list, "a", "b", "c");
list.stream()
.filter(s -> s=="a")
.forEach(System.out::println);
limit
//对流数据进行截取
List<String> list = new ArrayList<>();
Collections.addAll(list, "a", "b", "c");
list.stream()
.limit(2)
.forEach(System.out::println);
skip
//跳过指定个数数据
List<String> list = new ArrayList<>();
Collections.addAll(list, "a", "b", "c");
list.stream()
.skip(1)
.forEach(System.out::println);
map
//把一种类型的流转换为另一种类型的流
List<String> list = new ArrayList<>();
Collections.addAll(list, "1", "2", "3");
list.stream()
.map(s -> Integer.parseInt(s))
.forEach(s -> System.out.println(s.getClass()));
sorted
//将数据排序,可以默认自然排序(无参),也可以指定比较器(有参)
Stream<Integer> stream = Stream.of(55, 22, 66, 33, 11);
stream.sorted()
.sorted((o1, o2) -> o2 - o1)
.forEach(System.out::println);
distinct
//去重 自定义对象去重需要重写equals和hashcode方法
Stream<String> stream = Stream.of("a", "a", "b", "b", "c");
stream.distinct().forEach(System.out::println);
match
Stream接口中提供了三个相关方法
boolean allMatch(Predicate<? super T> predicate);//元素是否全部满足条件
boolean anyMatch(Predicate<? super T> predicate);//元素是否有任一个满足条件
boolean noneMatch(Predicate<? super T> predicate);//元素是否全部不满足条件
//allMatch
Stream<Integer> stream = Stream.of(5, 4, 3, 2, 1);
boolean b = stream.allMatch(s -> s < 7);
System.out.println(b);
//anyMatch
Stream<Integer> stream = Stream.of(5, 4, 3, 2, 1);
boolean b = stream.anyMatch(s -> s < 3);
System.out.println(b);
//noneMatch
Stream<Integer> stream = Stream.of(5, 4, 3, 2, 1);
boolean b = stream.noneMatch(s -> s < 3);
System.out.println(b);
find
//从集合中找到第一个数据
//findFirst
Stream<Integer> stream = Stream.of(5, 4, 3, 2, 1);
Optional<Integer> first = stream.findFirst();
System.out.println(first.get());
//findAny
Stream<Integer> stream = Stream.of(5, 4, 3, 2, 1);
Optional<Integer> first = stream.findAny();
System.out.println(first.get());
max和min
//得到最大或最小的值
//max 取最后一个值,自己比较排序好
Optional<Integer> max = Stream.of(5, 4, 3, 2, 1).max((o1, o2) -> o1 - o2);
System.out.println(max.get());
//min 取第一个值,自己排序好
Optional<Integer> max = Stream.of(5, 4, 3, 2, 1).min((o1, o2) -> o2 - o1);
System.out.println(max.get());
reduce ***
//将所有数据归纳得到一个数据
Stream<Integer> integerStream = Stream.of(4, 5, 9, 7);
//T reduce(T identity, BinaryOperator<T> accumulator);
// T identity是默认值
// BinaryOperator对数据处理的方式
Integer sum = integerStream.reduce(0, (a, b) -> {
System.out.println("a = " + a + ";b = " + b);
return a+b;
});
System.out.println(sum);
//map和reduce组合使用
//求所有年龄的总和
//1.得到所有年龄
//2.求和
Person p1 = new Person("张三", 20);
Person p2 = new Person("李四", 32);
Person p3 = new Person("王五", 18);
Person p4 = new Person("赵六", 45);
Stream<Person> stream = Stream.of(p1, p2, p3, p4);
int ageCount = stream.map(s -> s.getAge())
.reduce(0, (a, b) -> a + b);
System.out.println(ageCount);
mapToInt
//Stream流中的Integer数据转换为int数据