Class::instanceMethod invoke a method on a reference to an object supplied by the context (e.g. String::length, equivalent x -> x.length())
Constructor reference
public class Person {
private String name;
public Person(){}
public Person (String name) {
this.name = name;
}
// other methods
}
List<Person> people = names.stream().map(Person::new).collect(Collectors.toList());
Based on the context in lambda expression => constructor with String arg is used.
Copy constructor
public Person (Person p) {
this.name = p.name;
}
💡 Useful if you want to isolate original instances.
Person before = new Person("Before");
List<Person> people = Stream.of(before).collect(Collectors.toList());
Person after = people.get(0);
assertTrue(before == after);
before.setName("changed");
assertEquals("changed", after.getName());
Using copy constructor it is possible to break that connection.
people = Stream.of(before).map(Person::new).collect(Collectors.toList());
Person after = people.get(0);
assertFalse(before == after);
assertEquals(before, after);
before.setName("changed");
assertFalse(before.equals(after));
To Array
People[] people = names.stream()
.map(Person::new) // constructor reference for Person
.toArray(Person[]::new); // constructor reference for array of Person
Functional interfaces
@java.lang.FunctionalInterface // provides compile-time check
public interface TheChecker {
boolean check(String s); // no need for abstract keyword
// int anotherMethod(); // if added => not a functional interface any more
default String sayHello() {
return "Hello";
}
static void theStaticMethod() {
System.out.println("I'm a static method in an interface");
}
}
usage
TheChecker checker = new ConcreteChecker();
checker.sayHello();
all methods are public => no need to specify deliberately
// not a functional, because it has 2 abstract methods in total
public interface TheChild extends TheChecker {
int bla();
}
Methods from Object don't count against the single abstract method limit (e.g. Comparator is still functional interface).
java.util.function
Consumer
void accept(T t)
default Consumer<T> andThen(Consumer<? super T> after)
long count =Arrays.stream(strings).map(String::length).count();int totalLength =Arrays.stream(strings) .mapToInt(String::length).sum(); OptionalDouble ave =Arrays.stream(strings).mapToInt(String::length).average();OptionalInt max =Arrays.stream(strings).mapToInt(String::length).max();
Reduce method:
OptionalIntreduce(IntBinaryOperator op)intreduce(int identity,IntBinaryOperator op)int sum =IntStream.rangeClosed(1,10) .reduce((x, y) -> x + y).orElse(0);// x - accumulator, y - value of the element in streamint sum =Stream.of(1,2,3,4,5,6,7,8,9,10) .reduce(0, Integer::sum);Stream.of("this","is","a","list").reduce("", String::concat); // thisisalistStream.of("this","is","a","list").collect(Collectors.joining());
Most general form of reduce
<U>Ureduce(U identity,BiFunction<U,? super T,U> accumulator,BinaryOperator<U> combiner)
Consider a Book class, the goal is to create a Map<Integer, Book> from List<Book>
publicclassBook { privateInteger id; privateString title;}books.stream().reduce(newHashMap<Integer,Book>(),// collect to (map, book) -> { // how to add one elementmap.put(book.getId(), book);return map: }, (map1, map2) -> { // how to compibe two maps. Needed for multithreading map1.putAll(map2);return map1; });
Debugging streams with peak
Stream<T>peek(Consumer<? super T> action)
returns a stream consisting of the elements of this stream, additionally performing the provided action on each element as they are consumed from the resulting stream.
Converting Strings to stream
String implements CharSequence, it meanspublicdefaultIntStreamchars() {...}publicdefaultIntStreamcodePoints() {...}