Java8之Stream流(一)基礎(chǔ)體驗(yàn)
Java8之Stream流(三)縮減操作
Java8之Stream流(四)并行流
Java8之Stream流(五)映射流
Java8之Stream流(六)收集
Java8之Stream流(七)流與迭代器
我們的第一篇文章役电,主要是通過一個Demo姥饰,讓大家體驗(yàn)了一下使用流API的那種酣暢淋漓的感覺鄙陡。如果你沒有實(shí)踐轰枝,我還是再次呼吁你動手敲一敲碉考,自己實(shí)實(shí)在跑一遍上一篇的Demo导绷。相信你的感受和理解也會隨之加深的累魔。繼續(xù)探索流API的高級功能之前,我們先從接口級別全面了解一下流API副女,這個對于我們來說是至關(guān)重要的蛤高。但這一篇不需要花太多時(shí)間,直接看一遍就可以了碑幅。接下來戴陡,我給大家準(zhǔn)備了一張流API關(guān)鍵知識點(diǎn)的UML圖。但是大家只需要花一兩分鐘沟涨,整理看一下就可以了恤批,不需要記住,先有個印象裹赴,后面我給大家講解一些關(guān)鍵的方法:
流API UML
我先整體介紹一下:流API定義的幾個接口喜庞,都是在java.util.stream包中的.其中上圖中的BaseStream接口是最基礎(chǔ)的接口,它提供了所有流都可以使用的基本功能:
public interface BaseStream<T, S extends BaseStream<T, S>> extends AutoCloseable {
//....先忽略這些具體的細(xì)節(jié)
}
由這個接口的定義我們得知棋返,BaseStream是一個泛型接口,它有兩個類型參數(shù)T和S延都, 其中T指定了流中的元素的類型,并且由<S extends BaseStream<T, S>>可以知道S必須為BaseStream或BaseStream子類(換句話說,就是S必須是擴(kuò)展自BaseStream的)。BaseStream繼承了AutoCloseable接口懊昨。這個接口主要是簡化了關(guān)閉資源的操作窄潭。但是像平時(shí)我們操作的集合或數(shù)組春宣,基本上都不會出現(xiàn)關(guān)閉流的情況酵颁。
//由BaseStream接口派生出的流接口包括IntStream ,LongStream月帝,DoubleStream 躏惋,Stream<T>
public interface IntStream extends BaseStream<Integer, IntStream> {
}
public interface LongStream extends BaseStream<Long, LongStream> {
}
public interface DoubleStream extends BaseStream<Double, DoubleStream> {
}
//這是最具代表性的接口
public interface Stream<T> extends BaseStream<T, Stream<T>> {
//....先忽略這些具體的細(xì)節(jié)
}
由于Stream接口是最具代表性的,所以我們就選擇它來講解嚷辅,其實(shí)在我們學(xué)完Stream接口簿姨,其它的三個接口,在使用上基本是一致的了,我們上一篇的Demo基本上也是使用Stream接口來做的練習(xí)扁位。我們回想一下:在上一個Demo中我們通過集合框架的stream()方法准潭,就能返回一個流了,它的返回類型就是Stream<T>域仇,比如我們Stream<Integer>刑然,由此得知,Stream接口里的類型參數(shù)T就是流中的元素的類型暇务。木有錯泼掠,就是這樣滴。到這里垦细,整個系列你們已經(jīng)全部學(xué)會了择镇,下課。
戰(zhàn)斗才剛剛開始括改!
現(xiàn)在是時(shí)候開始記憶一些知識了.
BaseStream詳解:
public interface BaseStream<T, S extends BaseStream<T, S>> extends AutoCloseable {
Iterator<T> iterator();//line2
Spliterator<T> spliterator();//line3
boolean isParallel();//line4
S sequential();//line5
S parallel();//line6
S unordered();//line7
S onClose(Runnable closeHandler);//line8
@Override
void close();//line10
}
-
Iterator<T> iterator();
//line2
獲得流的迭代器腻豌,并返回對該迭代器的引用(終端操作) -
Spliterator<T> spliterator();
//line3
獲取流的spliterator,并返回其引用(終端操作) -
boolean isParallel();
//line4
如果調(diào)用流是一個并行流嘱能,則返回true;如果調(diào)用流是一個順序流饲梭,則返回false。 -
S sequential();
//line5
基于調(diào)用流焰檩,返回一個順序流憔涉。如果調(diào)用流已經(jīng)是順序流了,就返回該流析苫。(中間操作) -
S parallel();
//line6
基于調(diào)用流兜叨,返回一個并行流。如果調(diào)用流已經(jīng)是并行流了衩侥,就返回該流国旷。(中間操作) -
S unordered();
//line7
基于調(diào)用流,返回一個無序流茫死。如果調(diào)用流已經(jīng)是無序流了跪但,就返回該流。(中間操作) -
S onClose(Runnable closeHandler);
//line8
返回一個新流峦萎,closeHandler指定了該流的關(guān)閉處理程序屡久,當(dāng)關(guān)閉該流時(shí),將調(diào)用這個處理程序爱榔。(中間操作) -
void close();
//line10
從AutoCloseable繼承來的被环,調(diào)用注冊關(guān)閉處理程序,關(guān)閉調(diào)用流(很少會被使用到)
"終端操作"&"中間操作"
細(xì)心的同學(xué)應(yīng)該注意到了详幽,BaseStream接口里面的很多方法都在最后標(biāo)識了(終端操作)和(中間操作)筛欢,它們之間的區(qū)別是非常重要的浸锨。
終端操作 會消費(fèi)流,這種操作會產(chǎn)生一個結(jié)果的版姑,比如上面的 iterator()和 spliterator()柱搜,以及上一篇中提到的min()和max(),或者是執(zhí)行某一種操作剥险,比如上一篇的forEach()冯凹,如果一個流被消費(fèi)過了,那它就不能被重用的炒嘲。
中間操作 中間操作會產(chǎn)生另一個流宇姚。因此中間操作可以用來創(chuàng)建執(zhí)行一系列動作的管道。一個特別需要注意的點(diǎn)是:中間操作不是立即發(fā)生的夫凸。相反浑劳,當(dāng)在中間操作創(chuàng)建的新流上執(zhí)行完終端操作后,中間操作指定的操作才會發(fā)生夭拌。所以中間操作是延遲發(fā)生的魔熏,中間操作的延遲行為主要是讓流API能夠更加高效地執(zhí)行。
"中間操作"的狀態(tài)
流的中間操作鸽扁,可以為分無狀態(tài)操作
和有狀態(tài)操作
兩種蒜绽,在無狀態(tài)操作中,在處理流中的元素時(shí)桶现,會對當(dāng)前的元素進(jìn)行單獨(dú)處理躲雅。比如:謂詞過濾操作,因?yàn)槊總€元素都是被單獨(dú)進(jìn)行處理的骡和,所有它和流中的其它元素?zé)o關(guān)相赁,因此被稱為無狀態(tài)操作;而在有狀態(tài)操作中,某個元素的處理可能依賴于其他元素慰于。比如查找最小值钮科,最大值,和排序婆赠,因?yàn)樗麄兌家蕾囉谄渌脑孛喔R虼藶榉Q為有狀態(tài)操作。當(dāng)需要進(jìn)行并行處理流時(shí)休里,有狀態(tài)的操作和無狀態(tài)的區(qū)別是非常重要的蛆挫,因?yàn)橛袪顟B(tài)操作可能需要幾次處理才能完成,后面的文章我將會給大家詳細(xì)地講份帐,現(xiàn)在只需要正常學(xué)習(xí)下去就可以了
另外璃吧,指出一點(diǎn)楣导,如果大家了解泛型的話废境,應(yīng)該知道,泛型的類型參數(shù)只能是引用類型,因此Stream操作的對象只能是引用類型的噩凹,不能用于基本類型巴元。當(dāng)然官方早已考慮到這一點(diǎn)了,前面你們看到的IntStream驮宴,LongStream逮刨,DoubleStream就是官方給我們提供的處理基本類型的流了。此處是不是應(yīng)該給他們掌聲堵泽!
Stream詳解
在有了前面的那些知識作鋪墊之后修己,學(xué)Stream接口應(yīng)該會順風(fēng)順?biāo)恕_€是先看看Stream的詳情先:
public interface Stream<T> extends BaseStream<T, Stream<T>> {
Stream<T> filter(Predicate<? super T> predicate);//line2
<R> Stream<R> map(Function<? super T, ? extends R> mapper);//line3
IntStream mapToInt(ToIntFunction<? super T> mapper);//line4
LongStream mapToLong(ToLongFunction<? super T> mapper);
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper);
LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper);
DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper);
Stream<T> distinct();
Stream<T> sorted();//line12
Stream<T> sorted(Comparator<? super T> comparator);//line13
Stream<T> peek(Consumer<? super T> action);
Stream<T> limit(long maxSize);
Stream<T> skip(long n);
void forEach(Consumer<? super T> action);//line17
void forEachOrdered(Consumer<? super T> action);//line18
Object[] toArray();
<A> A[] toArray(IntFunction<A[]> generator);
T reduce(T identity, BinaryOperator<T> accumulator);
Optional<T> reduce(BinaryOperator<T> accumulator);
<U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
BinaryOperator<U> combiner);
<R> R collect(Supplier<R> supplier,
BiConsumer<R, ? super T> accumulator,
BiConsumer<R, R> combiner);
<R, A> R collect(Collector<? super T, A, R> collector);
Optional<T> min(Comparator<? super T> comparator);//line30
Optional<T> max(Comparator<? super T> comparator);//line31
long count();
boolean anyMatch(Predicate<? super T> predicate);
boolean allMatch(Predicate<? super T> predicate);
boolean noneMatch(Predicate<? super T> predicate);
Optional<T> findFirst();
Optional<T> findAny();
// Static factories
public static<T> Builder<T> builder() {
return new Streams.StreamBuilderImpl<>();
}
public static<T> Stream<T> empty() {
return StreamSupport.stream(Spliterators.<T>emptySpliterator(), false);
}
public static<T> Stream<T> of(T t) {
return StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false);
}
@SafeVarargs
@SuppressWarnings("varargs") // Creating a stream from an array is safe
public static<T> Stream<T> of(T... values) {
return Arrays.stream(values);
}
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {
Objects.requireNonNull(f);
final Iterator<T> iterator = new Iterator<T>() {
@SuppressWarnings("unchecked")
T t = (T) Streams.NONE;
@Override
public boolean hasNext() {
return true;
}
@Override
public T next() {
return t = (t == Streams.NONE) ? seed : f.apply(t);
}
};
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
iterator,
Spliterator.ORDERED | Spliterator.IMMUTABLE), false);
}
public static<T> Stream<T> generate(Supplier<T> s) {
Objects.requireNonNull(s);
return StreamSupport.stream(
new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s), false);
}
public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) {
Objects.requireNonNull(a);
Objects.requireNonNull(b);
@SuppressWarnings("unchecked")
Spliterator<T> split = new Streams.ConcatSpliterator.OfRef<>(
(Spliterator<T>) a.spliterator(), (Spliterator<T>) b.spliterator());
Stream<T> stream = StreamSupport.stream(split, a.isParallel() || b.isParallel());
return stream.onClose(Streams.composedClose(a, b));
}
}
-
Stream<T> filter(Predicate<? super T> predicate);
//line2
產(chǎn)生一個新流迎罗,其中包含調(diào)用流中滿足predicate指定的謂詞元素(中間操作) -
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
//line3
產(chǎn)生一個新流睬愤,對調(diào)用流中的元素應(yīng)用mapper,新流中包含這些元素纹安。(中間操作) -
IntStream mapToInt(ToIntFunction<? super T> mapper);
//line4
對調(diào)用流中元素應(yīng)用mapper尤辱,產(chǎn)生包含這些元素的一個新IntStream流。(中間操作) -
Stream<T> sorted();
//line12
-
Stream<T> sorted(Comparator<? super T> comparator);
//line13
`
產(chǎn)生一個自然順序排序或者指定排序條件的新流(中間操作) -
void forEach(Consumer<? super T> action);
//line17
-
void forEachOrdered(Consumer<? super T> action);
//line18
遍歷了流中的元素(終端操作) -
Optional<T> min(Comparator<? super T> comparator)
//line30
-
Optional<T> max(Comparator<? super T> comparator);
//line31
獲得流中最大最小值厢岂,比較器可以由自己定義光督,也可以使用JDK提供的(終端操作)
小結(jié)一下
這一篇主要是介紹了流API的一些關(guān)鍵方法,和一些關(guān)鍵的概念塔粒,雖然稍微枯燥了一點(diǎn)结借,但是,不能否認(rèn)卒茬,全面地學(xué)習(xí)流API映跟,會讓你對流API的認(rèn)識會更加的深刻,所以如果時(shí)間允許扬虚,請?jiān)僬J(rèn)真讀讀這一篇文章吧努隙,當(dāng)然,也可以在實(shí)踐中慢慢認(rèn)識它們辜昵,但是荸镊,對于這些基本概念的知識,你越早掌握堪置,對你的益處是更加大的躬存。到此為止,流API的基礎(chǔ)知識已經(jīng)學(xué)完了舀锨,后面的幾篇文章我們就要開始更加深入地理解和運(yùn)用他們實(shí)現(xiàn)一起強(qiáng)有力的功能了岭洲!