Stream API


Materiály

Stream

Stream je generické rozhraní a jedna z novinek Javy 8. Umožňuje nám pracovat s kolekcemi bez nutnosti iterace pomocí foreach. Princip práce s kolekcemi jako se streamy je inspirovaný funkcionálním programováním. Práce s kolekcemi se děje pomocí agregovaných funkcí. Například map, reduce, filter...

Vytvoření Streamu

Rozhraní Collection má dvě metody pro vytvoření streamu.

Jako zdroj streamu může být kolekce, pole nebo I/O stream.

    
    List<Integer> integers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9));

    integers.stream(); // Vytvoření klasického streamu

    integers.parallelStream(); // Vytvoření paralleního streamu

    int[] integerArray = {1, 2, 3, 4, 5, 6, 7, 8, 9};

    Stream.of(1, 2, 3, 5); // Stream hodnot

    Stream.of(integerArray); // Stream který obsahuje všechny prvky pole integerArray
    

Operace se Streamy

Rozhraní Stream poskytuje mnoho operací pro práci se streamy. Většina z nich vrací další stream se kterým lze náledně provádět další operace. Seznam všech dostupných metod lze nalézt zde.

forEach

Stream poskytuje metodu forEach pomocí které lze projít všechny prvky streamu. Náledující příklad vytiskne všechny prvky na obrazovku.

    
    List<Integer> integers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9));

    // Lambda výraz
    integers.stream().forEach(integer -> System.out.println(integer));

    // Pomocí reference na metodu.
    integers.stream().forEach(System.out::println);
    

map

Vrací stream který obsahuje prvky na které byla aplikována funkce (například umocnění 2).

    
    List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);

    //List obsahující mocniny 2
    List<Integer> squaresList = numbers.stream().map( i -> i * i).collect(Collectors.toList());
    

filter

Pomocí filter vybereme ze streamu pouze ty prvky které splňují zadanou podmínku.

    
    List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");

    // Počet prázdných řetězců
    int count = strings.stream().filter(string -> string.isEmpty()).count();
    

reduce

Aplikuje redukci na stream a vrací Optional jako výledek akumulace.

    
    List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");

    // Součet čísel se streamu
    int sum = integers.stream().reduce(0, ((integer, integer2) -> integer + integer2));
    

Collectors

"Collectory" se používají pří spojení výledků zpracování prvků streamu. Může vrace List nebo String.

    
    List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");

    // Spojení do List
    List<String> filtered = strings.stream()
                                        .filter(string -> !string.isEmpty())
                                        .collect(Collectors.toList());

    // Spojení do String
    String mergedString = strings.stream()
                                .filter(string -> !string.isEmpty())
                                .collect(Collectors.joining(", "));
    

Příklad

        
        public class Person {

            public enum Sex {
                MALE, FEMALE
            }

            private String name;
            private LocalDate birthday;
            private Sex gender;
            private String emailAddress;

            // ...

            public int getAge() {
                // ...
            }

            public String getName() {
                // ...
            }
        }

        // Výpis všech uživatelů pomocí for-each
        for (Person p : roster) {
            System.out.println(p.getName());
        }

        // Výpis pomocí streamu
        roster
            .stream()
            .forEach(e -> System.out.println(e.getName());


        // Většina operací vrací opět stream takže lze opět volat další metody pro práci se streamy
        // Výpis všech uživatelů s mužským pohlavím
        roster
            .stream()
            .filter(e -> e.getGender() == Person.Sex.MALE)
            .forEach(e -> System.out.println(e.getName()));

        // Ekvivalentně pomocí for-each
        for (Person p : roster) {
            if (p.getGender() == Person.Sex.MALE) {
                System.out.println(p.getName());
            }
        }

        // Výpočet prúměrného věku všech uživatelů s mužským pohlavím.
        double average = roster
                            .stream()
                            .filter(p -> p.getGender() == Person.Sex.MALE)
                            .mapToInt(Person::getAge)
                            .average()
                            .getAsDouble();


        
    

Úkoly

K řešení úkolů použíjte Stream API!

  1. S pomocí Stream API implementujte statické metody int[] even(int[] arg) a List<Integer> evenNumbers(List<Integer> arg) vracející pole/seznam čísel, kde budou pouze sudá čísla z argumentu arg.
  2. Navhrněte jednoduchou elektronickou kuchařku. Ta se bude skládat z následujících tříd:
    • Ingredient - obsahující následující informace o surovině: název suroviny, měrná jednotka, jednotková cena
    • Recipe - obsahující seznam a množství surovin nutných pro přípravu pokrmu.
    Třída by měla obsahovat následující metody:
    • .toString() - která vrací seznam surovin a jejich množství jako řetězec
    • .getPrice() - která vrací cenu jídla
    • .isCookable(List<Ingredient> availableIngredients) - která vrací true, pokud seznam obsahuje všechny potřebné ingredience pro uvaření pokrmu
    • .getIngredientsByPrice() - která vrací názvy surovin seřazené podle jejich ceny
    • .getTheMostExpensiveIngredient() - která vrací nejnákladnější položku v receptu
Úkoly posílejte na email r.vyjidacek@gmail.com s předmětem ZP3JV07. Termín odevzdání do půlnoci 15. 11. 2017. Odevzdávejte pouze zdrojové kódy v archivu zip.