【Java8新特性】Stream API有哪些中間操做?看完你也能夠吊打面試官!!

寫在前面

在上一篇《【Java8新特性】面試官問我:Java8中建立Stream流有哪幾種方式?》中,一名讀者去面試被面試官暴虐!歸根結底,那哥兒們仍是對Java8的新特性不是很瞭解呀!那麼,咱們繼續講述Java8的新特性,旨在最終可讓每位讀者在跳槽面試的過程當中吊打面試官!!html

Stream的中間操做

多箇中間操做能夠鏈接起來造成一個流水線,除非流水線上觸發終止操做,不然中間操做不會執行任何的處理!而在終止操做時一次性所有處理,稱爲「惰性求值」 。 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對象的集合進行操做。好了,咱們開始具體的操做案例。函數

1.filter()方法

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());
}

2.limit()方法

主要做用爲:截斷流,使其元素不超過給定數量。

先來看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條符合條件得元素,則不會繼續往下迭代數據,能夠提升效率。

3.skip()方法

跳過元素,返回一個扔掉了前 n 個元素的流。若流中元素 不足 n 個,則返回一個空流。與 limit(n) 互補。

源碼定義以下所示。

Stream<T> skip(long n);

源碼定義比較簡單,一樣只須要傳入一個long類型的數字便可。其含義是跳過n個元素。

簡單示例以下所示。

//跳過前2個值
list.stream().skip(2).forEach(System.out :: println);

4.distinct()方法

篩選,經過流所生成元素的 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) 接收一個函數做爲參數,將流中的每一個值都換成另 一個流,而後把全部流鏈接成一個流

1.map()方法

接收一個函數做爲參數,該函數會被應用到每一個元 素上,並將其映射成一個新的元素。

先來看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());

2.flatMap()

接收一個函數做爲參數,將流中的每一個值都換成另 一個流,而後把全部流鏈接成一個流。

先來看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新特性時少走彎路。

img

相關文章
相關標籤/搜索