Java 8
Lambda expressions
[optional] With explicit data types:
String[] names = directory.list((File dir, String name) -> name.endsWith(".java"));
Block syntax
String[] names = directory.list((File dir, String name) -> {
return name.endsWith(".java")
});
Method references
Advantage over lambda:
shorter
includes the names of the class containing the method => easier to read
Syntax
object::instanceMethod
(e.g.System.out::println
, equivalentx -> System.out.println(x)
)Class::staticMethod
(e.g.Math::max
, equivalent(x,y) -> Math.max(x,y)
)Class::instanceMethod
invoke a method on a reference to an object supplied by the context (e.g.String::length
, equivalentx -> 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)
How andThen
method works:
v--- 1. c.accept("Java2s.com");
c.andThen(c).andThen(c).accept("Java2s.com");
^ ^
| |
| 3. c.accept("Java2s.com");
|
2. c.accept("Java2s.com");
Interface
single abstract method
IntConsumer
void accept(int x)
DoubleConsumer
void accept(double x)
LongConsumer
void accept(long x)
BiConsumer
void accept(T t, U u)
Supplier
T get()
Interface
Single abstract method
IntSupplier
int getAsInt()
DoubleSupplier
double getAsDouble()
LongSupplier
long getAsLong()
BooleanSupplier
boolean getAsBoolean()
Use case:
concept of
deferred
execution
public static void info(Logger logger, Supplier<String> message) {
if (logger.isLoggable(Level.INFO))
logger.info(message.get());
}
Predicate
boolean test(T t)
💡 You can have a method which has a parameter of Predicate type.
💡 Can be a good idea to have frequently used predicates as a constants.
💡 It is possible to combine predicates using default methods:
car.filter(cars, PREDICATE_IS_RED.or(PREDICATE_IS_FAST));
public List<Car> filter(List<Car> cars, Predicate<Car> pre);
Function
[Function<T,R>] R apply(T t)
Transforms input parameter of type T to output of type R
If (T == R) => UnaryOperator;
BiFunction
[BiFunction<T,U,R>] R apply(T t, U u)
if (R == T == U) => BinaryOperator
(e.g. Math.max)
Stream
Stream is a sequence of elements that does not save elements or modify the original source
Streams do not process any data until a terminal expression is reached
long count = stream
.map((value) -> { return value.toUpperCase(); }) // just listener is added. Non-terminal operation
.map((value) -> { return value.substring(0,3); }) // just listener is added. Non-terminal operation
.count(); // Terminal operation. Triggers stream processing.
Terminal operations: anyMatch(), allMatch(), noneMatch(), count(), collect(), findAny(), forEach(),...
Create streams
class Stream {
@SafeVarargs
public static<T> Stream<T> of(T... valuse) {
return Arrays.stream(values);
}
// returns infinite sequential ordered stream
static <T> Stream<T> iterate(T seed, UnaryOperator<T> f){}
// produces sequential unordered stream by invoking Supplier
static <T> Stream<T> generate(Supplier<T> s)
}
List<BigDecimal> nums =
Stream.iterate(BigDecimal.ONE, n -> n.add(BigDecimal.ONE) )
.limit(10)
.collect(Collectors.toList());
System.out.println(nums);
// prints [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Stream.generate(Math::random)
.limit(10)
.forEach(System.out::println)
IntStream, LongStream, DoubleStream
static IntStream range(int startInclusive, int endExclusive)
static IntStream rangeClosed(int startInclusive, int endInclusive)
static LongStream range(long startInclusive, long endExclusive)
static LongStream rangeClosed(long startInclusive, long endInclusive)
List<Integer> ints = IntStream.range(10, 15)
.boxed() // without boxed() DOES NOT COMPILE
.collect(Collectors.toList());
Boxed streams
List<Integer> ints = IntStream.of(3, 1, 4, 1, 5, 9)
.boxed() // to convert int to Integer for collection
.collect(Collectors.toList());
List<Integer> ints = IntStream.of(3, 1, 4, 1, 5, 9)
.mapToObj(Integer::valueOf)
.collect(Collectors.toList())
Reduction operations
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:
OptionalInt reduce(IntBinaryOperator op)
int reduce(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 stream
int 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); // thisisalist
Stream.of("this", "is", "a", "list")
.collect(Collectors.joining());
Most general form of reduce
<U> U reduce(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>
public class Book {
private Integer id;
private String title;
}
books.stream()
.reduce(new HashMap<Integer,Book>(), // collect to
(map, book) -> { // how to add one element
map.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 means
public default IntStream chars() {...}
public default IntStream codePoints() {...}
Last updated
Was this helpful?