目录
函数式接口
什么是函数式(Functional)接口?
只包含一个抽象方法的接口,称为函数式接口
比如Runnable接口,接口里只包含一个抽象接口的run()方法,同时类上有@FunctionalInterface注解。
@FunctionalInterface注解可以检查它是否是一个函数式接口,同时Javadoc也会包含一条声明,说明这个接口是一个函数式接口。
函数式接口其实就是为了便于写lambda表达式而产生的。
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
如果要使用Lambda表达式,就一定要使用函数式接口,因为lambda表达式的本质就是作为函数式接口的实例。
Java内置四大核心函数式接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
Consumer<T> 消费型接口 | T | void | 对类型为T的对象应用操作,包含方法 void accpet(T t) |
Supplier<T> 供给型接口 | 无 | T | 返回类型为T的对象,包含方法: T get() |
Function<T,R> 函数型接口 | T | R | 对类型为T的对象应用操作,并返回结果。结果为R类型的对象,包含方法 R apply(T t) |
Predicate<T> 断定型接口 | T | boolean | 确定类型为T的对象是否满足某约束,并返回boolean值,包含方法: boolean值。包含方法: boolean test(T t) |
上面一大段看起来挺抽象的,用举例子的方式来说明。
实例(一) Function 函数式接口
public static void main(String[] args) {
// Function<T,R> T:函数的输入类型 R:函数的返回类型
// Function 匿名实现类的写法
Function<String,String> function = new Function<String,String>() {
@Override
public String apply(String str) {
return str;
}
};
System.out.println(function.apply("函数式接口"));
// 函数式接口 lambda表达式的写法
Function function1=(str)->{return str;};
System.out.println(function1.apply("lambda表达式"));
}
实例(二)Predicate 断定型接口
public static void main(String[] args) {
// 断定型接口 Predicate 传入一个指定类型的参数,返回一个经过判定后的布尔值
// 匿名内部类写法
Predicate<String> predicate = new Predicate<String>() {
@Override
public boolean test(String str) {
return str.isEmpty();
}
};
System.out.println(predicate.test("test"));
// lambda表达式的写法
Predicate<String> predicate1 =str->str.isEmpty();
System.out.println(predicate1.test(""));
}
实例(三)Consumer 消费型接口
public static void main(String[] args) {
// Consumer 消费型接口 接受单个输入参数但不返回结果
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String o) {
System.out.println(o);
}
};
consumer.accept("打印");
// lambda表达式写法
Consumer<String> consumer1 = str->System.out.println(str);
consumer1.accept("打印");
}
实例(四)Supplier 供给型接口
public static void main(String[] args) {
// 供给型接口 没有参数 有返回值
// 匿名实现类的写法
Supplier<Integer> supplier = new Supplier<Integer>() {
@Override
public Integer get() {
return 1024;
}
};
System.out.println(supplier.get());
Supplier<Integer> supplier2 =()->{ return 1024; };
System.out.println(supplier2.get());
}
方法引用与构造器的使用
当要传递给lambda体的操作,已经有实现方法了,就可以使用方法引用。
方法引用可以看做是lambda表达式的深层次的表达,方法引用就是lambda表达式,也就是函数式接口的实例,可以通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖。
使用要求:实现接口的抽象的参数列表和返回值类型,必须与方法引用的参数列表和返回值类型保持一致。
方法体 System.out.println(str); 已经有了实现方法,是PrintStream中 void printLn(T t)方法,因此它可以使用方法引用
使用格式:类或者对象::方法名
// 消费型接口 Consumer中的 void accept(T t)
Consumer<String> consumer = str->{ System.out.println(str);};
/**
* PrintStream中的void println(T t)
* System.out.println() 是PrintStream中 void printLn(T t)方法
* 因此我们这个System.out.println(str)方法体中已经有了lambda表达式的实现了,所以可以使用方法引用
* 使用格式:类或者对象::方法名
*/
Consumer<String> consumer2 = System.out::println;
强大的StreamAPI
Java8中的两个最为重要的改变就是lambda表达式以及StreamAPI。
SteamAPI(java.util.stream)把真正的函数式编程风格引入到Java中,可以写出高效率、干净、简洁的代码。
使用StreamAPI对集合数据进行操作,就类似于使用SQL执行数据库查询,也可以使用SteamAPI并行执行操作,简而言之,SteamAPI提供了高效、易于使用的处理数据的方式。
Stream到底是什么呢?
Stream是数据渠道,用于操作数据源(集合,数组等)所产生的元素序列。
集合讲的是数据,Stream讲的是计算
Stream的操作三个步骤
- 创建Stream
- 一个数据源(集合、数组),获取一个流
- 中间操作
- 一个中间操作链,对数据源的数据进行处理
- 终止操作
- 一旦终止操作,就执行中间操作链,并产生结果,之后不会再被使用
创建Stream方式一:通过集合
Java8中的Collection接口被拓展,提供了两个获取流的方法
default Stream<E> Stream():返回一个顺序流
default Steam<E> parallelStream:返回一个并行流
/**
* 创建流的第一种方式 通过集合
*
* @param args
*/
public static void main(String[] args) {
List<User> userList = new ArrayList<>();
userList.add(new User("小沈", 12));
userList.add(new User("小丁", 13));
userList.add(new User("小蟹", 14));
// 返回一个顺序流
Stream<User> stream = userList.stream();
// 返回一个并行流
Stream<User> parallelStream = userList.parallelStream();
}
创建Steam方式二:通过数组
java8中的Arrays的静态方法steam()可以获取数组流
static<T> Stream<T> stream(T[] array):返回一个流
/**
* 创建Stream的第二种方式 通过数组
*/
@Test
public void test() {
int[] arr = new int[]{1, 2, 3, 4, 5};
// 得到一个Integer类型的数组流
IntStream stream = Arrays.stream(arr);
User user1 = new User("小沈", 12);
User user2 = new User("小蟹", 12);
User[] userarry = new User[]{user1, user2};
// 得到一个User类型的数组流
Stream<User> stream1 = Arrays.stream(userarry);
}
创建Stream的方式三:通过Stream的of()
/**
* 创建Stream方式三: 通过Stream的of()
*/
@Test
public void test3() {
// 得到一个Integer类型的流
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5);
}
Stream的中间操作 - 筛选与切片
多个中间操作可以连起来形成一个流水线,触发流水线上触发终止操作,否则中间操作不会执行任何处理,而在终止操作时一次性全部处理,称为惰性求值。
1.筛选与切片
方法 | 描述 |
filter(Predicate p) | 接受Lambda,从流中排出某些元素 |
distinct() | 筛选,通过流所产生元素的hashCode()和equals()去重复元素 |
limit(long maxSize) | 截断流,使其元素不超过给定数量 |
skip(long n) | 跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补 |
实例(一) filter方法测试
Lambda表达式我总也用不熟练,所以我采用了老式写法和Lambda写法,以熟悉Lambda表达式。
@Test
public void test4() {
List<User> userList = new ArrayList<>();
userList.add(new User("小沈", 1));
userList.add(new User("小丁", 11));
userList.add(new User("小蟹", 21));
// 返回一个顺序流
Stream<User> stream = userList.stream();
// 过滤出userList里age>10的元素 匿名实现类写法
stream.filter(new Predicate<User>() {
@Override
public boolean test(User user) {
return user.getAge()>10;
}
}).forEach(new Consumer<User>() {
@Override
public void accept(User user) {
System.out.println(user);
}
});
Stream<User> stream2 = userList.stream();
// 过滤出userList里age>10的元素 Lambda表达式写法
stream2.filter(u->u.getAge()>10).forEach(System.out::println);
}
实例(二)distinc()方法测试
@Test
public void test5() {
List<User> userList = new ArrayList<>();
userList.add(new User("小蟹", 1));
userList.add(new User("小沈", 1));
userList.add(new User("小丁", 11));
// 重复元素
userList.add(new User("claw", 1));
userList.add(new User("claw", 1));
// distinct 去除重复元素
userList.stream().distinct().forEach(new Consumer<User>() {
@Override
public void accept(User user) {
System.out.println(user);
}
});
// lambda表达式的方式
userList.stream().distinct().forEach(System.out::println);
}
实例(三)limit()和skip()方法测试,写到这里就觉得Lambda表达式的方便快捷了。
@Test
public void test6() {
List<User> userList = new ArrayList<>();
userList.add(new User("小蟹", 1));
userList.add(new User("小沈", 1));
userList.add(new User("小丁", 11));
// limit()只展示一个元素 输出 结果: User(name=小蟹, age=1)
userList.stream().limit(1).forEach(System.out::println);
// skip()跳过第一个元素 输出结果: User(name=小沈, age=1)
//User(name=小丁, age=11)
userList.stream().skip(1).forEach(System.out::println);
}
Stream的中间操作 - 映射
方法 | 描述 |
map(Function t) | 接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素 |
flatMap(Function f) | 接受一个函数作为参数,将流中的每一个值都换成另一个流,然后把所有流连接成一个流 |
实例(一) map()方法的使用
@Test
public void mapTest() {
// map(Function t)接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
// 匿名实现类写法
list.stream().map(new Function<String, Object>() {
@Override
public Object apply(String s) {
// 将列表的字符转换为大写
return s.toUpperCase();
}
}).forEach(System.out::println);
// Lambda表达式写法
list.stream().map(e->e.toUpperCase()).forEach(System.out::println);
}
Stream的中间操作 - 排序
(待完结)
Stream 的终止操作
(待完结)