重走Java基礎(chǔ)之Streams(三)

來(lái)源:重走Java基礎(chǔ)之Streams(三)
作者:知秋(極樂(lè)科技知乎專欄原創(chuàng)作者)
博客:一葉知秋


重走Java基礎(chǔ)之Streams(二)

Creating a Stream

所有javaCollection都有stream()parallelStream()方法可以從中構(gòu)造一個(gè)Stream:

Collection<String> stringList = new ArrayList<>();
Stream<String> stringStream = stringList.parallelStream();

可以使用以下兩種方法之一從數(shù)組創(chuàng)建Stream:

String[] values = { "aaa", "bbbb", "ddd", "cccc" };
Stream<String> stringStream = Arrays.stream(values);
Stream<String> stringStreamAlternative = Stream.of(values);

Arrays.stream()Stream .of()不同之處在于 Stream.of()有一個(gè)varargs參數(shù),因此可以像下面這樣使用:

Stream<Integer> integerStream = Stream.of(1, 2, 3);

還有一些primitive(原始的,原函數(shù),看下面例子便知道什么意思了)Streams,你可以使用。 例如:

IntStream intStream = IntStream.of(1, 2, 3);
DoubleStream doubleStream = DoubleStream.of(1.0, 2.0, 3.0);

這些primitive streams (原始流)也可以使用Arrays.stream()方法構(gòu)造:

IntStream intStream = Arrays.stream(new int[]{ 1, 2, 3 });

可以從具有指定范圍的數(shù)組創(chuàng)建一個(gè)Stream。

int[] values= new int[]{1, 2, 3, 4, 5};
IntStream intStram = Arrays.stream(values, 1, 3);

注意任何primitive streams (原始流)可以使用boxed方法轉(zhuǎn)換為boxed類型流:

Stream<Integer> integerStream = intStream.boxed();

這在某些情況下可能是有用的,如果你想收集數(shù)據(jù),因?yàn)閜rimitive streams (原始流)沒(méi)有任何可以需要一個(gè)Collector來(lái)作為參數(shù)的collect方法。

再多舉幾個(gè)例子:

1.計(jì)算列表中的元素?cái)?shù)

注意需

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

List<Integer> list = IntStream.range(1, 100).boxed().collect(Collectors.toList());
System.out.println(list.stream().count());

2.計(jì)算列表中元素的平均數(shù)

Double avarage = list.stream().collect(Collectors.averagingInt(item -> item));

3.對(duì)列表元素進(jìn)行統(tǒng)計(jì)

List<Integer> list = IntStream.range(1, 100).boxed().collect(Collectors.toList());
IntSummaryStatistics iss = list.stream().collect(Collectors.summarizingInt(value -> value));
System.out.println(iss);

輸出結(jié)果:

IntSummaryStatistics{count=99, sum=4950, min=1, average=50.000000, max=99}

4.根據(jù)List創(chuàng)建Map

List<Integer> list = IntStream.range(1, 100).boxed().collect(Collectors.toList());
Map<Integer, Integer> map = list.stream().collect(Collectors.toMap(p -> p, q->q*3));
System.out.println(map);

輸出結(jié)果:

{1=3, 2=6, 3=9, 4=12, 5=15, 6=18, 7=21, 8=24, 9=27, 10=30, 11=33, 12=36, 13=39, 14=42, 15=45, 16=48, 17=51, 18=54, 19=57, 20=60, 21=63, 22=66, 23=69, 24=72, 25=75, 26=78, 27=81, 28=84, 29=87, 30=90, 31=93, 32=96, 33=99, 34=102, 35=105, 36=108, 37=111, 38=114, 39=117, 40=120, 41=123, 42=126, 43=129, 44=132, 45=135, 46=138, 47=141, 48=144, 49=147, 50=150, 51=153, 52=156, 53=159, 54=162, 55=165, 56=168, 57=171, 58=174, 59=177, 60=180, 61=183, 62=186, 63=189, 64=192, 65=195, 66=198, 67=201, 68=204, 69=207, 70=210, 71=213, 72=216, 73=219, 74=222, 75=225, 76=228, 77=231, 78=234, 79=237, 80=240, 81=243, 82=246, 83=249, 84=252, 85=255, 86=258, 87=261, 88=264, 89=267, 90=270, 91=273, 92=276, 93=279, 94=282, 95=285, 96=288, 97=291, 98=294, 99=297}

5.求列表元素的最大數(shù)

List<Integer> list = new Random().ints(-100,100).limit(250).boxed().collect(Collectors.toList());
Optional<Integer> max = list.stream().reduce(Math::max);
max.ifPresent(value -> System.out.println(value));

應(yīng)該有些理解了吧。

重用a stream chain(一個(gè)流鏈)的中間操作

當(dāng)終端操作被調(diào)用時(shí),流被關(guān)閉。當(dāng)我們的需求只有發(fā)生在終端操作的改變時(shí),可以 重復(fù)使用中間操作流。 我們可以創(chuàng)建 a stream supplier(一個(gè)流供應(yīng)者)來(lái)構(gòu)造一個(gè)已經(jīng)建立了所有中間操作的新流。

Supplier<Stream<String>> streamSupplier = () -> Stream.of("apple", "banana","orange", "grapes", "melon","blueberry","blackberry")
.map(String::toUpperCase).sorted();

  streamSupplier.get().filter(s ->   s.startsWith("A")).forEach(System.out::println);

// APPLE

  streamSupplier.get().filter(s -> s.startsWith("B")).forEach(System.out::println);

// BANANA
// BLACKBERRY
// BLUEBERRY

int []數(shù)組可以使用流轉(zhuǎn)換為L(zhǎng)ist

int[] ints = {1,2,3};
List<Integer> list = IntStream.of(ints).boxed().collect(Collectors.toList());

通過(guò) flatMap()來(lái)扁平化處理流

flatMap:和map類似,不同的是其每個(gè)元素轉(zhuǎn)換得到的是Stream對(duì)象,會(huì)把子Stream中的元素壓縮到父集合中

map和flatMap方法示意圖:

此圖各種上傳不上,可以看此鏈接

可以看出map只轉(zhuǎn)換

可以看出flatMap不僅轉(zhuǎn)換,又進(jìn)一步合并了一下,將多個(gè)子Stream合并為一個(gè)Stream。

由下面例子可以看出,大的 Stream中的子 Stream可以被扁平化處理為單個(gè)連續(xù)的 Stream:

Map<String, List<Integer>> map = new LinkedHashMap<>();
map.put("a", Arrays.asList(1, 2, 3));
map.put("b", Arrays.asList(4, 5, 6));

List<Integer> allValues = map.values() // Collection<List<Integer>>
        .stream()                      // Stream<List<Integer>>
        .flatMap(List::stream)         // Stream<Integer>
        .collect(Collectors.toList());

System.out.println(allValues);
// [1, 2, 3, 4, 5, 6]

含Map的List可以被扁平化處理成一個(gè)連續(xù)的Stream:

List<Map<String, String>> list = new ArrayList<>();
Map<String,String> map1 = new HashMap();
map1.put("1", "one");
map1.put("2", "two");

Map<String,String> map2 = new HashMap();
map2.put("3", "three");
map2.put("4", "four");
list.add(map1);
list.add(map2);

Set<String> output= list.stream()  //  Stream<Map<String, String>>
    .map(Map::values)              // Stream<List<String>>
    .flatMap(Collection::stream)   // Stream<String>
    .collect(Collectors.toSet());  //Set<String>
[one, two, three,four]

使用Streams實(shí)現(xiàn)數(shù)學(xué)函數(shù)

Streams,尤其是 IntStreams,是一種實(shí)現(xiàn)求和項(xiàng)(Σ)的優(yōu)雅方法。Stream可以用作求和的的范圍邊界。

E.g., Madhava的Pi近似值由公式給出(Source: https://en.wikipedia.org/wiki/Approximations_of_%CF%80):

這可以以任意精度計(jì)算。 例如,101項(xiàng)次冪:

double pi = Math.sqrt(12) * 
            IntStream.rangeClosed(0, 100)
                     .mapToDouble(k -> Math.pow(-3, -1 * k) / (2 * k + 1))
                     .sum();

Note: 使用double的精度,選擇29的上限就足以獲得與Math.Pi大概一致的結(jié)果.

使用IntStream迭代索引

streams的元素通常不允許訪問(wèn)當(dāng)前項(xiàng)的索引值。 要通過(guò)訪問(wèn)索引來(lái)迭代數(shù)組或ArrayList,使用IntStream.range(start,endExclusive)。

String[] names = { "Jon", "Darin", "Bauke", "Hans", "Marc" };

IntStream.range(0, names.length)
    .mapToObj(i -> String.format("#%d %s", i + 1, names[i]))
    .forEach(System.out::println);

range(start,endExclusive) 方法返回另一個(gè) ìntStream并和 mapToObj(mapper)返回一個(gè)String。

Output:

這非常類似于使用帶有計(jì)數(shù)器的正常的for循環(huán),但是具有流水線和并行化的優(yōu)點(diǎn):

for (int i = 0; i < names.length; i++) {
    String newName = String.format("#%d %s", i + 1, names[i]);
    System.out.println(newName);
}

連接流

變量聲明示例:

Collection<String> abc = Arrays.asList("a", "b", "c");
Collection<String> digits = Arrays.asList("1", "2", "3");
Collection<String> greekAbc = Arrays.asList("alpha", "beta", "gamma");

Example 1 - 連接兩個(gè)流

final Stream<String> concat1 = Stream.concat(abc.stream(), digits.stream());

concat1.forEach(System.out::print);
// prints: abc123

Example 2 -連接多個(gè)流

final Stream<String> concat2 = Stream.concat(
    Stream.concat(abc.stream(), digits.stream()),
    greekAbc.stream());

System.out.println(concat2.collect(Collectors.joining(", ")));
// prints: a, b, c, 1, 2, 3, alpha, beta, gamma

或者為了簡(jiǎn)化嵌套的concat()語(yǔ)法我們也可以使用flatMap():

final Stream<String> concat3 = Stream.of(
    abc.stream(), digits.stream(), greekAbc.stream())
    .flatMap(s -> s);
// or `.flatMap(Function.identity());` (java.util.function.Function)

System.out.println(concat3.collect(Collectors.joining(", ")));
// prints: a, b, c, 1, 2, 3, alpha, beta, gamma

在從重復(fù)連接構(gòu)造Streams 時(shí)要小心,因?yàn)樵L問(wèn)深度并置的Stream的元素可能導(dǎo)致深層調(diào)用鏈或者甚至是StackOverflowException(本就是棧操作)。

IntStream to String

Java沒(méi)有* Char Stream *,所以當(dāng)使用Strings并構(gòu)造一個(gè)Character的Characters時(shí),一個(gè)選項(xiàng)是使用String.codePoints()方法獲取一個(gè)IntStream** ,所以IntStream可以得到如下:

public IntStream stringToIntStream(String in) {
return in.codePoints();
}

更多的涉及做其他方式的轉(zhuǎn)換,即IntStreamToString。 可以這樣做:

public String intStreamToString(IntStream intStream) {
return intStream.collect(StringBuilder::new, 
StringBuilder::appendCodePoint, StringBuilder::append).toString();
}

使用流和方法引用來(lái)編寫(xiě)自己的文檔化流程代碼

通過(guò)方法引用來(lái)創(chuàng)建具有帥氣風(fēng)格的文檔化代碼,使用帶有Stream的方法引用使得復(fù)雜的過(guò)程易于閱讀和理解(所以才說(shuō)流程)。 考慮下面的代碼:

public interface Ordered {
default int getOrder(){
return 0;
    }
}

public interface Valued<V extends Ordered> {
boolean hasPropertyTwo();
V getValue();
}

public interface Thing<V extends Ordered> {
boolean hasPropertyOne();
Valued<V> getValuedProperty();
}

public <V extends Ordered> List<V> myMethod(List<Thing<V>> things) {
    List<V> results = new ArrayList<V>();
for (Thing<V> thing : things) {
if (thing.hasPropertyOne()) {
            Valued<V> valued = thing.getValuedProperty();
if (valued != null && valued.hasPropertyTwo()){
                V value = valued.getValue();
if (value != null){
                    results.add(value);
                }
            }
        }
    }
    results.sort((a, b)->{
return Integer.compare(a.getOrder(), b.getOrder());
    });
return results;
}

最后使用Streams和方法引用重寫(xiě)的自定義的方法更易讀,而且每個(gè)步驟都很容易理解 - 它不僅更短,還顯示了哪些接口和類負(fù)責(zé)每個(gè) 步:

public <V extends Ordered> List<V> myMethod(List<Thing<V>> things) {
return things.stream()
        .filter(Thing::hasPropertyOne)
        .map(Thing::getValuedProperty)
        .filter(Objects::nonNull)
        .filter(Valued::hasPropertyTwo)
        .map(Valued::getValue)
        .filter(Objects::nonNull)
        .sorted(Comparator.comparing(Ordered::getOrder))
        .collect(Collectors.toList());
}  

未完待續(xù)……

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本文采用實(shí)例驅(qū)動(dòng)的方式,對(duì)JAVA8的stream API進(jìn)行一個(gè)深入的介紹。雖然JAVA8中的stream AP...
    浮梁翁閱讀 26,117評(píng)論 3 50
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,534評(píng)論 19 139
  • 來(lái)源:重走Java基礎(chǔ)之Streams(四)作者:知秋博客:一葉知秋轉(zhuǎn)載請(qǐng)注明來(lái)源和作者! 接上篇重走Java基礎(chǔ)...
    極樂(lè)君閱讀 525評(píng)論 0 3
  • Java SE 8中的主要新語(yǔ)言功能是lambda表達(dá)式。您可以將lambda表達(dá)式視為匿名方法;類似方法,lam...
    ATG丶MapleLeaf閱讀 1,100評(píng)論 0 1
  • 第一章 為什么要關(guān)心Java 8 使用Stream庫(kù)來(lái)選擇最佳低級(jí)執(zhí)行機(jī)制可以避免使用Synchronized(同...
    謝隨安閱讀 1,554評(píng)論 0 4

友情鏈接更多精彩內(nèi)容