Wyobraźmy sobie system do przechowywania zamówień, w którym zamówienie zawierać będzie informacje o kliencie, listę zamówionych pozycji oraz swój numer.
@Data
@AllArgsConstructor
public class Order {
    private Integer orderNumber;
    private Customer customer;
    private List<OrderItem> items;
 }
Aby nie zaciemniać klas konstuktorami, getterami i setterami skorzstam z Project Lombok. Adnotacje @Data oraz @AllArgsConscturor zrobią całą robotę za nas.
Klasa Customer zawiera nazwę oraz adres klienta.
@Data
@AllArgsConstructor
public class Customer {
    private String customerName;
    private Address address;
}
@Data
@AllArgsConstructor
public class Address {
    private String city;
    private String country;
}
Pozycja na zamówieniu zawiera towar oraz jego ilość.
@Data
@AllArgsConstructor
public class OrderItem {
    private Item item;
    private Integer quantity;
    public Integer getItemValue() {
        return item.getPrice() * quantity;
    }
}
@Data
@AllArgsConstructor
public class Item {
    private String itemName;
    private Integer price;
}
Stwórzmy kilka zamówień.
        Customer domesticCustomer = new Customer("Frutos S.A", new Address("Kraków", "Polska"));
        Customer anotherDomesticCustomer = new Customer("Verduras Sp. z o.o.", new Address("Warszawa", "Polska"));
        Customer foreignCustomer = new Customer("Uluru Ltd.", new Address("Sidney", "Australia"));
        Item apple = new Item("Golden Delicious Apple", 6);
        Item pear = new Item("Conference Pear", 5);
        Item potato = new Item("Potato", 2);
        Item strawberry = new Item("Strawberry", 9);
        Item onion = new Item("Onion", 1);
        Order localOrder = new Order(1, domesticCustomer, Arrays.asList(
                new OrderItem(potato, 100),
                new OrderItem(onion, 20),
                new OrderItem(strawberry, 50),
                new OrderItem(apple, 200)));
        Order anotherLocalOrder = new Order(2, anotherDomesticCustomer, Arrays.asList(
                new OrderItem(potato, 200),
                new OrderItem(onion, 180)));
        Order exportOrder = new Order(3, foreignCustomer, Arrays.asList(
                new OrderItem(apple, 1300),
                new OrderItem(pear, 600)
        ));
        List<Order> orderList = new ArrayList<>();
        orderList.add(localOrder);
        orderList.add(anotherLocalOrder);
        orderList.add(exportOrder);
Wyciągnijmy z listy zamówień wszystkie adresy dostaw.
List<Address> addresses = orderList.stream()
                .map(order -> order.getCustomer().getAddress())
                .collect(Collectors.toList());
        System.out.println(addresses);
Wynik:
 [Address(city=Kraków, country=Polska), Address(city=Warszawa, country=Polska), Address(city=Sidney, country=Australia)] 
Użyliśmy operacji map, która mapuje obiekt Order na Address. Wynik operacji umieszczony został na liście addresses za pomocą operacjicollect i kolektora Collectors.toList().
A co gdybyśmy chcieli zobaczyć tylko listę zamówień krajowych?
List<Address> addresses = orderList.stream()
                .map(order -> order.getCustomer().getAddress())
                .filter(address -> address.getCountry().equals("Polska"))
                .collect(Collectors.toList());
Jak widzisz pojawiła się operacja filter, która za pomocą predykatu pozostawia w strumieniu elementy spełniające podany warunek.  Predykat to w interfejs definiujący metodę test. Poniżej fragment jego definicji z java.util.function. 
public interface Predicate<T> {
    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);
Dobrą praktyką, znacznie poprawiającą czytelność kodu (zwłaszcza w przypadku bardziej skomplikowanych warunków) jest definiowanie predykatów na zewnątrz strumienia.
Predicate<Address> polishAddress = address -> address.getCountry().equals("Polska");
Filtrowanie z wykorzystaniem zdefiniowanego predykatu:
List<Address> addresses = orderList.stream()
                .map(order -> order.getCustomer().getAddress())
                .filter(polishAddress)
                .collect(Collectors.toList());
Zbierzmy teraz wszystkie pozycje z listy zamówień.
        List<OrderItem> items = orderList.stream()
                .map(Order::getItems)
                .flatMap(Collection::stream)
                .collect(Collectors.toList());
        System.out.println(items);
Operacja mapowania map dokonała konwersji zamówienia Order do listy List<OrderItem>. Za pomocą flatMap dołączmy wszystkie elementy OrderItem kolekcji do jednego strumienia wynikowego, który to następnie zbieramy do listy. Collectors::stream zamienia List<OrderItem> w strumień, co inaczej można by zapisać .flatMap(orderItems -> orderItems.stream())
Zsumujmy wartość zamówienia exportOrder
Integer sum = exportOrder.getItems().stream()
                .map(OrderItem::getItemValue)
                .reduce(Integer::sum)
                .orElse(0);
Kilka słów wyjaśnienia co się dzieje. Za pomocą map konwertujemy obiekt OrderItem na obiekt Integer. Robi to funkcja getItemValue, która oblicza wartość pozycji zamówienia. W wyniku tej operacji otrzymujemy strumień liczb całkowitych, który następnie redukujemy do jednego elementu za pomocą operacji reduce.  Jako reduktor użyta została metoda statyczna sum z klasy Integer.  Końcowe orElse zwraca wartość 0 gdyby strumień okazał się pusty np. zamiast exportOder sumowalibyśmy emptyOrder zdefiniowane następująco:  Order emptyOrder = new Order(4, null, Collections.emptyList()); 
Poniżej bardziej analityczny zapis, bez wykorzystania getItemValue i bez sumatora, który zobrazuje szczegółowo co się wydarzyło.
  Optional<Integer> optionalSum = exportOrder.getItems().stream()
                .map(orderItem -> orderItem.getItem().getPrice() * orderItem.getQuantity())
                .reduce((itemValue1, itemValue2) -> itemValue1 + itemValue2);
Jak widzisz, reduktor pobiera dwa elementy a zwraca jeden.
Posortujmy zamówienia. Jako kryterium sortowania przyjmiemy wartość zamówionych pozycji.
        orderList.stream().sorted((order1, order2) -> {
            int value1 = order1.getItems().stream()
                    .map(OrderItem::getItemValue)
                    .reduce(Integer::sum)
                    .orElse(0);
            int value2 = order2.getItems().stream()
                    .map(OrderItem::getItemValue)
                    .reduce(Integer::sum)
                    .orElse(0);
            return Integer.compare(value1, value2);
        })
                .forEach(System.out::println);
Gdybyśmy chcieli uzyskać tylko pierwszy element (zamówienie o najniższej wartości), można skorzystać z findFirst. Zmodyfikujmy również nieco kod, poprzez dodanie do klasy Order implementacji interfejsu Comparable.
@Data
@AllArgsConstructor
public class Order implements Comparable<Order> {
    private Integer orderNumber;
    private Customer customer;
    private List<OrderItem> items = new ArrayList<>();
    @Override
    public int compareTo(Order o) {
        int thisValue = this.getItems().stream()
                .map(OrderItem::getItemValue)
                .reduce(Integer::sum)
                .orElse(0);
        int otherValue = o.getItems().stream()
                .map(OrderItem::getItemValue)
                .reduce(Integer::sum)
                .orElse(0);
        return Integer.compare(thisValue, otherValue);
    }
}
Wyświetlmy zamówienie o najmniejszej wartości.
 orderList.stream()
                .sorted()
                .findFirst()
                .ifPresent(System.out::println);
Zamówienie o największej wartości możemy uzyskać np. poprzez użycie reduktora, który zostawiał będzie zawsze drugi element (w wyniku jego działania na “końcu” zostanie ostatnie zamówienie, czyli to o najwyższej wartości).
orderList.stream()
                .sorted()
                .reduce((order, order2) -> order2)
                .ifPresent(System.out::println);
Na koniec pogrupujmy zamówienia wg kraju dostawy. Wykorzystamy w tym celu kolektor Collectors.groupingBy.
 Map<String, List<Order>> ordersByCountry = orderList.stream()
                .collect(Collectors.groupingBy(order -> order.getCustomer().getAddress().getCountry()));
