成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

貓頭鷹的深夜翻譯:Java Streams

Yu_Huang / 594人閱讀

摘要:處理數(shù)據(jù)流時(shí),可以避免在不必要時(shí)檢查所有數(shù)據(jù)。它對(duì)每個(gè)輸入元素執(zhí)行一個(gè)映射函數(shù)并返回一個(gè)包含結(jié)果值統(tǒng)計(jì)信息的特殊類。例如等操作可能在并行數(shù)據(jù)流中產(chǎn)生不同的結(jié)果。

前言

Stream是Java 8中的一個(gè)重大新功能。這個(gè)深入的教程是流支持的許多功能的介紹,并著重于簡(jiǎn)單實(shí)用的示例。

了解這個(gè)之前,你需要有對(duì)Java 8有基礎(chǔ)的實(shí)踐性的知識(shí)(lambda表達(dá)式,方法引用)

介紹

首先,不應(yīng)該將Java 8 Streams與Java I/O流混淆(例如:FileInputStream等);它們彼此之間關(guān)聯(lián)不大。

簡(jiǎn)而言之,流是對(duì)數(shù)據(jù)源的包裝,使我們能夠使用該數(shù)據(jù)源并對(duì)其快速便捷的批量處理。

流不存儲(chǔ)數(shù)據(jù),從這個(gè)意義上說(shuō),它不是一個(gè)數(shù)據(jù)結(jié)構(gòu)。它也從不修改底層數(shù)據(jù)源。

java.util.stream提供的新功能支持對(duì)元素的流進(jìn)行函數(shù)式編程風(fēng)格的操作,比如在集合上進(jìn)行map-reduce轉(zhuǎn)換。

現(xiàn)在讓我們?cè)谟懻撔g(shù)語(yǔ)和核心概念之前,深入一些簡(jiǎn)單的流創(chuàng)建和使用示例。

創(chuàng)建Stream

我們首先從現(xiàn)有數(shù)組中獲取流:

private static Employee[] arrayOfEmps = {
    new Employee(1, "Jeff Bezos", 100000.0), 
    new Employee(2, "Bill Gates", 200000.0), 
    new Employee(3, "Mark Zuckerberg", 300000.0)
};
Stream.of(arrayOfEmps);

我們也能從現(xiàn)有的列表中獲取流:

private static List empList = Arrays.asList(arrayOfEmps);
empList.stream();

請(qǐng)注意,Java 8在Collection接口添加了一個(gè)新的stream()方法。

我們也可以在獨(dú)立對(duì)象上使用Stream.of()創(chuàng)建流:

Stream.of(arrayOfEmps[0], arrayOfEmps[1], arrayOfEmps[2]);

或者直接使用Stream.builder():

Stream.Builder empStreamBuilder = Stream.builder();
empStreamBuilder.accept(arrayOfEmps[0]);
empStreamBuilder.accept(arrayOfEmps[1]);
empStreamBuilder.accept(arrayOfEmps[2]);
Stream empStream = empStreamBuilder.build();

還有其他方法可以獲得流,其中的一些方法我們將在下面的部分中看到。

流操作

現(xiàn)在讓我們看看我們可以在語(yǔ)言中使用新流支持的幫助下執(zhí)行的一些常見用法和操作。

forEach()是最簡(jiǎn)單也是最常用的操作。它遍歷流元素,并在每個(gè)元素上調(diào)用提供的函數(shù)。

這個(gè)方法非常常見,它直接在Iterable,Map等中引入了:

@Test
public void whenIncrementSalaryForEachEmployee_thenApplyNewSalary() {    
    empList.stream().forEach(e -> e.salaryIncrement(10.0));
    assertThat(empList, contains(
      hasProperty("salary", equalTo(110000.0)),
      hasProperty("salary", equalTo(220000.0)),
      hasProperty("salary", equalTo(330000.0))
    ));
}

它將有效地調(diào)用empList中每個(gè)元素的salaryIncrement()方法。

forEach()是一個(gè)終結(jié)操作。在執(zhí)行該操作后,流管道將被視為已經(jīng)被使用,將無(wú)法再被使用。我們會(huì)在下一節(jié)繼續(xù)討論終結(jié)操作。

map()在對(duì)原始流執(zhí)行完函數(shù)后會(huì)創(chuàng)建一個(gè)新的流。新的流將會(huì)是另一種類型。

以下示例將整數(shù)流轉(zhuǎn)換為Employee流:

@Test
public void whenMapIdToEmployees_thenGetEmployeeStream() {
    Integer[] empIds = { 1, 2, 3 };
    List employees = Stream.of(empIds)
      .map(employeeRepository::findById)
      .collect(Collectors.toList());
    assertEquals(employees.size(), empIds.length);
}

這里我們先從一個(gè)數(shù)組中獲得員工Id流。每個(gè)Id被傳入employeeRepository:findById()方法并返回對(duì)應(yīng)Employee對(duì)象,從而高效的生成一個(gè)員工流。

我們可以看到collect()方法是如何在前一個(gè)例子中工作的。當(dāng)我們完成所有處理,就可以用這種方法從流中獲取內(nèi)容:

@Test
public void whenCollectStreamToList_thenGetList() {
    List employees = empList.stream().collect(Collectors.toList());
    assertEquals(empList, employees);
}

collect()對(duì)流中的數(shù)據(jù)元素執(zhí)行可變折疊操作(將元素重新打包到一些數(shù)據(jù)結(jié)構(gòu)并進(jìn)行一些額外的邏輯處理,比如將它們連接起來(lái))。

此操作的策略通過(guò)Collections接口的實(shí)現(xiàn)來(lái)提供。在上面的例子中,我們使用toList收集器將流中的元素收集到List實(shí)例中。

現(xiàn)在讓我們看一下filter()方法。這會(huì)產(chǎn)生一個(gè)新的流,其中包含通過(guò)給定測(cè)試(由Predicate指定)的原始流的元素。

@Test
public void whenFilterEmployees_thenGetFilteredStream() {
    Integer[] empIds = { 1, 2, 3, 4 };
    List employees = Stream.of(empIds)
      .map(employeeRepository::findById)
      .filter(e -> e != null)
      .filter(e -> e.getSalary() > 200000)
      .collect(Collectors.toList());
    assertEquals(Arrays.asList(arrayOfEmps[2]), employees);
}

在上面的例子中,我們先篩選掉值為null的不合法員工號(hào),然后再使用了一個(gè)過(guò)濾器篩選出工資超過(guò)一定閾值的員工。

@Test
public void whenFindFirst_thenGetFirstEmployeeInStream() {
    Integer[] empIds = { 1, 2, 3, 4 };
    Employee employee = Stream.of(empIds)
      .map(employeeRepository::findById)
      .filter(e -> e != null)
      .filter(e -> e.getSalary() > 100000)
      .findFirst()
      .orElse(null);
    assertEquals(employee.getSalary(), new Double(200000));
}

這里會(huì)返回薪水大于10000的第一個(gè)員工,如果沒有薪水大于10000的員工,則返回null。

我們已經(jīng)看到了如何使用collect()從數(shù)據(jù)流中獲取數(shù)據(jù)。如果我們需要從流中獲取數(shù)組,我們可以簡(jiǎn)單地使用toArray()

@Test
public void whenStreamToArray_thenGetArray() {
    Employee[] employees = empList.stream().toArray(Employee[]::new);
    assertThat(empList.toArray(), equalTo(employees));
}             

Employee[]::new會(huì)新建一個(gè)空的Employee數(shù)組 - 它會(huì)用流中的元素填充。

流可以容納復(fù)雜的數(shù)據(jù)結(jié)構(gòu),如Stream>。在這樣的場(chǎng)景下,我們可以使用flatMap()來(lái)扁平化數(shù)據(jù)結(jié)構(gòu),簡(jiǎn)化后序的操作。

@Test
public void whenFlatMapEmployeeNames_thenGetNameStream() {
    List> namesNested = Arrays.asList( 
      Arrays.asList("Jeff", "Bezos"), 
      Arrays.asList("Bill", "Gates"), 
      Arrays.asList("Mark", "Zuckerberg"));
    List namesFlatStream = namesNested.stream()
      .flatMap(Collection::stream)
      .collect(Collectors.toList());
    assertEquals(namesFlatStream.size(), namesNested.size() * 2);
}

我們?cè)谇懊婵吹搅?b>forEach()的使用,它是一個(gè)終結(jié)操作。但是,有時(shí)我們需要在執(zhí)行任何終結(jié)操作之前對(duì)流的每個(gè)元素執(zhí)行多個(gè)操作。

peek()方法在這種場(chǎng)景下很實(shí)用。簡(jiǎn)單來(lái)說(shuō),它會(huì)在流的每個(gè)元素上執(zhí)行特定的操作,并返回一個(gè)新的流。peek()是一個(gè)中間操作。

@Test
public void whenIncrementSalaryUsingPeek_thenApplyNewSalary() {
    Employee[] arrayOfEmps = {
        new Employee(1, "Jeff Bezos", 100000.0), 
        new Employee(2, "Bill Gates", 200000.0), 
        new Employee(3, "Mark Zuckerberg", 300000.0)
    };
    List empList = Arrays.asList(arrayOfEmps);
    empList.stream()
      .peek(e -> e.salaryIncrement(10.0))
      .peek(System.out::println)
      .collect(Collectors.toList());
    assertThat(empList, contains(
      hasProperty("salary", equalTo(110000.0)),
      hasProperty("salary", equalTo(220000.0)),
      hasProperty("salary", equalTo(330000.0))
    ));
}
方法類型和管道

我們之前討論時(shí)提出,流操作可以分為中間操作和終結(jié)操作。

中間操作如filter()會(huì)返回一個(gè)新的流,并且可以在該流之上進(jìn)行后續(xù)操作。終結(jié)操作如forEach(),將流標(biāo)記為已經(jīng)使用,在這之后該流就不可以被使用了。

一個(gè)流管道由一個(gè)流源組成,然后是零個(gè)或多個(gè)中間操作,以及一個(gè)終端操作。

@Test
public void whenStreamCount_thenGetElementCount() {
    Long empCount = empList.stream()
      .filter(e -> e.getSalary() > 200000)
      .count();
    assertEquals(empCount, new Long(1));
}

一些操作被定義為短路操作,短路操作允許在無(wú)盡流上的計(jì)算可以在有限時(shí)間內(nèi)完成:

@Test
public void whenLimitInfiniteStream_thenGetFiniteElements() {
    Stream infiniteStream = Stream.iterate(2, i -> i * 2);
    List collect = infiniteStream
      .skip(3)
      .limit(5)
      .collect(Collectors.toList());
    assertEquals(collect, Arrays.asList(16, 32, 64, 128, 256));
}

我們會(huì)在后面繼續(xù)討論無(wú)盡流。

惰性計(jì)算

流的最重要的特征之一是它們?cè)试S通過(guò)惰性計(jì)算進(jìn)行顯著的優(yōu)化。只有在啟動(dòng)終結(jié)操作的時(shí)候才會(huì)執(zhí)行流上的計(jì)算。所有的中間操作都是惰性執(zhí)行的,所以除非在需要得出結(jié)果的時(shí)候,否則它們不會(huì)執(zhí)行。

比如,我們之前看到的findFirst()例子。這里執(zhí)行了多少次map()操作?4次,因?yàn)檩斎霐?shù)組包含4個(gè)元素。

@Test
public void whenFindFirst_thenGetFirstEmployeeInStream() {
    Integer[] empIds = { 1, 2, 3, 4 };
    Employee employee = Stream.of(empIds)
      .map(employeeRepository::findById)
      .filter(e -> e != null)
      .filter(e -> e.getSalary() > 100000)
      .findFirst()
      .orElse(null);
    assertEquals(employee.getSalary(), new Double(200000));
}

Stream執(zhí)行了一個(gè)map操作和兩個(gè)filter操作。

它首先在id 1上執(zhí)行所有操作。由于id 1的工資不大于100000,處理轉(zhuǎn)移到下一個(gè)元素。

Id 2滿足兩個(gè)過(guò)濾器謂詞,因此流將執(zhí)行終結(jié)操作findFirst()并返回結(jié)果。

在Id 3 和Id 4上不會(huì)執(zhí)行任何操作。

處理數(shù)據(jù)流時(shí),可以避免在不必要時(shí)檢查所有數(shù)據(jù)。當(dāng)輸入流是無(wú)限的并且非常大時(shí),這種行為變得更加重要。

基于比較的流操作

讓我們從sorted()方法開始。它會(huì)根據(jù)我們傳入的比較器對(duì)流元素進(jìn)行排序。

例如,我們可以根據(jù)名字對(duì)員工進(jìn)行排序:

@Test
public void whenSortStream_thenGetSortedStream() {
    List employees = empList.stream()
      .sorted((e1, e2) -> e1.getName().compareTo(e2.getName()))
      .collect(Collectors.toList());
    assertEquals(employees.get(0).getName(), "Bill Gates");
    assertEquals(employees.get(1).getName(), "Jeff Bezos");
    assertEquals(employees.get(2).getName(), "Mark Zuckerberg");
}

需要注意在sorted()方法中不會(huì)進(jìn)行短路操作。

@Test
public void whenFindMin_thenGetMinElementFromStream() {
    Employee firstEmp = empList.stream()
      .min((e1, e2) -> e1.getId() - e2.getId())
      .orElseThrow(NoSuchElementException::new);
    assertEquals(firstEmp.getId(), new Integer(1));
}

我們還可以通過(guò)使用Comparator.comparing()方法免去定義比較邏輯。

@Test
public void whenFindMax_thenGetMaxElementFromStream() {
    Employee maxSalEmp = empList.stream()
      .max(Comparator.comparing(Employee::getSalary))
      .orElseThrow(NoSuchElementException::new);
    assertEquals(maxSalEmp.getSalary(), new Double(300000.0));
}

distinct()不接受任何參數(shù)并返回流中的不同元素,從而消除重復(fù)。它使用元素的equals()方法來(lái)決定兩個(gè)元素是否相等:

@Test
public void whenApplyDistinct_thenRemoveDuplicatesFromStream() {
    List intList = Arrays.asList(2, 5, 3, 2, 4, 3);
    List distinctIntList = intList.stream().distinct().collect(Collectors.toList());
    assertEquals(distinctIntList, Arrays.asList(2, 5, 3, 4));
}
allMatch, anyMatch和noneMatch

這些操作會(huì)接收一個(gè)Predicate并返回一個(gè)boolean值。一旦確定了答案,就執(zhí)行短路操作并停止處理:

@Test
public void whenApplyMatch_thenReturnBoolean() {
    List intList = Arrays.asList(2, 4, 5, 6, 8);
    boolean allEven = intList.stream().allMatch(i -> i % 2 == 0);
    boolean oneEven = intList.stream().anyMatch(i -> i % 2 == 0);
    boolean noneMultipleOfThree = intList.stream().noneMatch(i -> i % 3 == 0);
    assertEquals(allEven, false);
    assertEquals(oneEven, true);
    assertEquals(noneMultipleOfThree, false);
}

allMatch()會(huì)檢查流中所有元素的謂詞是否為真。在它遇到5時(shí)無(wú)法被2整除,它會(huì)立即返回false。

anyMatch()會(huì)檢查流中任何一個(gè)元素的謂詞是否為真。這里,再次施加短路操作并且在第一個(gè)元素之后立即返回true。

noneMatch()檢查是否沒有與謂詞匹配的元素。在這里,只要遇到可被3整除的6就返回false。

特定類型的流

目前為止,我們討論的都是對(duì)象引用流。但是還有IntStream, LongStream, 和 DoubleStream分別對(duì)應(yīng)Int,Long和Double基礎(chǔ)數(shù)據(jù)類型的流。當(dāng)需要處理大量的數(shù)字類型值時(shí),使用它們會(huì)非常方便。

創(chuàng)建

創(chuàng)建一個(gè)IntStream最常用的方法是在一個(gè)現(xiàn)有流上調(diào)用mapToInt()方法。

@Test
public void whenFindMaxOnIntStream_thenGetMaxInteger() {
    Integer latestEmpId = empList.stream()
      .mapToInt(Employee::getId)
      .max()
      .orElseThrow(NoSuchElementException::new);
    assertEquals(latestEmpId, new Integer(3));
}

我們先生成了一個(gè)empList的流然后再在其上通過(guò)在mapToInt中調(diào)用Employee::getId方法來(lái)獲得一個(gè)IntStream。最后我們調(diào)用max()獲得最大值。

我們還可以使用IntStream.of()生成IntStream

IntStream.of(1, 2, 3);

或者是IntStream.range():

IntStream.range(10, 20)

它會(huì)生成一個(gè)包含10-19之間所有整數(shù)的IntStream。
這里有一個(gè)重要的區(qū)別需要注意一下:

Stream.of(1, 2, 3)

該方法生成的是一個(gè)Stream對(duì)象而不是IntStream。
類似的,使用map()而不是mapToInt()將會(huì)生成Stream而不是IntStream。

empList.stream().map(Employee::getId);
特殊操作

特定類型的流相比于標(biāo)準(zhǔn)的流提供了額外的操作。比如sum(), average(), range()等。

@Test
public void whenApplySumOnIntStream_thenGetSum() {
    Double avgSal = empList.stream()
      .mapToDouble(Employee::getSalary)
      .average()
      .orElseThrow(NoSuchElementException::new);
    assertEquals(avgSal, new Double(200000));
}
Reduction操作

Reduction操作(也稱為fold)獲得一系列輸入元素,并通過(guò)重復(fù)執(zhí)行組合操作將它們組合為單個(gè)匯總結(jié)果。我們已經(jīng)看到過(guò)幾個(gè)Reduction操作如findFirst(), min()和max()。

讓我們看一看通俗意義上的reduce()的使用。

T reduce(T identity, BinaryOperator accumulator)

identity代表起始值而accumulator代表一個(gè)二元操作符。

@Test
public void whenApplyReduceOnStream_thenGetValue() {
    Double sumSal = empList.stream()
      .map(Employee::getSalary)
      .reduce(0.0, Double::sum);
    assertEquals(sumSal, new Double(600000));
}

這里我們將起始值設(shè)置為0.0并且對(duì)流上的元素重復(fù)的執(zhí)行Double::sum()。通過(guò)在Stream中使用reduce我們有效的實(shí)現(xiàn)了DoubleStream的sum方法。

高級(jí)collect

我們已經(jīng)看過(guò)如何使用Collectors.toList()從流中獲取list。讓我們?cè)倏磶讉€(gè)從流中獲取數(shù)據(jù)的方法。

@Test
public void whenCollectByJoining_thenGetJoinedString() {
    String empNames = empList.stream()
      .map(Employee::getName)
      .collect(Collectors.joining(", "))
      .toString();
    assertEquals(empNames, "Jeff Bezos, Bill Gates, Mark Zuckerberg");
}

我們還可以使用toSet方法從流中獲取Set:

@Test
public void whenCollectBySet_thenGetSet() {
    Set empNames = empList.stream()
            .map(Employee::getName)
            .collect(Collectors.toSet());
    assertEquals(empNames.size(), 3);
}
toCollection
@Test
public void whenToVectorCollection_thenGetVector() {
    Vector empNames = empList.stream()
            .map(Employee::getName)
            .collect(Collectors.toCollection(Vector::new));
    assertEquals(empNames.size(), 3);
}

這里內(nèi)部創(chuàng)建了一個(gè)新的空集合,并對(duì)流中的每個(gè)元素調(diào)用了add()方法。

summarizingDouble

summarizingDouble是另一個(gè)有趣的收集器。它對(duì)每個(gè)輸入元素執(zhí)行一個(gè)double-producing映射函數(shù)并返回一個(gè)包含結(jié)果值統(tǒng)計(jì)信息的特殊類。

@Test
public void whenApplySummarizing_thenGetBasicStats() {
    DoubleSummaryStatistics stats = empList.stream()
      .collect(Collectors.summarizingDouble(Employee::getSalary));
    assertEquals(stats.getCount(), 3);
    assertEquals(stats.getSum(), 600000.0, 0);
    assertEquals(stats.getMin(), 100000.0, 0);
    assertEquals(stats.getMax(), 300000.0, 0);
    assertEquals(stats.getAverage(), 200000.0, 0);
}

可以看到我們是如何分析每位員工的工資并獲取有關(guān)該數(shù)據(jù)的統(tǒng)計(jì)信息 - 如最小值,最大值,平均值等。

summaryStatistics()可以在使用特定流的時(shí)候用來(lái)生成類似的結(jié)果:

@Test
public void whenApplySummaryStatistics_thenGetBasicStats() {
    DoubleSummaryStatistics stats = empList.stream()
      .mapToDouble(Employee::getSalary)
      .summaryStatistics();
    assertEquals(stats.getCount(), 3);
    assertEquals(stats.getSum(), 600000.0, 0);
    assertEquals(stats.getMin(), 100000.0, 0);
    assertEquals(stats.getMax(), 300000.0, 0);
    assertEquals(stats.getAverage(), 200000.0, 0);
}
partitioningBy

我們可以根據(jù)元素是否滿足某個(gè)條例將一個(gè)流分解為兩個(gè)。

讓我們將一個(gè)數(shù)值數(shù)組分成奇數(shù)數(shù)組和偶數(shù)數(shù)組:

@Test
public void whenStreamPartition_thenGetMap() {
    List intList = Arrays.asList(2, 4, 5, 6, 8);
    Map> isEven = intList.stream().collect(
      Collectors.partitioningBy(i -> i % 2 == 0));
    assertEquals(isEven.get(true).size(), 4);
    assertEquals(isEven.get(false).size(), 1);
}

在這里,流被分解并存入Map中,并使用true和false鍵代表偶數(shù)數(shù)組和奇數(shù)數(shù)組。

groupingBy

groupingBy()提供高級(jí)分解。它將我們的流分解為兩個(gè)或多個(gè)子流。

它接收一個(gè)分類方法作為參數(shù)。這個(gè)分類方法會(huì)作用于流中的每一個(gè)元素。

分類方法返回的值會(huì)作為Map的鍵。

@Test
public void whenStreamGroupingBy_thenGetMap() {
    Map> groupByAlphabet = empList.stream().collect(
      Collectors.groupingBy(e -> new Character(e.getName().charAt(0))));
    assertEquals(groupByAlphabet.get("B").get(0).getName(), "Bill Gates");
    assertEquals(groupByAlphabet.get("J").get(0).getName(), "Jeff Bezos");
    assertEquals(groupByAlphabet.get("M").get(0).getName(), "Mark Zuckerberg");
}

在上面這個(gè)簡(jiǎn)單的例子中,我們根據(jù)員工的首字母進(jìn)行分組。groupingBy()使用Map對(duì)流中的數(shù)據(jù)進(jìn)行分組。但是,有時(shí)候我們可能需要將元素分組為另一種類型。我們可以使用mapping(),它實(shí)際上可以使收集器適應(yīng)不同的類型。

@Test
public void whenStreamMapping_thenGetMap() {
    Map> idGroupedByAlphabet = empList.stream().collect(
      Collectors.groupingBy(e -> new Character(e.getName().charAt(0)),
        Collectors.mapping(Employee::getId, Collectors.toList())));
    assertEquals(idGroupedByAlphabet.get("B").get(0), new Integer(2));
    assertEquals(idGroupedByAlphabet.get("J").get(0), new Integer(1));
    assertEquals(idGroupedByAlphabet.get("M").get(0), new Integer(3));
}

這里mapping()使用getId()映射函數(shù)將流元素Employee映射到員工id - 這是一個(gè)Integer。這些ID仍然根據(jù)員工名字的首字母進(jìn)行分組。
reducing()類似于reduce():

@Test
public void whenStreamReducing_thenGetValue() {
    Double percentage = 10.0;
    Double salIncrOverhead = empList.stream().collect(Collectors.reducing(
        0.0, e -> e.getSalary() * percentage / 100, (s1, s2) -> s1 + s2));
    assertEquals(salIncrOverhead, 60000.0, 0);
}

reducing + groupingBy

@Test
public void whenStreamGroupingAndReducing_thenGetMap() {
    Comparator byNameLength = Comparator.comparing(Employee::getName);
    Map> longestNameByAlphabet = empList.stream().collect(
      Collectors.groupingBy(e -> new Character(e.getName().charAt(0)),
        Collectors.reducing(BinaryOperator.maxBy(byNameLength))));
    assertEquals(longestNameByAlphabet.get("B").get().getName(), "Bill Gates");
    assertEquals(longestNameByAlphabet.get("J").get().getName(), "Jeff Bezos");
    assertEquals(longestNameByAlphabet.get("M").get().getName(), "Mark Zuckerberg");
}

我們首先根據(jù)員工的首字母將其分組,然后在各個(gè)組里,我們找到名字最長(zhǎng)的員工。

Parallel Streams
@Test
public void whenParallelStream_thenPerformOperationsInParallel() {
    Employee[] arrayOfEmps = {
      new Employee(1, "Jeff Bezos", 100000.0), 
      new Employee(2, "Bill Gates", 200000.0), 
      new Employee(3, "Mark Zuckerberg", 300000.0)
    };
    List empList = Arrays.asList(arrayOfEmps);
    empList.stream().parallel().forEach(e -> e.salaryIncrement(10.0));
    assertThat(empList, contains(
      hasProperty("salary", equalTo(110000.0)),
      hasProperty("salary", equalTo(220000.0)),
      hasProperty("salary", equalTo(330000.0))
    ));
}

因?yàn)檫@里涉及到多線程,所以我們需要注意一下幾點(diǎn):

確保代碼是線程安全的。特別要注意并行操作可能會(huì)的修改的共享數(shù)據(jù)。

如果執(zhí)行操作的順序或輸出流中返回的順序很重要,我們不應(yīng)該使用并行流。例如findFirst()等操作可能在并行數(shù)據(jù)流中產(chǎn)生不同的結(jié)果。

確保并行執(zhí)行是值得的。

Infinite Streams
@Test
public void whenGenerateStream_thenGetInfiniteStream() {
    Stream.generate(Math::random)
      .limit(5)
      .forEach(System.out::println);
}

我們給generate()方法提供了Supplier,當(dāng)需要新元素時(shí)就會(huì)調(diào)用這個(gè)方法。
我們需要提供一個(gè)終止進(jìn)程的條件。通常使用的一種方法是limit()。在上面的例子中,我們將元素的數(shù)量限制為5,并在它們生成時(shí)候打印它們。

@Test
public void whenIterateStream_thenGetInfiniteStream() {
    Stream evenNumStream = Stream.iterate(2, i -> i * 2);
    List collect = evenNumStream
      .limit(5)
      .collect(Collectors.toList());
    assertEquals(collect, Arrays.asList(2, 4, 8, 16, 32));
}

iterate()有兩個(gè)參數(shù):一個(gè)初始值,稱為種子值,和一個(gè)使用前一個(gè)值來(lái)生成下一個(gè)值的函數(shù)。該方法是有狀態(tài)的,因此不適合并行運(yùn)行。


想要了解更多開發(fā)技術(shù),面試教程以及互聯(lián)網(wǎng)公司內(nèi)推,歡迎關(guān)注我的微信公眾號(hào)!將會(huì)不定期的發(fā)放福利哦~

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/68766.html

相關(guān)文章

  • 頭鷹深夜翻譯:JDK Vs. JRE Vs. JVM之間區(qū)別

    摘要:什么是為執(zhí)行字節(jié)碼提供一個(gè)運(yùn)行環(huán)境。它的實(shí)現(xiàn)主要包含三個(gè)部分,描述實(shí)現(xiàn)規(guī)格的文檔,具體實(shí)現(xiàn)和滿足要求的計(jì)算機(jī)程序以及實(shí)例具體執(zhí)行字節(jié)碼。該類先被轉(zhuǎn)化為一組字節(jié)碼并放入文件中。字節(jié)碼校驗(yàn)器通過(guò)字節(jié)碼校驗(yàn)器檢查格式并找出非法代碼。 什么是Java Development Kit (JDK)? JDK通常用來(lái)開發(fā)Java應(yīng)用和插件?;旧峡梢哉J(rèn)為是一個(gè)軟件開發(fā)環(huán)境。JDK包含Java Run...

    blair 評(píng)論0 收藏0
  • 頭鷹深夜翻譯Java WeakHashMap

    摘要:本文簡(jiǎn)介類概覽類構(gòu)造器總結(jié)類構(gòu)造方法類使用舉例類概覽是一個(gè)實(shí)現(xiàn)了接口,并且鍵為型的哈希表。中的條目不再被正常使用時(shí),會(huì)被自動(dòng)刪除。它的鍵值均支持。和絕大多數(shù)的集合類一樣,這個(gè)類不是同步的。 本文簡(jiǎn)介 WeakHashMap類概覽 WeakHashMap類構(gòu)造器總結(jié) WeakHashMap類構(gòu)造方法 WeakHasjMap類使用舉例 1. WeakHashMap類概覽 Wea...

    BothEyes1993 評(píng)論0 收藏0
  • 頭鷹深夜翻譯JavaCAS(Compare And Swap)

    摘要:否則它就會(huì)用新的值替代當(dāng)前值。在這種情況下,鎖可能會(huì)優(yōu)于原子變量,但在實(shí)際的爭(zhēng)用級(jí)別中,原子變量的性能優(yōu)于鎖。在中引入了另外一個(gè)構(gòu)件。 題目要求 在我們深入了解CAS(Compare And Swap)策略以及它是如何在AtomicInteger這樣的原子構(gòu)造器中使用的,首先來(lái)看一下這段代碼: public class MyApp { private volatile int ...

    hosition 評(píng)論0 收藏0
  • 頭鷹深夜翻譯Java 10JEP 286-局部變量類型推斷

    摘要:在此基礎(chǔ)上又向前邁進(jìn)了一步局部變量類型推斷允許開發(fā)人員跳過(guò)局部變量的類型聲明局部變量是指在方法定義,初始化塊,循環(huán)和其它的如代碼塊,會(huì)推斷該局部變量的類型。 前言 之前面試的時(shí)候問了我是否了解JDK10的變化,一時(shí)回答不出來(lái),所以只回答了JDK8中的函數(shù)式編程和流編程。今天看到這篇講JAVA10的文章,順便了解一下。 正文 JAVA10的所有新特性請(qǐng)參考這里。在所有的JEP中,JEP-...

    chavesgu 評(píng)論0 收藏0
  • 頭鷹深夜翻譯:BlockingQueue和持續(xù)管理

    摘要:我們將使用單個(gè)線程管理任務(wù)放入隊(duì)列的操作以及從隊(duì)列中取出的操作。同時(shí)這個(gè)線程會(huì)持續(xù)的管理隊(duì)列。另一個(gè)線程將用來(lái)創(chuàng)建,它將一直運(yùn)行知道服務(wù)器終止。此線程永遠(yuǎn)不會(huì)過(guò)期,有助于實(shí)現(xiàn)持續(xù)監(jiān)控。這些請(qǐng)求將會(huì)自動(dòng)的被獲取,并在線程中繼續(xù)處理。 在Java中,BlockingQueue接口位于java.util.concurrent包下。阻塞隊(duì)列主要用來(lái)線程安全的實(shí)現(xiàn)生產(chǎn)者-消費(fèi)者模型。他們可以使用...

    YanceyOfficial 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<