在上一篇《【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新特性時少走彎路。