理解Stream流的关键在于明确每一个方法的参数(在对流操作的方法中大多数需要传入一个接口)!
一、常用的函数式接口
1、Consumer接口(消费者)
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
这里可以忽略default方法(同为jdk8的新特性)但与本次内容无关,只关注函数式接口的唯一抽象方法accept(T t)
这个方法接受一个参数,并做无返回值的一些处理,相当于一个消费者,吸收了一个对象做了各种处理然后吞掉。
1.1Where use Consumer?
最常用的:Stream流操作中的foreach()
常见于list.stream().foreach(System.out::println) <=> list.stream().foreach((a)->System.out.print(a))
所以你可以看出,为什么foreach中要这么写,因为它需要一个Consumer接口。(看上去是句废话,但是对第一次看到这样写法的人,这句话是理解问题的关键)
2、Predicate接口(我愿意叫他仲裁者)
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
default Predicate<T> negate() {
return (t) -> !test(t);
}
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
老规矩只看唯一的抽象方法。这个接口功能很简单,你传一个值,我返回一个true或false。
2.1Where use Predicate?
这个接口说起来你感觉很迷茫,但是只要一用,立刻就会懂了比如Stream的filter方法:
Stream<T> filter(Predicate<? super T> predicate);
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("");
list.add(null);
list.add("test");
List<String> res = list.stream().filter((str)->str != null &&
!str.isEmpty()).collect(Collectors.toList());
System.out.println(res);
}
如果filter中的 Predicate接口的test方法返回true意味着这个数据不会被过滤,如果false则过滤掉。这个方法会经常出现因为筛选数据很方便,顺便一提stream中的方法几乎都自带了遍历所以不要奇怪为什么filter会过滤整个list。
3、Function接口(我愿意叫他转换者)
public interface Function<T, R> {
R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static <T> Function<T, T> identity() {
return t -> t;
}
}
老规矩不说,这个apply方法要注意它的泛型,它的意思就是你传入一个值,我可以转换成别的类型的值,当然可以还是转换为原来类型这个就没人拦着了。
3.1Where use Function?
这个接口在Stream操作中用的也极为普遍,原因是map方法真的太好用了
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
public static void main(String[] args) {
List<Person> personList = GeneratorPersonList();
List<String> nameList = personList.stream().map((person)->person.getName()).collect(Collectors.toList());
System.out.println(personList);
System.out.println(nameList);
}
忽略GeneratorPersonList(),这只是我自己编写的一个生成测试list用的(没什么技术含量)就知道这是一个Person的List
Person类(诺,就是个实体类):
public class Person {
private long id;
private String name;
private int age;
private String job;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", job='" + job + '\'' +
'}';
}
}
于是,上面代码的意思就是遍历这个全是Person的List将其中的每一个元素Person对象都变成Person中的属性name,最后用collect方法生成一个新的list。(collect方法后面的章节会提到现在就理解为生成链式操作之后的list就好了)。
怎么样现在有没有感受到Stream的魅力,对于我这样接触不深的人来说,用了几遍之后的感觉就是,它是一个强大的清洗于数据处理工具,用于容器,让你省略了重复的遍历代码,清爽!