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 - 람다와 스트림

  • 506