gianluca.aguzzi@unibo.it
angelo.filaseta@unibo.it
Questo materiale è ampiamente basato su quello realizzato dai Prof. Mirko Viroli e Roberto Casadei, che ringrazio.
Ogni errore riscontratovi è esclusiva responsabilità degli autori di questo documento.
List<Persona>
con proprietà reddito e città, ottenere la somma dei redditi di tutte le persone di CesenaPerson
– equals
, hashCode
e toString
omessipublic class Person {
private final String name;
private final Optional<String> city;
private final double income;
private final Set<String> jobs;
public Person(String name, String city, double income, String... jobs) {
this.name = Objects.requireNonNull(name);
this.city = Optional.ofNullable(city); // null in ingresso indica città assente
this.income = income;
this.jobs = new HashSet<>(Arrays.asList(jobs)); // conversione a set
}
public String getName() {
return this.name;
}
public Optional<String> getCity() {
return this.city;
}
public double getIncome() {
return this.income;
}
public Set<String> getJobs(){
return Collections.unmodifiableSet(this.jobs); // copia difensiva
}
//.. seguono hashCode, equals e toString
public class UseStreamsOnPerson {
public static void main(String[] args) {
final List<Person> list = new ArrayList<>();
list.add(new Person("Mario","Cesena",20000,"Teacher"));
list.add(new Person("Rino","Forlì",50000,"Professor"));
list.add(new Person("Lino","Cesena",110000,"Professor","Dean"));
list.add(new Person("Ugo","Cesena",20000,"Secretary"));
list.add(new Person("Marco",null,4000,"Contractor"));
final double result = list.stream()
.filter(p->p.getCity().isPresent())
.filter(p->p.getCity().get().equals("Cesena"))
.mapToDouble(Person::getIncome)
.sum();
System.out.println(result);
// alternativa con iteratore: qual è la più leggibile?
double res2 = 0.0;
for (final Person p: list){
if (p.getCity().isPresent() && p.getCity().get().equals("Cesena")){
System.out.println(p);
res2 = res2 + p.getIncome();
}
}
System.out.println(res2);
}
}
java.util.stream
: interfacce e classi per gli streamStream<X>
: stream e metodi statici di “factory”BaseStream<X,B>
: sopra-interfaccia di Stream
con i metodi baseDoubleStream
: stream di double
, con metodi base e specificiIntStream
, LongStream
: similiCollector<T,A,R>
: rappresenta una operazione di riduzioneCollectors
: fornisce una serie di collettoripublic interface Collection<E> extends Iterable<E> {
..
Iterator<E> iterator();
default boolean removeIf(Predicate<? super E> filter) {..}
default Spliterator<E> spliterator() {..}
default Stream<E> stream() {..}
default Stream<E> parallelStream() {..}
}
java.util.BaseStream
public interface BaseStream<T, S extends BaseStream<T, S>> extends ... {
// Torna un iteratore sugli elementi rimasti dello stream, e lo chiude
Iterator<T> iterator();
// spliterator è un iteratore che supporta parallelismo..
Spliterator<T> spliterator();
// è uno stream gestibili in modalità parallela
boolean isParallel();
// torna una variante sequenziale dello stream
S sequential();
// torna una variante parallela dello stream
S parallel();
// torna una variante non ordinata dello stream
S unordered();
// associa un handler chiamato alla chiusura dello stream
S onClose(Runnable closeHandler);
void close();
}
Stream<X>
empty
, of
, iterate
, generate
, concat
filter
, map
, flatMap
, distinct
, sorted
, peek
, limit
, skip
, mapToXYZ
,..forEach
, forEachOrdered
, toArray
, reduce
, collect
, min
, max
, count
, anyMatch
, allMatch
, noneMatch
, findFirst
, findAny
,..DoubleStream
e similiStream<X>
con un trasformatore mapToObj()
o boxed()
sum
java.util.Stream
: costruzione stream, 1/3public interface Stream<T> extends BaseStream<T, Stream<T>> {
// Static factories
public static<T> Stream<T> empty() {..}
public static<T> Stream<T> of(T t) {..}
public static<T> Stream<T> of(T... values) {..}
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {..}
public static<T> Stream<T> generate(Supplier<T> s) {..}
public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) {..}
// also recall method Collection.stream() and Collection.parallelStream()
public static<T> Builder<T> builder() {..}
public interface Builder<T> extends Consumer<T> {
void accept(T t);
default Builder<T> add(T t) {
accept(t);
return this;
}
Stream<T> build();
}
java.util.Stream
: trasformazione stream, 2/3
// Stream transformation
Stream<T> filter(Predicate<? super T> predicate);
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
Stream<T> distinct();
Stream<T> sorted();
Stream<T> sorted(Comparator<? super T> comparator);
Stream<T> peek(Consumer<? super T> action);
Stream<T> limit(long maxSize);
Stream<T> skip(long n);
IntStream mapToInt(ToIntFunction<? super T> mapper);
LongStream mapToLong(ToLongFunction<? super T> mapper);
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);
IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper);
LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper);
DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper);
java.util.Stream
: terminazione stream, 3/3 // Terminal Operations
void forEach(Consumer<? super T> action);
void forEachOrdered(Consumer<? super T> action);
Object[] toArray();
<A> A[] toArray(IntFunction<A[]> generator);
T reduce(T identity, BinaryOperator<T> accumulator);
Optional<T> reduce(BinaryOperator<T> accumulator);
<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);
<R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);
<R, A> R collect(Collector<? super T, A, R> collector);
Optional<T> min(Comparator<? super T> comparator);
Optional<T> max(Comparator<? super T> comparator);
long count();
boolean anyMatch(Predicate<? super T> predicate);
boolean allMatch(Predicate<? super T> predicate);
boolean noneMatch(Predicate<? super T> predicate);
Optional<T> findFirst();
Optional<T> findAny();
}
public class UseTransformations {
public static void main(String[] args) {
final List<Integer> li = List.of(10, 20, 30, 5, 6, 7, 10, 20, 100);
System.out.print("All\t\t :");
li.stream().forEach(i -> System.out.print(" " + i));
System.out.print("\nFilter(>10)\t :");
li.stream()
.filter(i -> i > 10) // fa passare solo certi elementi
.forEach(i -> System.out.print(" " + i));
System.out.print("\nMap(N:i+1)\t :");
li.stream()
.map(i -> "N:" + (i + 1)) // trasforma ogni elemento
.forEach(i -> System.out.print(" " + i));
System.out.print("\nflatMap(i,i+1)\t :");
li.stream()
.flatMap(i -> List.of(i, i + 1).stream()) // trasforma e appiattisce
.map(String::valueOf) // invece del forEach..
.map(" "::concat)
.forEach(System.out::print);
}
}
public class UseTransformations2 {
public static void main(String[] args) {
final List<Integer> li = List.of(10,20,30,5,6,7,10,20,100);
System.out.print("\nDistinct\t :");
li.stream().distinct() // elimina le ripetizioni
.forEach(i->System.out.print(" "+i));
System.out.print("\nSorted(down)\t :");
li.stream().sorted((i,j)->j-i) // ordina
.forEach(i->System.out.print(" "+i));
System.out.print("\nPeek(.)\t\t :");
li.stream().peek(i->System.out.print(".")) // esegue una azione per ognuno
.forEach(i->System.out.print(" "+i));
System.out.print("\nLimit(5)\t :");
li.stream().limit(5) // solo i primi 5
.forEach(i->System.out.print(" "+i));
System.out.print("\nSkip(5)\t\t :");
li.stream().skip(5) // salta i primi 5
.forEach(i->System.out.print(" "+i));
}
}
public class UseFactories {
public static void main(String[] args) {
final List<Integer> li = List.of(10,20,30,5,6,7,10,20,100);
System.out.print("Collection: ");
li.stream()
.forEach(i->System.out.print(" "+i));
System.out.print("\nEmpty: ");
Stream.empty()
.forEach(i->System.out.print(" "+i));
System.out.print("\nFromValues: ");
Stream.of("a","b","c")
.forEach(i->System.out.print(" "+i));
System.out.print("\nIterate(+1): ");
Stream.iterate(0,i->i+1) // 0,1,2,3,...
.limit(20)
.forEach(i->System.out.print(" "+i));
}
}
public class UseFactories2 {
public static void main(String[] args) {
System.out.print("\nSuppl(random): ");
Stream.generate(()->Math.random()) // rand,rand,rand,...
.limit(5)
.forEach(i->System.out.print(" "+i));
//DoubleStream.generate(()->Math.random())... stream unboxed
System.out.print("\nConcat: ");
Stream.concat(Stream.of("a","b"),Stream.of(1,2))
.forEach(i->System.out.print(" "+i));
System.out.print("\nBuilder: ");
Stream.builder()
.add(1)
.add(2)
.build()
.forEach(i->System.out.print(" "+i));
System.out.print("\nRange: ");
IntStream.range(0,20) // 0,1,..,19
.forEach(i->System.out.print(" "+i));
}
}
public class UseOtherFactories {
private final static String aDir = "/home/mirko/aula";
private final static String aFile = "/home/mirko/aula/oop/17/Counter.java";
public static void main(String[] args) throws Exception {
final Path dirPath = FileSystems.getDefault().getPath(aDir);
System.out.println("Found below "+aDir);
Files.find(dirPath, 2, (a,b)->true).forEach(System.out::println);
System.out.println("List directory "+aDir);
Files.list(dirPath).forEach(System.out::println);
final Path filePath = FileSystems.getDefault().getPath(aFile);
System.out.println("Contenuto of "+aFile);
Files.lines(filePath).forEach(System.out::println);
System.out.println("Contenuto of "+aFile+" in altra codifica");
Files.lines(filePath,StandardCharsets.ISO_8859_1).forEach(System.out::println);
// Si veda il sorgente di BufferedReader.lines() per capire come si realizza
// uno stream a partire da un iteratore
System.out.println("Stream da una stringa..");
"Hellò!".chars().mapToObj(i->(char)i).forEach(System.out::println);
}
}
public class UseTerminations {
public static void main(String[] args) {
final List<Integer> li = List.of(10,20,30,5,6,7,10,20,100);
System.out.print("ForEach:\t ");
li.stream().forEach(i->System.out.print(" "+i));
System.out.print("\nForEachOrdered: ");
li.stream().forEachOrdered(i->System.out.print(" "+i));
final Integer[] array = li.stream().toArray(i->new Integer[i]);
System.out.println("\nToArray:\t "+Arrays.toString(array));
//Integer sum = li.stream().reduce(0,(x,y)->x+y);
final Integer sum = li.stream().reduce(0,Integer::sum);
System.out.println("Sum:\t\t "+sum);
//Optional<Integer> max = li.stream().max((x,y)->x-y);
final Optional<Integer> max = li.stream().max(Integer::compare);
System.out.println("Max:\t\t "+max);
final long count = li.stream().count();
System.out.println("Count:\t\t "+count);
final boolean anyMatch = li.stream().anyMatch(x -> x==100);
System.out.println("AnyMatch:\t "+anyMatch);
final Optional<Integer> findAny = li.stream().findAny();
System.out.println("FindAny:\t "+findAny);
}
}
Stream.collect
public class UseGeneralizedCollectors {
public static void main(String[] args) {
final List<Integer> li = List.of(10,20,30,5,6,7,10,20,100);
// Uso collect a tre argomenti
final Set<Integer> set = li.stream().collect(
()->new HashSet<>(), // oggetto collettore
(h,i)->h.add(i), // aggiunta di un elemento
(h,h2)->h.addAll(h2)); // concatenazione due collettori
System.out.println("Set: "+set); // un HashSet coi valori dello stream
// Più frequente: uso collect passandogli un collettore general-purpose
final Set<Integer> set2 = li.stream().collect(Collector.of(
HashSet::new, // oggetto collettore
HashSet::add, // aggiunta di un elemento
(h,h2)->{h.addAll(h2); return h;})); // concatenazione due collettori
System.out.println("Set: "+set2);
// cosa fa questo collettore? (.. un po' complicato)
final int res=li.stream().collect(Collector.of(
()->Arrays.<Integer>asList(0), // oggetto collettore
(l,i)->l.set(0,i+l.get(0)), // aggiunta di un elemento
(l,l2)->{l.set(0,l.get(0)+l2.get(0)); return l;})) // concatenazione
.get(0); // estrazione risultato
System.out.println("Res: "+res);
}
}
Collectors
class Collectors { // some methods, all public static and generic..
Collector<T, ?, C> toCollection(Supplier<C> collectionFactory)
Collector<T, ?, List<T>> toList()
Collector<T, ?, Set<T>> toSet()
Collector<CharSequence, ?, String> joining(CharSequence delimiter,
CharSequence prefix,
CharSequence suffix)
Collector<T, ?, Optional<T>> minBy(Comparator<? super T> comparator)
Collector<T, ?, Optional<T>> maxBy(Comparator<? super T> comparator)
Collector<T, ?, Integer> summingInt(ToIntFunction<? super T> mapper)
Collector<T, ?, Long> summingLong(ToLongFunction<? super T> mapper)
Collector<T, ?, Map<K, List<T>>> groupingBy(Function<? super T, ? extends K> classifier)
Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,
Collector<? super T, A, D> downstream)
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper)
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction)
Collector<T, ?, DoubleSummaryStatistics>
summarizingDouble(ToDoubleFunction<? super T> mapper)
}
UseCollectors
: collettori di baseimport static java.util.stream.Collectors.*;
public class UseCollectors {
public static void main(String[] args) {
final List<Integer> li = List.of(10,20,30,5,6,7,10,20,100);
// una List
System.out.println(li.stream().collect(toList()));
// un Set
System.out.println(li.stream().collect(toSet()));
// un TreeSet
// System.out.println(li.stream().collect(toCollection(TreeSet::new)));
System.out.println(li.stream().collect(minBy(Integer::compare)));
System.out.println(li.stream().collect(summingInt(Number::intValue)).toString());
System.out.println(li.stream().map(i->i.toString())
.collect(joining(",","(",")")));
// (10,20,30,5,6,7,10,20,100)
}
}
UseCollectors
: collettori di base pt 2import static java.util.stream.Collectors.*;
public class UseCollectors2 {
public static void main(String[] args) {
final List<Integer> li = List.of(10,20,30,5,6,7,10,20,100);
final Map<Integer,List<Integer>> map = li.stream()
.collect(groupingBy(x -> x/10));
System.out.println(map); // {0=[5,6,7], 1=[10,10], ..}
final Map<Boolean,Optional<Integer>> map2 = li.stream()
.collect(groupingBy(x->x%2==0,minBy(Integer::compare)));
System.out.println(map2); // minimo dei pari e minimo dei dispari
final Map<Integer,Integer> map3 = li.stream()
.distinct()
.collect(toMap(x->x,x->x+1));
System.out.println(map3); // mappa ogni x in x+1
final Map<Integer,Integer> map4 = li.stream().collect(toMap(x->x/10,x->x,(x,y)->x+y));
System.out.println(map4); // somma degli elementi in ogni decina
System.out.println(li.stream().collect(summarizingInt(Number::intValue)).toString());
}
}
Person
public static void main(String[] args) {
final List<Person> list = new ArrayList<>();
list.add(new Person("Mario","Cesena",20000,"Teacher"));
list.add(new Person("Rino","Forlì",50000,"Professor"));
list.add(new Person("Lino","Cesena",110000,"Professor","Dean","Secretary"));
list.add(new Person("Ugo","Cesena",20000,"Secretary"));
list.add(new Person("Marco",null,4000,"Contractor"));
// Jobs of people from Cesena
final String res =
list.stream()
.filter(p->p.getCity().filter(x->x.equals("Cesena")).isPresent())
.flatMap(p->p.getJobs().stream())
.distinct()
.collect(Collectors.joining("|", "[[", "]]"));
System.out.println(res);
// Average income of professors
final double avg =
list.stream()
.filter(p->p.getJobs().contains("Professor"))
.mapToDouble(Person::getIncome)
.average().getAsDouble();
System.out.println(avg);
System.out.println(
list.stream()
.filter(p->p.getJobs().contains("Professor"))
.mapToDouble(Person::getIncome).average());
}
}
public class UseStreamsForAlgorithms {
public static void main(String[] args) {
System.out.println(
LongStream.iterate(2, x->x+1)
.filter((i)->LongStream.range(2, i/2+1).noneMatch(j -> i%j==0))
.limit(1000)
.mapToObj(String::valueOf)
.collect(Collectors.joining(",","[","]")));
final Random r = new Random();
System.out.println(
IntStream.range(0, 10000)
.map(i->r.nextInt(6)+r.nextInt(6)+2)
.boxed() // da int a Integer
.collect(groupingBy(x->x, collectingAndThen(counting(), d->d/10000.0))));
System.out.println(
"Prova di testo: indovina cosa produce la seguente computazione......"
.chars()
.mapToObj(x->String.valueOf((char)x))
.collect(groupingBy(x->x,counting()))
.entrySet()
.stream()
.sorted((e1,e2)->-Long.compare(e1.getValue(),e2.getValue()))
.limit(3)
.map(String::valueOf)
.collect(Collectors.joining(",","[","]")));
}
}