19. JAVA – strumieniowanie


Strumieniowanie pozwala na iterowanie i manipulowanie elementami list i kolekcji z użyciem wyrażenia lambda.


Stream jest interfejsem generycznym,  doskonałą alternatywą dla przeglądania list przy pomocy pętli i przede wszystkim jest szybsza. Pozwala też na równoległe wykonywanie operacji na danych.

Przykład:

plik Products.java

public class Products {
public String name;
public int price;

public Products(String name, int price) {
this.name = name;
this.price = price;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getPrice() {
return price;
}

public void setPrice(int price) {
this.price = price;
}

@Override
public String toString() {
return "Products{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
}

plik Car.java

import java.util.Comparator;
import java.util.Objects;

// w klasie musimy użyć Comparatoor do porównywania elementów, aby skutecznie stosować collect
public class Car implements Comparator<Car> {
public String name;
public int topSpeed;
public int price;

public Car(String name, int topSpeed, int price) {
this.name = name;
this.topSpeed = topSpeed;
this.price = price;
}

@Override
public String toString() {
return "Car{" +
"name='" + name + '\'' +
", topSpeed=" + topSpeed +
", price=" + price +
'}';
}

//dodajemy equals and hashcode
//wybieramy z listy tylko name
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Car car = (Car) o;
return name.equals(car.name);
}

@Override
public int hashCode() {
return Objects.hash(name);
}

//implementujemy metodę compare
//modyfikujemy zwracanie
@Override
public int compare(Car o1, Car o2) {
return o1.name.compareTo(o2.name);
}
}

 

plik StreamExample.java

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StreamExample {
public static void main(String[] args) {
//tworzymy zbiór, który następnie przefiltrujemy
ArrayList<String> arl = new ArrayList<>();
arl.add("PHP");
arl.add("Python");
arl.add("C++");
arl.add("Java");
arl.add("JS");
arl.add("HTML");
arl.add("CSS");


//aby iterować po elementach użyjemy strumieniowania zamiast pętli
//chcemy otrzymać listę elementów, których liczba znaków =3
arl.stream().filter(someValue -> someValue.length()>=3 && someValue.length()<=6)//nie dajemy średnika, bo po kropce damy kolejne metody
//możemy dodatkowo przefiltrować ze względu na literę od jakiej sie zaczybnna nazwa
.filter(someValue -> someValue.startsWith("P") || someValue.startsWith("C"))
//iterujemy po elementach po przefiltrowaniu
.forEach(someValue -> System.out.println(someValue));

//tworzymy listę na bazie klasy
ArrayList<StreamMyClass> animals = new ArrayList<>();
animals.add( new StreamMyClass("Elephant", 4000, 2));
animals.add( new StreamMyClass("Python", 150, 5));
animals.add( new StreamMyClass("ShiTzu", 7, 0));
animals.add( new StreamMyClass("Wasp", 0, 7));
animals.add( new StreamMyClass("Wife", 75, 10));

//filtrujemy elementy własnej klasy po poziomie agresji
animals.stream().filter(myName -> myName.agrresive>6)
.filter(myName -> myName.weight>50 && myName.weight<100)
.forEach(myValue -> System.out.println(myValue));

//otrzymane wyniki możemy dodać do innej kolekcji
//służy do tego metoda collect()

//tworzymy listę z wyników filtrowania
List<StreamMyClass> list = animals.stream().filter(sth -> sth.agrresive>6)
.collect(Collectors.toList());

//teraz chcemy z wyników zrobić ArrayList
ArrayList<StreamMyClass> arrl = new ArrayList<>(list);
arrl.forEach(sth -> System.out.println(sth));

//aby dodać do kolekcji tylko unikalne elementy użuwamy Set()
//przykład1
List<Integer> arr = Arrays.asList(8,-2,6,7,5,4,2,8,3,3,7,10,-11,2);
Set<Integer> uniqueNumbers = arr.stream().collect(Collectors.toSet());
System.out.println(uniqueNumbers);

//przykład2
ArrayList<String> arl1 = new ArrayList<>();
arl1.add("PHP");
arl1.add("PHP");
arl1.add("Python");
arl1.add("C++");
arl1.add("C++");
arl1.add("Java");
arl1.add("JS");
arl1.add("JS");
arl1.add("HTML");
arl1.add("CSS");
Set<String> set = arl1.stream().filter(someValue -> someValue.length()>=3 && someValue.length()<=6)
.filter(someValue -> someValue.startsWith("P") || someValue.startsWith("C"))
.collect(Collectors.toSet());
set.forEach(someValue-> System.out.println(someValue));

//przykład z użyciem klasy Car
ArrayList<Car> cars = new ArrayList<>();
//dodajmy elementy, które będą zduplikowane
cars.add(new Car("Peugeot", 270, 150000));
cars.add(new Car("Ford", 250, 170000));
cars.add(new Car("Peugeot", 270, 150000));
cars.add(new Car("Mercedes", 240,200000));
cars.add(new Car("Mercedes", 240,200000));
cars.add(new Car("MBW", 280,260000));
cars.add(new Car("Opel", 220, 140000));
//użyjemy teraz collect() do wyciągnięcia unikalnych danych
Set<Car> set1 = cars.stream().filter(car -> car.price<260000)
.collect(Collectors.toSet());
set1.forEach(car -> System.out.println(car));

//sumowanie elementów za pomoca summingToInt na bazie klasy Products
ArrayList<Products> product = new ArrayList<Products>();
//dodajmy elementy
product.add(new Products("apple", 3));
product.add(new Products("banana", 4));
product.add(new Products("pineaple", 4));
product.add(new Products("bag", 1));
product.add(new Products("perfume", 12));
int totalPrice = product.stream()
.collect(Collectors.summingInt(element -> element.price));
System.out.println(totalPrice);

//sumowanie elementów można teżprzeprowadzić za pomocą reduce()
//skorzystamy z istniejącej listy arr
int resultSum = arr.stream()
//reduce(wartość starowa, (lambda z 2 argumentami: suma dotychczasowa i następny element)
.reduce(0, (tempsum, el) -> tempsum+el);
System.out.println("wynik sumowania reduce() " + resultSum);

//sumowanie z referencją do metody
int resultSumRef = arr.stream()
.reduce(0, Integer::sum);
System.out.println("Wynik sumowania redice() z referencją: " + resultSumRef);

//zmapujmy teraz naszą listę po name
Map<String, Products> producsMap = product.stream()
.collect(Collectors.toMap(el->el.name, el->el));
System.out.println(producsMap);
//odwołajmy sie do konkretnego elementu mapy
Products pr = producsMap.get("apple");
System.out.println(pr);

//referencja do metody
List<Integer> producsPrices = product.stream()
.filter(el->el.price<10)
.map(Products::getPrice).collect(Collectors.toList());
System.out.println(producsPrices);

//kolekcję możemy przeszukać pod kątem największem i najmniejszej wartości
//max()
Products prodMaxPrices = product.stream().max((pr1, pr2) -> pr1.price>pr2.price ? 1 : -1)
.get();
System.out.println(prodMaxPrices);
//min()
Products prodMinPrices = product.stream().min((pr1, pr2) -> pr1.price>pr2.price ? 1 : -1)
.get();
System.out.println(prodMinPrices);

//ograniczmy listę zwracanych wyników, wykorzystamy istniejącą litę arr
List<Integer> result = arr.stream()
.filter(el-> el > 0 && el <10)
.limit(6)
.collect(Collectors.toList());
System.out.println(result);

//aby zliczyc ilość elementów używamy count()
System.out.println(result.stream().count());

//jeżeli potrzebujemy stworzyć stream wypełniony wartościami, możemy zrobić to dwojako
//z użyciem limit
System.out.println("Stream z limitowaniem");
Stream.iterate(0, i -> i+1).limit(10)
.forEach(stream -> System.out.print(stream));
//bez limit
System.out.println("\nStream bez limitowania");
Stream.iterate(0, i -> i<10, i -> i+1)
.forEach(stream -> System.out.print(stream));

}
}

plik StreamMyClass.java

//filtrowanie własnej klasy
//stwórzmy klasę dla klasyfikacji zwierząt pod kątem wagi i agresji
public class StreamMyClass {
public String name;
public int weight;
public int agrresive; //1-10

public StreamMyClass(String name, int weight, int agrresive) {
this.name = name;
this.weight = weight;
this.agrresive = agrresive;
}

@Override
public String toString() {
return "StreamMyClass{" +
"name='" + name + '\'' +
", weight=" + weight +
", agrresive=" + agrresive +
'}';
}
}