Lambda & Stream
Lambda
function addition(a, b) { return a + b; } addition(1, 2);
(function(a, b) { return a + b; })(1, 2);
((a, b) => a + b)(1, 2) // ES 6
Lambda
함수가 반드시 이름을 가질 필요는 없다.
Lambda
Java 1.8
public static void main(String[] args) {
final List<Integer> numbers = Arrays.asList(1, 5, 3, 4, 2);
numbers.sort(new Comparator<Integer>() {
@Override public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
}
public static void main(String[] args) {
final List<Integer> numbers = Arrays.asList(1, 5, 3, 4, 2);
numbers.sort(new Comparator<Integer>() {
@Override public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
}
public static void main(String[] args) {
final List<Integer> numbers = Arrays.asList(1, 5, 3, 4, 2);
numbers.sort(new Comparator<Integer>() {
@Override public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
numbers.sort((Integer o1, Integer o2) -> {
return o1 - o2;
});
}
Lambda
Functional Interface
Functional Interface == Interface
Functional Interface ==
(Interface && Single Method)
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
Functional Interface ==
(Interface && Single Method)
Single Method != (default || static)
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static <T> Function<T, T> identity() {
return t -> t;
}
}
Closure
function Function() {
var name = "ABC";
return function display() {
console.log(name);
}
}
function Function() {
var name = "ABC";
return function display() {
console.log(name);
}
}
function Function() {
var name = "ABC";
return function display() {
console.log(name);
}
}
Free variable
public static void main(String[] args) {
final String name = "ABC";
new Thread(new Runnable() {
@Override public void run() {
System.out.println(name);
}
}).start();
}
public static void main(String[] args) {
final String name = "ABC";
new Thread(new Runnable() {
@Override public void run() {
System.out.println(name);
}
}).start();
}
public static void main(String[] args) {
String name = "ABC";
new Thread(() -> System.out.println(name)).start();
new Thread(() -> System.out.println(name += "DEF")).start();
}
public static void main(String[] args) {
String name = "ABC";
new Thread(() -> System.out.println(name)).start();
new Thread(() -> System.out.println(name += "DEF")).start();
}
java: local variables referenced from
a lambda expression
must be final or effectively final
Type Inference
Map<String, List<Integer>> map1 = new HashMap<String, List<Integer>>();
List<Number> list1 = new ArrayList<Number>();
Map<String, List<Integer>> map2 = new HashMap<>();
List<Number> list2 = new ArrayList<>();
JAVA 1.7
Map<String, List<Integer>> map = new HashMap<>();
List<Number> list = new ArrayList<>();
Arrays.asList(1, 5, 3, 4, 2).sort((Integer o1, Integer o2) -> {
return o1 - o2;
});
Arrays.asList(1, 5, 3, 4, 2).sort((o1, o2) -> {
return o1 - o2;
});
Arrays.asList(1, 5, 3, 4, 2).sort((o1, o2) -> o1 - o2);
JAVA 1.8
Default Function
Function<T, R>
BiFunction<T, U, R>
Function<Integer, Integer> func = (n) -> n + n;
func.apply(5); // 10
func.compose(func).apply(5); // 20
BiFunction<Integer, Integer, Integer> biFunc = (n1, n2) -> n1 + n2;
biFunc.apply(3, 7); // 10
biFunc.andThen(func).apply(3, 7); // 20
Consumer<T>
BiConsumer<T, U>
Consumer<String> con = (s) -> System.out.println(s);
con.accept("ABC");
con.andThen(con).accept("ABC");
BiConsumer<String, Integer> biCon = (s, n) -> System.out.println(s + n);
biCon.accept("1 + 2 = ", 3);
Predicate<T>
BiPredicate<T, U>
Predicate<Integer> pre = (n) -> n > 0;
pre.test(3); // true
pre.and(pre.or(pre)).or(pre.and(pre)).negate().test(1); false
BiPredicate<Integer, Integer> biPre = (n1, n2) -> (n1 + n2) > 0;
biPre.test(1, 3); // true
Supplier<T>
Supplier<Integer> sup = () -> 1 + 3;
sup.get(); // 4
Stream
final List<Integer> integers = Arrays.asList(1, 5, 3, 4, 2);
integers.sort(new Comparator<Integer>() {
@Override public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
for (int i = 0; i < integers.size(); i++) {
Integer num = integers.get(i);
if (num > 0) {
integers.set(i , num + 1);
}
}
for (int i = 0; i < integers.size(); i++) {
System.out.println(integers.get(i));
}
final List<Integer> integers = Arrays.asList(1, 5, 3, 4, 2);
integers.sort(new Comparator<Integer>() {
@Override public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
int index = 0;
for (Integer num : integers) {
if (num > 0) {
integers.set(index, num + 1);
}
index++;
}
for (Integer num : integers) {
System.out.println(num);
}
final List<Integer> integers = Arrays.asList(1, 5, 3, 4, 2);
integers.sort(new Comparator<Integer>() {
@Override public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
int index = 0;
for (Integer num : integers) {
if (num > 0) {
integers.set(index, num + 1);
}
index++;
}
for (Integer num : integers) {
System.out.println(num);
}
Stream.of(1, 5, 3, 4, 2)
.sorted()
.filter(n -> n > 0)
.map(n -> n + 1)
.forEach(n -> System.out.println(n));
Creating Stream
Integer[] arr = { 1, 2, 3, 4, 5 };
Stream<Integer> stream1 = Arrays.stream(arr);
Stream<Integer> stream2 = Stream.of(arr);
Stream<Integer> stream3 = Stream.of(1, 2, 3, 4, 5);
Arrays
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
Stream<Integer> stream = list.stream();
Collections
Stream.iterate(0, n -> n + 1)
.limit(100)
.forEach(n -> System.out.println(n));
Stream.generate(() -> Math.random())
.limit(100)
.forEach(n -> System.out.println(n));
Unlimited Stream
Stream Operator
Stream.of(1, 2, 3)
.map(n -> n + n)
.collect(Collectors.toList());
intermediate operation
terminal operation
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
Stream<T> filter(Predicate<? super T> predicate);
Stream<T> distinct();
Stream<T> sorted();
Stream<T> sorted(Comparator<? super T> comparator);
Stream<T> skip(long n);
Stream<T> limit(long maxSize);
Stream<T> peek(Consumer<? super T> action);
void forEach(Consumer<? super T> action);
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();
<R> Stream<R> flatMap(
Function<? super T, ? extends Stream<? extends R>> mapper);
<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper)
<R, A> R collect(Collector<? super T, A, R> collector);
<R> R collect(Supplier<R> supplier,
BiConsumer<R, ? super T> accumulator,
BiConsumer<R, R> combiner);
Stream Sample
final List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
final List<Integer> filter = numbers.stream().filter(n -> n > 0)
.collect(Collectors.toList());
Optional<Integer> findAny = numbers.stream()
.filter(n -> n > 0)
.findAny();
boolean allMatch = numbers.stream().allMatch(n -> n > 0);
boolean noneMatch = numbers.stream().noneMatch(n -> n > 0);
numbers.stream()
.distinct()
.filter(n -> n > 0)
.map(n -> n * n)
.sorted(Comparator.comparingInt(o -> o))
.forEach(System.out::println);
numbers.stream()
.distinct()
.filter(n -> n % 2 == 0)
.map(n -> n * n)
.sorted()
.findFirst()
.orElseGet(() -> {
System.out.println("Empty!");
return 0;
});
users.stream()
.distinct()
.flatMap(u -> u.getPhoneNumbers.stream())
.filter(n -> isKoreaCountryCode(n))
.collect(Collectors.toList());
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.query("SELECT * FROM .users WHERE age > ?",
ps -> ps.setInt(1, 10),
(rs, rowNum) -> new User(
rs.getLong("SID"),
rs.getString("NAME"),
rs.getInt("AGE"))
);
RestTemplate restTemplate = new RestTemplate();
restTemplate.execute(downloadUrl, HttpMethod.GET, (req) -> {
HttpHeaders httpHeaders = req.getHeaders();
httpHeaders.
setAccept(Collections.singletonList(
MediaType.APPLICATION_OCTET_STREAM));
}, (res) -> {
Path path = fileInfo.getPath();
Files.copy(
res.getBody(), path, StandardCopyOption.REPLACE_EXISTING);
return fileInfo.getSaveFileInfo();
});
Precautions
Stream<Integer> numberStream = Stream.of(1, 2, 3, 4, 5);
numberStream.forEach(System.out::println);
boolean allMatch = numberStream.allMatch(n -> n > 0);
스트림은 재활용 X
final List<Integer> numbers = Arrays.asList(1, 2, 3);
numbers.stream().map(n -> {
System.out.println("map: " + n);
return n + n;
}).filter(n -> {
System.out.println("filter: " + n);
return true;
}).forEach(n -> System.out.println("forEach: " + n));
map: 1
filter: 2
forEach: 2
map: 2
filter: 4
forEach: 4
map: 3
filter: 6
forEach: 6
스트림의 연산자는 순차실행 X
final List<Integer> numbers = Arrays.asList(1, 3, 2);
numbers.stream().map(n -> {
System.out.println("map: " + n);
return n + n;
}).filter(n -> {
System.out.println("filter: " + n);
return n > 5;
}).forEach(n -> System.out.println("forEach: " + n));
map: 1
filter: 2
map: 3
filter: 6
forEach: 6
map: 2
filter: 4
스트림의 연산자는 순차실행 X
final List<Integer> numbers = Arrays.asList(1, 3, 2);
numbers.stream().map(n -> {
System.out.println("map1: " + n);
return n + n;
}).map(n -> {
System.out.println("map2: " + n);
return n * n;
}).findFirst()
.ifPresent(n -> System.out.println("forEach: " + n));
map1: 1
map2: 2
forEach: 4
스트림의 연산자는 순차실행 X
final List<Integer> numbers = new ArrayList<>();
numbers.add(1); numbers.add(2); numbers.add(3);
numbers.stream().map(n -> {
System.out.println("map: " + n);
return n + n;
}).peek(numbers::remove)
.forEach(n -> System.out.println("forEach: " + n));
map: 1
forEach: 2
map: 3
forEach: 6
map: null
Exception in thread "main" java.lang.NullPointerException
스트림의 연산자는 순차실행 X
Stream.iterate(0, n -> n + 1)
.map(n -> n * n)
.forEach(System.out::println);
Stream.generate(Math::random)
.map(n -> n * n)
.limit(100)
.forEach(System.out::println);
무한 스트림 생성 주의
Stream.iterate(0, n -> n)
.distinct()
.limit(100)
.forEach(System.out::println);
Stream.iterate(0, n -> n)
.parallel()
.distinct()
.limit(100)
.forEach(System.out::println);
무한 스트림 생성 주의
E N D
2016/05/26 Feature Complete
2016/12/22 Feature Extension Complete 2017/01/05 Rampdown Start
2017/02/09 All Tests Run
2017/02/16 Zero Bug Bounce
2017/03/16 Rampdown Phase Two 2017/06/22 Initial Release Candidate 2017/07/06 Final Release Candidate 2017/09/21 General Availability
JAVA 9
Lambda & Stream
By serivires
Lambda & Stream
자바 8 - 람다와 스트림
- 538