Java中的流:您需要知道的一切
#braziliandevs #java #stream #java8

地图,过滤,减少

让我们从一个示例开始?

* Given a list of people
* We need to compute the average of the age of those people
* For the people older than 20
  1. 在这种情况下,很明显,我们从一个人对象开始,但是我们将以达到年龄的方式工作 /转换数据(地图< / strong>)。地图将一个对象带到另一个对象,通常是不同的类型

Map, um objeto entra, é transformado (seta) e sai outro

  1. 这样,让我们​​过滤年龄,以使他是20多年的一天。这是一个过滤器。过滤器实际上过滤了数据,而不是转换数据(与地图不同),而是决定是否维护数据。 2.1。代码中的过滤器接收谓词作为parano,它代表接收参数并返回布尔值的函数。

Filter, entra idade, passa pelo filter (seta) e voltam apenas idades >20

  1. 最后,让我们计算出已经从人转换并拍摄的数据的一天,以使年龄> 20岁。在这种情况下,这种操作的AVG将减少,让我们将开始作为聚合(最小,最大,计数等)。

Image description

Opera§£o 行为
地图 转换数据,更改您的类型。没有更改元素的数量。
过滤器 不会转换数据,减少(或主要是相同)元素的数量
减少 将元素结合在一起。基于减少操作(例如总和,母亲或母亲)产生价值的终端操作。

为什么使用流?

List<Person> people = ...;

int sum = 0;
int count = 0;
for (Person person: people) {
    if (person.getAge() > 20) {
        count++;
        sum += person.getAge();
    }
}
double average = 0d;

if(count > 0) {
    average = sum / count;
}

在上面的示例中,我们有一种详细描述应采取的措施以达到结果(呈现示例的结果),如果我们更改算法,我们需要更改代码,即使算法相同。例如,在SQL中,不一定有必要写:

Select AVG(age) from People
Where People.age>20

请注意,在上面的示例中,我们描述了我们想完成的工作,前提,我们描述结果,而不是应计算结果。

Collection x流

好吧,如果方式是列表或任何集合的一部分,我们可以尝试实现以下操作:

List<Person> people = ...;
double average = 
people.map(person -> person.getAge())
    .filter(age  > age > 20)
    .average();

错误!

但是,为什么收藏和溪流的API分开?

想象这些操作是在1,000,000人名单上进行的!因此,在第一张地图之后,您将拥有1,000,000个年龄的整数清单(因为该地图不会降低尺寸本身),我认为您已经可以意识到复制收集对于处理器和记忆< /p>

ðâstrong>流对象提供地图 /过滤器 /减少操作而无需重复和母亲效率!< / strong> < / p>

但是这是如何工作的?当我们使用pessoas.stream()时,我们返回了一个Stream<Pessoa>。根据定义,流的对象不携带数据,它是为创建它的。

集合是一种内存数据结构,可以保持值,在我们开始使用集合之前,所有值都应该被填充。而Java流是按需计算的数据结构。 Java流不在存储数据,它在源数据结构(集合和数组)上运行,并产生可以使用并执行特定操作的管道数据。

https://www.digitalocean.com/community/tutorials/java-8-stream

中级操作x最终

流在集合上工作以执行MAP /过滤器 /降低,中间操作是返回流的流(Stream<T> map()©All Stream,返回流)的流操作。

最终操作是操作,在处理 /管道结束后再次返回集合,koud3是一个示例< / p>

以及如何

这是代码的示例。

List<Pessoa> pessoas = new ArrayList<>();

    pessoas.stream()
                 .map(...)
                 .filter(...)
                 .average();

ðâ€很重要:您不能两次处理相同的流:

Stream personNames = personStream.map(Person::getName);
Stream emptyNames = personNames.filter( name -&gt; name.isEmpty())
Stream notEmptyNames = personNames.filter( name -&gt; !name.isEmpty())

这种方式会破裂,因为我们正在两次处理相同的流人名称!

Stream personNames = personStream.map(Person::getName);
Stream personAges = personStream.map(Person::getAge);

在这种情况下,我们正在应用地图来创建两个不同的流,而不是打破道路。

Stream emptyNames = personStream.map(Person::getName).filter(name -&gt; name.isEmpty());
Stream notEmptyNames = personStream.map(Person::getName).filter(name -&gt; !name.isEmpty());

这样,处理两个不同的流也可以工作

请不要创建变量以与流合作,我通过教学

来做到这一点

平板图

平面图可处理关系1:n

示例:城市,我们每个城市有几个人。

Image description

假设我们想让所有人,无论他们的城市如何。在这种情况下,我们关心的是相关实体,而不是与城市本身*。 Flatmap做到这一点,采用城市实体并返回我们的Stream<Pessoas>*

List<City> cities = ...;

Function<City, Stream<Person>> flatMapper = 
    city -> city.getPeople().stream();

long count = 
    cities.stream()
    .flatMap(flatMapper) /*poderíamos colocar city -> city.getPeople().stream() direto*/
    .count()

ðâflatmap操作接收一个对象,并从其他对象返回流

List words "Gomu","Gomu", "No", "Mi");

Stream streamStream =
words.stream()
.map(w -&gt; w.split("")) // Stream
.flatMap(Arrays::stream) ; // Stream

flatmap在这种情况下起作用,因为应用 flatMap(Arrays::stream) ,您告诉java Stream 并将其转换为一条字符串( Stream )使用整个母亲 Arrays.stream()

从正则流

String sentence = "the quick brown fox jumps over the lazy dog";
String[] words = sentence. split(
Stream<String> wordsStream = Arrays.stream(words);
Ion count = wordsStream.count()

当我们这样做时,我们创建了一个中间数组,我们丢失了流的建议。

我们可以做:

Pattern pattern = Pattern.compile(" ");
long count = pattern.sp1itAsStream(sentence) . count();

另一种类型的流

在Java中,有一些流界面来更正确地操纵某些原始词,一个例子是IntStream

intstream

IntStream 当您使用原始整数值时,在空间和性能方面倾向于更有效,因为它避免了对象的创建 Integer

a IntStream 提供专门的操作,以使用 sum() average() koud15 rangeClosed() ,等等。 A Stream<Integer> 提供流操作基因©rich 适用于对象,但是在跟踪原始类型方面的效率较低。

ðâ¡

重构同时完成母亲任务的例子

流同时执行母任务不好,因此,如果我们有一个循环添加值â添加值迷,则我们需要将其分为3(重复)(重复)并将其更改为“流”。当然,这大大降低了性能,但其中很少有要元素的元素,这基本上是无法察觉的。

public String statement() {

    double totalAmount = rentals.stream()
        .mapToDoub1e(this::computeRenta1Amount)
        .sum();
    int frequentRenterPoints = rentals.stream()
    .mapToInt (this::getFrequentRenterPoints)
    .sum();

    String statement = composeHeader();

    statement += rentals.stream()
    .map(this::computeStatementLine)
    .collect(Collectors.joining());

}

reduã§ãµs

之前,我们听说减少与SQL的聚合类似,让我们牢记它,但是它如何工作?

我们将以示例为例,总和是一个BinaryOperator<Integer>实现,该实现需要两个元素和总和(sum = (i1,i2) -> i1+i2;)。一次发生两个元素,将值关联,然后添加到另一个。

空流的减少

但是,空流的减少是什么?>身份元素是一个突出的值,它是减少操作的起点。在功能编程上下文中,当不是在流中组合的元素时,还原操作将返回身份元素,从而确保定义操作并且在空流的情况下不会引起例外。这尤其是在处理特定操作没有数据的情况下处理的情况,使您可以以可预测且安全的方式行事。

例如,总和的身份为0,鉴于0不会影响最终值,最终值。

麦克斯,我的,平均?

“身份元素”或 max 的初始值, min average < /strong>几乎没有什么不同,因为这些操作返回 Optional ,可以为空(如果流为空)或包含一个值,因此这些操作没有身份值。>>>>>>

因此,要访问Max,Min和AVG,您必须执行类似的操作:

Optional<Integer> max = numbers.stream().max(Integer::compareTo);
Optional<Integer> min = numbers.stream().min(Integer::compareTo);
OptionalDouble average = numbers.stream().mapToDouble(Integer::doubleValue).average();

    System.out.println("Max: " + max.orElse(null));
    System.out.println("Min: " + min.orElse(null));
    System.out.println("Average: " + average.orElse(Double.NaN));

我们如何写降低?

也许您想知道为什么我们也理解身份元素的基础,如果仍然不清楚,现在将是:

Java流中的操作 reduce 有两个主要的parano:

  1. 初始值(身份元素):已经解释了
  2. 累积的函数(累积函数):此函数用于将流元素组合在结果中。函数必须是接受两个参数并返回结果的lambda表达式或全部参考。总而言之 (a, b) -> a + b

ð如果您不通过正确的身份,则结果是不正确的。

如果您的流没有身份,则可以使用另一个母亲的过载,该过载未接收身份,但您返回可选。仅当您没有身份证

时使用它

可变容器中的减少

可变容器中的降低是指还原操作的应用,例如总和,乘法,母亲等,以及存储在封闭中的元素,该元件可以在还原过程中更改。在此上下文中的容器可以定义为灵活的数据结构,可让您以动态方式(列表,地图等)存储和修改元素。因此,我们可以简化可突变容器的减少,作为集合的减少。

在列表中调用 .stream() 时,您正在创建列表中包含的元素的流。流是可以以功能方式处理的一系列元素,,但它不会修改原始列表。在调用 .max() 基于某些标准的母元素时列表,但列表本身保持不变。

收藏家!

List<Person> pessoasDaBaixada = new ArrayList() ;
pessoasDaBaixada.stream()
.filter(p -> p.getDDD().equals("013"))
.forEach(p -> pessoasDaBaixada.add(p));

但是上面的示例与我们已经看到的示例并没有很大不同,为什么我们专注于这种减少类型?

要使用Java流API过滤和收集新列表上的元素,您应该使用合适的收集器,例如 koud31

收集器允许您捕获集合或其他具体数据类型中的减少操作结果。 换句话说,它们将处理的元素变成了一个可以使用和操纵的真实集合。

List<Person> pessoasDaBaixada = new ArrayList<>();

// Supondo que o número de DDD seja uma String
List<Person> pessoasComDDD13 = pessoasDaBaixada.stream()
    .filter(p -> "13".equals(p.getDDD()))
    .collect(Collectors.toList());
    //.toList() retornará uma lista imutável!

新母亲©All stream.tolist()不会产生可修改的列表,也不是collect(toUnmodifiableList())的快捷方式,因为toUnmodifiableList() **不接受null value 。 stream.tolist()的实现不受Koude Interface34的限制;因此,Stream.toList()分配了更少的内存。这使其非常适合在流的大小提前知道时使用。

其他收藏家的例子

  • Collectors.toSet() :从流元素中创建一个集合,删除重复。
  • Collectors.toMap(keyMapper, valueMapper) :使用映射函数从流元素中创建一个地图来提取键和值。
  • Collectors.groupingBy(classifier) :基于分类函数定义的标准的组流元素。
  • Collectors.joining(delimiter) :Concatena使用定界符的单宁元素中的流元素。
  • Collectors.summingInt() Collectors.summingLong() :计算整数或长流元素的总和。

使用示例:

Map<Integer, List<Person>> pessoasPorIdade = pessoas.stream()
    .collect(Collectors.groupingBy(Person::getIdade));
List<Person> pessoas = Arrays.asList(
    new Person("Jorge", 25),
    new Person("Ben", 30),
    new Person("Jor", 35)
);

Map<String, Integer> mapNomeIdade = pessoas.stream()
    .collect(Collectors.toMap(Person::getNome, Person::getIdade));

ðâ£o我们一定需要收藏家才能使用收藏品,但它们使生活更加狂热,请参见之前的两个示例:


// ex1
Map<Integer, List<Person>> pessoasPorIdade = new HashMap<>();
pessoas.forEach(person -> {
    Integer idade = person.getIdade();
    List<Person> pessoasComIdade = pessoasPorIdade.get(idade);
    if (pessoasComIdade == null) {
        pessoasComIdade = new ArrayList<>();
        pessoasPorIdade.put(idade, pessoasComIdade);
    }
    pessoasComIdade.add(person);
});

// ex2
Map<String, Integer> mapNomeIdade = new HashMap<>();
pessoas.stream().forEach(person -> mapNomeIdade.put(person.getNome(), person.getIdade()));

刷子:平行流

当我们探索降低和终端操作时,考虑Java平行流的潜力至关重要,但是这是什么? ðρ

平行流提供了利用CPU类别进行并行操作的能力,从而极大地改善了流性能。

尽管如此,值得一提的是,不能总是在流中考虑并行操作,假设您正在以减少的方式计算一天添加然后分配不同的部分,这将带来不正确的结果,这意味着运动是一个关联操作!

协会操作

是合并元素不影响结果的顺序的操作。关联操作的常见示例包括总和和乘法。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// Cálculo da soma em uma stream paralela
int sum = numbers.parallelStream().reduce(0, Integer::sum);
Esse parágrafo tem como objetivo apenas introduzir e notificar a existência de streams paralelas, procure saber mais sobre elas!

非社交操作

是相反的!

结论和感谢

感谢您的阅读,我希望它具有富有成效的知识,并且您拥有能够采取自己的步骤并在需要的情况下构建功能和高级流的所有必要知识。

反思

https://app.pluralsight.com/library/courses/692a1310-42db-4f3c-a33b-208a55b7bd84/table-of-contents

https://www.digitalocean.com/community/tutorials/java-8-stream

Maratona Java Virado no Jiraya

https://acervolima.com/coletores-java/