在上一篇《【Java8新特性】面试官问我:Java8中建立Stream流有哪几种方式?》中,一名读者去面试被面试官暴虐!归根结底,那哥儿们仍是对Java8的新特性不是很了解呀!那么,咱们继续讲述Java8的新特性,旨在最终可让每位读者在跳槽面试的过程当中吊打面试官!!html
多个中间操做能够链接起来造成一个流水线,除非流水线上触发终止操做,不然中间操做不会执行任何的处理!而在终止操做时一次性所有处理,称为“惰性求值” 。 Stream的中间操做是不会有任何结果数据输出的。java
Stream的中间操做在总体上能够分为:筛选与切片、映射、排序。接下来,咱们就分别对这些中间操做进行简要的说明。面试
这里,我将与筛选和切片有关的操做整理成以下表格。api
方法 | 描述 |
---|---|
filter(Predicate p) | 接收Lambda表达式,从流中排除某些元素 |
distinct() | 筛选,经过流所生成元素的 hashCode() 和 equals() 去 除重复元素 |
limit(long maxSize) | 截断流,使其元素不超过给定数量 |
skip(long n) | 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素 不足 n 个,则返回一个空流。与 limit(n) 互补 |
接下来,咱们列举几个简单的示例,以便加深理解。数组
为了更好的测试程序,我先构造了一个对象数组,以下所示。微信
protected List<Employee> list = Arrays.asList( new Employee("张三", 18, 9999.99), new Employee("李四", 38, 5555.55), new Employee("王五", 60, 6666.66), new Employee("赵六", 8, 7777.77), new Employee("田七", 58, 3333.33) );
其中,Employee类的定义以下所示。app
@Data @Builder @ToString @NoArgsConstructor @AllArgsConstructor public class Employee implements Serializable { private static final long serialVersionUID = -9079722457749166858L; private String name; private Integer age; private Double salary; }
Employee类的定义比较简单,这里,我就不赘述了。以后的示例中,咱们都是使用的Employee对象的集合进行操做。好了,咱们开始具体的操做案例。函数
filter()方法主要是用于接收Lambda表达式,从流中排除某些元素,其在Stream接口中的源码以下所示。学习
Stream<T> filter(Predicate<? super T> predicate);
能够看到,在filter()方法中,须要传递Predicate接口的对象,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); } }
能够看到,Predicate是一个函数式接口,其中接口中定义的主要方法为test()方法,test()方法会接收一个泛型对象t,返回一个boolean类型的数据。
看到这里,相信你们明白了:filter()方法是根据Predicate接口的test()方法的返回结果来过滤数据的,若是test()方法的返回结果为true,符合规则;若是test()方法的返回结果为false,则不符合规则。
这里,咱们可使用下面的示例来简单的说明filter()方法的使用方式。
//内部迭代:在此过程当中没有进行过迭代,由Stream api进行迭代 //中间操做:不会执行任何操做 Stream<Person> stream = list.stream().filter((e) -> { System.out.println("Stream API 中间操做"); return e.getAge() > 30; });
咱们,在执行终止语句以后,一边迭代,一边打印,而咱们并无去迭代上面集合,其实这是内部迭代,由Stream API 完成。
下面咱们来看看外部迭代,也就是咱们人为得迭代。
//外部迭代 Iterator<Person> it = list.iterator(); while (it.hasNext()) { System.out.println(it.next()); }
主要做用为:截断流,使其元素不超过给定数量。
先来看limit方法的定义,以下所示。
Stream<T> limit(long maxSize);
limit()方法在Stream接口中的定义比较简单,只须要传入一个long类型的数字便可。
咱们能够按照以下所示的代码来使用limit()方法。
//过滤以后取2个值 list.stream().filter((e) -> e.getAge() >30 ).limit(2).forEach(System.out :: println);
在这里,咱们能够配合其余得中间操做,并截断流,使咱们能够取得相应个数得元素。并且在上面计算中,只要发现有2条符合条件得元素,则不会继续往下迭代数据,能够提升效率。
跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素 不足 n 个,则返回一个空流。与 limit(n) 互补。
源码定义以下所示。
Stream<T> skip(long n);
源码定义比较简单,一样只须要传入一个long类型的数字便可。其含义是跳过n个元素。
简单示例以下所示。
//跳过前2个值 list.stream().skip(2).forEach(System.out :: println);
筛选,经过流所生成元素的 hashCode() 和 equals() 去 除重复元素。
源码定义以下所示。
Stream<T> distinct();
旨在对流中的元素进行去重。
咱们能够以下面的方式来使用disinct()方法。
list.stream().distinct().forEach(System.out :: println);
这里有一个须要注意的地方:distinct 须要实体中重写hashCode()和 equals()方法才可使用。
关于映射相关的方法以下表所示。
方法 | 描述 |
---|---|
map(Function f) | 接收一个函数做为参数,该函数会被应用到每一个元 素上,并将其映射成一个新的元素。 |
mapToDouble(ToDoubleFunction f) | 接收一个函数做为参数,该函数会被应用到每一个元 素上,产生一个新的 DoubleStream。 |
mapToInt(ToIntFunction f) | 接收一个函数做为参数,该函数会被应用到每一个元 素上,产生一个新的 IntStream。 |
mapToLong(ToLongFunction f) | 接收一个函数做为参数,该函数会被应用到每一个元 素上,产生一个新的 LongStream |
flatMap(Function f) | 接收一个函数做为参数,将流中的每一个值都换成另 一个流,而后把全部流链接成一个流 |
接收一个函数做为参数,该函数会被应用到每一个元 素上,并将其映射成一个新的元素。
先来看Java8中Stream接口对于map()方法的声明,以下所示。
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
咱们能够按照以下方式使用map()方法。
//将流中每个元素都映射到map的函数中,每一个元素执行这个函数,再返回 List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd"); list.stream().map((e) -> e.toUpperCase()).forEach(System.out::printf); //获取Person中的每个人得名字name,再返回一个集合 List<String> names = this.list.stream().map(Person :: getName).collect(Collectors.toList());
接收一个函数做为参数,将流中的每一个值都换成另 一个流,而后把全部流链接成一个流。
先来看Java8中Stream接口对于flatMap()方法的声明,以下所示。
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
咱们可使用以下方式使用flatMap()方法,为了便于你们理解,这里,我就贴出了测试flatMap()方法的全部代码。
/** * flatMap —— 接收一个函数做为参数,将流中的每一个值都换成一个流,而后把全部流链接成一个流 */ @Test public void testFlatMap () { StreamAPI_Test s = new StreamAPI_Test(); List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd"); list.stream().flatMap((e) -> s.filterCharacter(e)).forEach(System.out::println); //若是使用map则须要这样写 list.stream().map((e) -> s.filterCharacter(e)).forEach((e) -> { e.forEach(System.out::println); }); } /** * 将一个字符串转换为流 */ public Stream<Character> filterCharacter(String str){ List<Character> list = new ArrayList<>(); for (Character ch : str.toCharArray()) { list.add(ch); } return list.stream(); }
其实map方法就至关于Collaction的add方法,若是add的是个集合得话就会变成二维数组,而flatMap 的话就至关于Collaction的addAll方法,参数若是是集合得话,只是将2个集合合并,而不是变成二维数组。
关于排序相关的方法以下表所示。
方法 | 描述 |
---|---|
sorted() | 产生一个新流,其中按天然顺序排序 |
sorted(Comparator comp) | 产生一个新流,其中按比较器顺序排序 |
从上述表格能够看出:sorted有两种方法,一种是不传任何参数,叫天然排序,还有一种须要传Comparator 接口参数,叫作定制排序。
先来看Java8中Stream接口对于sorted()方法的声明,以下所示。
Stream<T> sorted(); Stream<T> sorted(Comparator<? super T> comparator);
sorted()方法的定义比较简单,我就再也不赘述了。
咱们也能够按照以下方式来使用Stream的sorted()方法。
// 天然排序 List<Employee> persons = list.stream().sorted().collect(Collectors.toList()); //定制排序 List<Employee> persons1 = list.stream().sorted((e1, e2) -> { if (e1.getAge() == e2.getAge()) { return 0; } else if (e1.getAge() > e2.getAge()) { return 1; } else { return -1; } }).collect(Collectors.toList());
若是以为文章对你有点帮助,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习Java8新特性。
最后,附上Java8新特性核心知识图,祝你们在学习Java8新特性时少走弯路。