作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO
联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬
一个需求
List<Person> list = new ArrayList<>();
list.add(new Person("李健", 46));
list.add(new Person("周深", 28));
list.add(new Person("张学友", 59));
请筛选出年龄大于40的歌手的名字
请大家先自行实现这个需求。
我的实现方式
我写了一个山寨版的Stream API,实现了其中的filter()和map(),底层原理稍有不同,仅做知识引入。
乍一看很麻烦,好像还不如直接写各种for,但我是为了复用,所以抽取了MyList类。
要想实现上面的编码形式,需要两块砖:
- 链式编程
- 把Lambda表达式作为接口实例传递(必须是函数式接口)
关于Lambda表达式,请参考《Lambda表达式》。
链式编程简单来说就是一个方法返回引用本身:
public class ChainCall {
public static void main(String[] args) {
Person bravo1988 = new Person().setName("mx").setAge(18).setMoney(1000.0);
}
}
class Person{
private String name;
private Integer age;
private Double money;
public String getName() {
return name;
}
public Integer getAge() {
return age;
}
public Double getMoney() {
return money;
}
public Person setName(String name) {
this.name = name;
// set方法要返回this,方便继续调用下一个set
return this;
}
public Person setAge(Integer age) {
this.age = age;
return this;
}
public Person setMoney(Double money) {
this.money = money;
return this;
}
}
OK,交代完毕,接下来请大家把我的代码拷贝到本地运行看看:
/**
* @author mx
*/
public class MockStream {
public static void main(String[] args) throws JsonProcessingException {
MyList<Person> personMyList = new MyList<>();
personMyList.add(new Person("李健", 46));
personMyList.add(new Person("周深", 28));
personMyList.add(new Person("张学友", 59));
// 需求:过滤出年龄大于40的歌手的名字
MyList<String> result = personMyList.filter(person -> person.getAge() > 40).map(Person::getName);
prettyPrint(result.getList());
System.out.println("\n---------------------------------------------\n");
// 对比真正的Stream API
List<Person> list = new ArrayList<>();
list.add(new Person("李健", 46));
list.add(new Person("周深", 28));
list.add(new Person("张学友", 59));
List<String> collect = list
.stream() // 真正的Stream API需要先转成stream流
.filter(person -> person.getAge() > 40) // 过滤出年纪大于40的歌手
.map(Person::getName) // 拿到他们的名字
.collect(Collectors.toList()); // 整理成List<String>
prettyPrint(collect);
}
/**
* 按JSON格式输出
*
* @param obj
* @throws JsonProcessingException
*/
private static void prettyPrint(Object obj) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
String s = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
System.out.println(s);
}
}
@Data
@AllArgsConstructor
class Person {
private String name;
private Integer age;
}
@Getter
class MyList<T> {
private List<T> list = new ArrayList<>();
public boolean add(T t) {
return list.add(t);
}
/**
* 给MyList传递具体的判断规则,然后MyList把内部现有符合判断(true)的元素集返回
* @param predicate
* @return
*/
public MyList<T> filter(Predicate<T> predicate){
MyList<T> filteredList = new MyList<>();
for (T t : list) {
if (predicate.test(t)) {
// 收集判断为true的元素
filteredList.add(t);
}
}
return filteredList;
}
/**
* 把MyList中的List<T>转为List<R>
*
* @param mapper
* @param <R>
* @return
*/
public <R> MyList<R> map(Function<T, R> mapper) {
MyList<R> mappedList = new MyList<>();
for (T t : list) {
mappedList.add(mapper.apply(t));
}
return mappedList;
}
}
/**
* 定义一个Predicate接口,名字无所谓
*
* @param <T>
*/
@FunctionalInterface
interface Predicate<T> {
/**
* 定义了一个test()方法,传入任意对象,返回true or false,具体判断逻辑由子类实现
*
* @param t
* @return
*/
boolean test(T t);
}
/**
* 定义一个Function接口,名字无所谓
*
* @param <E>
* @param <R>
*/
@FunctionalInterface
interface Function<E, R> {
/**
* 定义一个apply()方法,接收一个E返回一个R。也就是把E映射成R
*
* @param e
* @return
*/
R apply(E e);
}
看不懂没关系,感受一下即可。
下一篇会带着大家一步步分析上面的代码是怎么写出来的。
正所谓画龙画虎难画骨,山寨Stream API毕竟只是山寨版。
MyList虽然很像Stream API,但本质还是对List进行外部迭代,而正版的Stream API是先产生Stream流,再通过流迭代,属于内部迭代。两者其实存在本质区别。
本文只是借助这个例子切入Stream API的学习,你们也不可能在实际项目中放着正版Stream API不用,而拷贝我的山寨版。
作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO
进群,大家一起学习,一起进步,一起对抗互联网寒冬