一:函数型接口
函数式接口泛指那些在有且仅有一个抽象方法的接口,其标志特点是源码中含有@FunctionalInterface
注解。
名称 | 定位 |
---|---|
Function | 函数型接口 |
Predicate | 断定型接口 |
Consumer | 消费型接口 |
Supplier | 供给型接口 |
1.函数型接口(Function )
Function<T, R>,T是一个传入的参数类型,R是一个返回的参数类型,函数式接口只有一个抽象方法,在这里是apply,apply方法有返回值,其类型是R。
//函数式接口Function
Function<String,Boolean> function = (str)->{
return str.isEmpty();
};
System.out.println("函数式接口Function "+function.apply("adasdasd"));
打印结果:
函数式接口Function false
2.断定式接口(Predicate)
Predicate< T>只有一个用户的传入类型T,唯一的一个抽象方法为test,其固定返回类型为Boolean。
//断定式接口Predicate,返回只能是布尔
Predicate<Integer> predicate = (nu)->{
return nu>0;
};
System.out.println("函数式接口Predicate"+predicate.test(20));
运行结果:
函数式接口Predicate true
3.消费型接口(Consumer)
Consumer< T>,唯一指定参数T,唯一抽象方法为accept,无返回值。
//消费型接口
Consumer<String> consumer = (c1)->{
System.out.println(c1);
};
consumer.accept("消费型接口");
运行结果:
消费型接口
4.供给型接口(Supplier)
Supplier< T>唯一指定参数类型T,唯一抽象方法为get(),其返回类型为手动指定的T。
//供给型接口
Supplier<String> supplier = ()->{
return "gety";
};
System.out.println(supplier.get());
运行结果:
gety
二:Stream流计算
1.存在一个User实体类:
package Stream;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String name;
private Integer age;
}
2.在测试类中连续声明对应的五个实体类
User user1 = new User(1,"a",23);
User user2 = new User(2,"b",21);
User user3 = new User(3,"c",29);
User user4 = new User(4,"d",22);
User user5 = new User(5,"e",18);
问题1:输出id为偶数的User
public void test2(){
User user1 = new User(1,"a",23);
User user2 = new User(2,"b",21);
User user3 = new User(3,"c",29);
User user4 = new User(4,"d",22);
User user5 = new User(5,"e",18);
List<User> list = Arrays.asList(user1,user2,user3,user4,user5);
//用流来计算
list.stream()
.filter(user -> {return user.getId()%2==0;})
.forEach(System.out::println);
}
运行结果:
User(id=2, name=b, age=21)
User(id=4, name=d, age=22)
其中,.stream()是生成流,而具体的.filter中之所以可以填一个lambada表达式,是因为.filter的底层就是一个断定型接口。
问题2:继续在问题1的基础上输出年龄大于21岁的用户
public void test2(){
User user1 = new User(1,"a",23);
User user2 = new User(2,"b",21);
User user3 = new User(3,"c",29);
User user4 = new User(4,"d",22);
User user5 = new User(5,"e",18);
List<User> list = Arrays.asList(user1,user2,user3,user4,user5);
//用流来计算
list.stream()
.filter(user -> {return user.getId()%2==0;})
.filter(user -> {return user.getAge()>21;})
.forEach(System.out::println);
}
运行结果:
User(id=4, name=d, age=22)
问题3:在上一个问题的基础上将用户名大写输出
public void test2(){
User user1 = new User(1,"a",23);
User user2 = new User(2,"b",21);
User user3 = new User(3,"c",29);
User user4 = new User(4,"d",22);
User user5 = new User(5,"e",18);
List<User> list = Arrays.asList(user1,user2,user3,user4,user5);
//用流来计算
list.stream()
.filter(user -> {return user.getId()%2==0;})
.filter(user -> {return user.getAge()>21;})
.map(user -> {return user.getName().toUpperCase();})
.forEach(System.out::println);
}
运行结果:
D
至于这里为什么返回的是一个字符而不是一个User对象,是因为Map底层是一个函数式接口,而函数式接口的T和R可以是两个不同的类型。
第一个参数指定了对象user,但return 的user.getName().toUpperCase()
是一个字符串,所以就返回了一个字符串。
问题4:假如最终结果是多个值,我只想输出一个怎么办
改变一下,输出年龄大于20的
public void test2(){
User user1 = new User(1,"a",23);
User user2 = new User(2,"b",21);
User user3 = new User(3,"c",29);
User user4 = new User(4,"d",22);
User user5 = new User(5,"e",18);
List<User> list = Arrays.asList(user1,user2,user3,user4,user5);
//用流来计算
list.stream()
.filter(user -> {return user.getId()%2==0;})
.filter(user -> {return user.getAge()>20;})
.map(user -> {return user.getName().toUpperCase();})
.forEach(System.out::println);
}
运行结果:
B
D
然后最终只取一个
public void test2(){
User user1 = new User(1,"a",23);
User user2 = new User(2,"b",21);
User user3 = new User(3,"c",29);
User user4 = new User(4,"d",22);
User user5 = new User(5,"e",18);
List<User> list = Arrays.asList(user1,user2,user3,user4,user5);
//用流来计算
list.stream()
.filter(user -> {return user.getId()%2==0;})
.filter(user -> {return user.getAge()>20;})
.map(user -> {return user.getName().toUpperCase();})
.limit(1)
.forEach(System.out::println);
}
运行结果:
B
问题5:假如最终结果是多个值,我想倒叙排列后只输出一个怎么办
public void test2(){
User user1 = new User(1,"a",23);
User user2 = new User(2,"b",21);
User user3 = new User(3,"c",29);
User user4 = new User(4,"d",22);
User user5 = new User(5,"e",18);
List<User> list = Arrays.asList(user1,user2,user3,user4,user5);
//用流来计算
list.stream()
.filter(user -> {return user.getId()%2==0;})
.filter(user -> {return user.getAge()>20;})
.map(user -> {return user.getName().toUpperCase();})
.sorted((uu1,uu2)->{return uu2.compareTo(uu1);})
.limit(1)
.forEach(System.out::println);
}
运行结果:
D
如果我们采用流计算来执行从0加到1_0000_0000的和,那这种方式所消耗的时间将只会是无脑for循环的十甚至几十分之一,效率极高。
@Test
public void test3(){
long start = System.currentTimeMillis();
Long sum = LongStream.rangeClosed(0L,1_0000_0000L).parallel().reduce(0,Long::sum);
long end = System.currentTimeMillis();
System.out.println("计算结果"+sum+"计算时间"+(end-start));
}
运行结果:
计算结果5000000050000000计算时间177
对比,当然,如果计算量越大,流计算的优势就越明显。
@Test
public void test4(){
long start = System.currentTimeMillis();
Long sum = 0L;
for (long i = 0L; i < 1_0000_0000L; i++) {
sum+=i;
}
long end = System.currentTimeMillis();
System.out.println("计算结果"+sum+"计算时间"+(end-start));
}
运行结果:
计算结果4999999950000000计算时间931