
int[] arr = new int[5];
Arrays.setAll(arr, (i)->(int)(Math.random()*5)+1);
// 위와 동일 한식
int method(){
return (int)(Math.random()*5)+1;
}
메서드는 함수와 같은 의미이지만, 특정 클래스에 반드시 속해야 한다는 제약이 있기 때문에 기존의 함수와 같은 의미의 다른 용어를 선택해서 사용한다. 그러나 이제 다시 람다식을 통해 메서드가 하나의 독립적인 기능을 하리 때문에 함수라는 용어가 사용되게 된다.
반환 타입 메서드 이름(매개변수 선언){
문장들
}
-> 변경 ->
(매개변수 선언) -> { 문장들 }
// 예시 (변경 전)
int max(int a, int b){
return a>b ? a : b;
}
// 예시 (변경 후, 전부 가능)
(int a, int b) -> a>b ? a : b
(int a, int b) -> { return a>b ? a : b;}
(a, b) -> a>b ? a : b
MyFunction f = (int a, int b) -> a>b ? a : b; // 익명 객체를 람다식으로 대체
int big = f.max(5,3); // 익명 객체의 메서드를 호출
@FunctionalInterface
interface MyFunction { // 함수형 MyFunction을 정의
public abstract int max(int a, int b);
}
'@FunctionalInterface'를 붙이면, 컴파일러가 함수형 인터페이스를 올바르게 정의하였는지 확인해주므로, 꼭 붙이는 것이 좋다.
@FunctionalInterface
interface MyFunction{
void myMethod(); // 추상 메서드
}
void aMethod(MyFunction f){ // 매개변수의 타입이 함수형 인터페이스
f.myMethod(); // MyFunction에 정의된 메서드 호출
}
...
MyFunction f = () -> System.out.println("myMethod()");
aMethod(f);
// 또한 참조변수 없이 아래와 같이 직접 람다식을 매개변수로 지정하는 것도 가능하다.
aMethod(() -> System.out.println("myMethod()")); // 람다식을 매개변수로 지정
@FunctionalInterface
interface MyFunction{
void run(); // public abstract void run();
}
class LambdaEx1{
static void execute (MyFunction f){ // 매개변수 타입이 MyFunction인 메서드
f.run();
}
static MyFunction getMyFunction(){ // 반환 타입이 MyFunction인 메서드
MyFunction f = () -> System.out.println("f3.run()");
return f;
}
public static void main(String[] args){
// 람다식으로 MyFunction의 run()을 구현
MyFunction f1 = () -> System.out.println("f1.run()");
MyFunction f2 = new MyFunction(){ // 익명클래스로 run()을 구현
public void run(){
System.out.println("f2.run()");
}
}
MyFunction f3 = getMyFunction();
f1.run();
f2.run();
f3.run();
execute(f1);
execute(()->System.out.println("run()"));
}
}
MyFunction f = (MyFunction)(()->{}); // 양변의 타입이 다르므로 형변환이 필요
Object obj = (Object)(()->{}); // 에러. 함수형 인터페이스로만 형변환 가능

Predicate<String> isEmptyStr = s -> s.length() == 0;
String s = "";
if (isEmptyStr.test(s)) // if(s.length() == 0)
System.out.println("This is an empty String.");

매개변수의 타입으로 보통 'T'의 다음 문자인 'U', 'V', 'W'를 매개변수의 타입으로 사용하는 것일 뿐 별다른 의미는 없다.
@FunctionalInterface
interface TriFunction<T, U, V, R>{
R apply(T t, U u, V v)
}


import java.util.*;
class LambdaEx4{
public static void main(String[] args){
ArrayList<Integer> list = new ArrayList<>{}'
for (int i=0;i<10;i++)
list.add(i);
// list의 모든 요소를 출력
list.forEach(i->System.out.println(i+","));
System.out.println();
// list에서 2 또는 3의 배수를 제거한다.
list.removeIf(x-> x%2==0 || x%3==0);
System.out.println(list);
list.replaceAll(i->i*10); // list의 각 요소에 10을 곱한다.
System.out.println(list);
Map<String, String> map = new HashMap<>();
map.put("1", "1");
map.put("2", "2");
map.put("3", "3");
map.put("4", "4");
// map의 모든 요소를 {k, v}의 형식으로 출력한다.
map.forEach((k,v) -> System.out.println("{"+k+","+v+"},"));
System.our.println();
}
}
import java.util.function.*;
import java.util.*;
class LambdaEx5{
public static void main(String[] args){
Supplier<Integer> s = ()-> (int)(Math.random()*100)+1;
Consumer<Integer> c = i -> System.out.print(i+", ");
Predicate<Integer> p = i -> i%2==0;
Function<Integer, Integer> f = i -> i/10*10; // i의 일의 자리를 없앤다.
List<Integer> list = new ArrayList<>();
makeRandomList(s, list);
System.out.println(list);
printEventNum(p, c, list);
List<Integer> newList = doSomething(f, list);
System.out.println(newList);
}
static <T> List<T> doSomething(Function<T, T> f, List<T> list){
List<T> newList = new ArrayList<T>(list.size());
for(T i: list){
newList.add(f.apply(i));
}
return newList;
}
static <T> void printEvenNum(Predicate<T> p, Consumer<T> c, List<T> list){
System.out.println("[");
for(T i : list){
if(p.test(i))
c.accept(i);
}
System.out.println("]");
}
static <T> void makeRandomList(Supplier<T> s, List<T> list){
for (int i=0;i<10;i++)
list.add(s.get());
}
}
원래 Function인터페이스는 반드시 두개의 타입을 지정해 줘야 하기 때문에, 두 타입이 같아도 Function< T >라고 쓸수 없다. Function<T,T>라고 써야 한다.
default <V> Function<T,V> andThen(Function<? super R,? extends V> after)
default <V> Function<V,R> compose(Function<? super V,? extends T> before)
static <T> Function<T,T> identity()
default Predicate<T> and(Predicate<? super T> other)
default Predicate<T> or(Predicate<? super T> other)
default Predicate<T> negate()
static <T> Predicate<T> isEqual(Object targetRef)
Function<String, Interger> f = (String s) -> Integer.parseInt(s);
// 보통은 이렇게 람다식을 작성하는데, 이 람다식을 메서드로 표현하면 아래와 같다.
Interger wrapper(String s){ // 이 메서드의 이름은 의미없다.
return Integer.parseInt(s);
}
// 더 간략히 표현하면 아래와 같다.
Function<String, Integer> f = Integer::parseInt; //메서드 참조
하나의 메서드만 호출하는 람다식은 '클래스이름::메서드이름' 또는 '참조변수::메서드이름'으로 바꿀 수 있다.
Supplier<MyClass> s = () -> new MyClass(); // 람다식
Supplier<MyClass> s = () -> MyClass::new; // 람다식
List<String> sortedList = strStream2.sorted().collect(Collectors.toList());
stream.forEach(System.out::println);
//System.out::println == (str)->System.out.println(str)
중간 연산 : 연산 결과가 스트림인 연산. 스트림에 연속해서 중간 연산할 수 있음
최종 연산 : 연산 결과가 스트림이 아닌 연산. 스트림의 요소를 소모하므로 단 한번만 가능
parallel()과 Sequential()은 새로운 스트림을 생성하는 것이 아니라, 그저 스트림의 속성을 변경할 뿐이다.
int sum = strStream.parallel() // strStream을 병렬 스트림으로 전환
.mapToInt(s->s.length())
.sum();
Stream<T> Collection.stream();
-배열을 소스로 하는 스트림을 생성하는 메서드는 다음와 같이 Stream과 Arrays에 static메서드로 정의되어 있다.
Stream<T> Stream.of(T... values) // 가변 인자
Stream<T> Stream.of(T[])
Stream<T> Arrays.stream(T[])
Stream<T> Arrays.stream(T[] array, int startInclusive, int endExclusive);
IntStream IntStream.range(int begin, int end)
IntStream IntStream.rangeClosed(int begin, int end)
난수를 생성하는데 사용하는 Random클래스에는 아래와 같은 인스턴스 메서드들이 포함되어 있다. 이 메서드들은 해당 타입의 난수들로 이루어진 스트림을 반환한다.
이 메서드들이 반환하는 스트림은 크기가 정해지지 않은 '무한 스트림(infinite stream)'이므로 limit()도 같이 사용해서 스트림의 크기를 제한해 주어야 한다. limit()은 스트림의 개수를 지정하는데 사용되며, 무한 스트림을 유한 스트림으로 만들어 준다.
Stream emptyStream = Stream.empty();
long count = emptyStream.count();
IntStream intStream = IntStream.rangeClosed(1, 10);
intStream.skip(3).limit(5).forEach(System.out::println);
import java.util.*;
import java.util.stream.*;
class StreamEx4{
Stream<String[]> strArrStrm = Stream.of{
new String[] {"abc", "def", "jkl"},
new String[] {"ABC", "DEF", JKL""}
};
// Stream<Stream<String>> strStrmStrm = strArrStrm.map(Arrarys::stream);
Stream<String> strStrm = strArrStrm.flatMap(Arrays::stream);
strStrm.map(String::toLowerCase)
.distinct()
.sorted()
.forEach(System.out::println);
System.out.println();
String[] lineArr = {
"Believe or not It is true",
"Do or do not There is no try",
};
Stream<String> lineStream = Arrays.stream(lineArr);
lineStream.flatMap(line -> Stream.of(line.split(" +")))
.map(String::toLowerCase)
.distinct()
.sorted()
.forEach(System.out::println);
System.out.println();
Stream<String> strStrm1 = Stream.of("AAA", "ABC", "bBb", "Dd");
Stream<String> strStrm2 = Stream.of("bbb", "aaa", "ccc", "dd");
Stream<Stream<String>> strStrmStrm = Stream.of(strStrm1, strStrm2);
Stream<String> strStream = strStrmStrm
.map(s -> s.toArray(String[]::new))
.flatMap(Arrays::stream);
strStream.map(String::toLowerCase)
.distinct()
.forEach(System.out::println);
}
}
기본형 스트림의 min(), max()와 달리 매개변수로 Comparator를 필요로 한다는 차이가 있다.
collect() : 스트림의 최종연산, 매개변수로 컬렉터를 필요로 한다.
Collector : 인터페이스, 컬렉터는 이 인터체이스를 구현해야 한다.
Collectors : 클래스, static메서드로 미리 작성된 컬렉터를 제공한다.
sort() 할 때, Comparator가 필요한 것처럼 collect()할 때는 Collector가 필요하다.
List<String> names = stuStream.map(Student::getName)
.collect(Collector.toList());
ArrayList<String> list = names.stream()
.collect(Collectors.toCollection(ArrayList::new));
// Map은 키와 쌍으로 저장해야하므로 객체의 어떤 필드를 키로 사용할지와 값으로 사용할지를 지정해줘야함
Map<String, Person> map = personStream
.collect(Collectors.toMap(p->p.getRegId(), p->p));
Collector groupingBy(Function classifier)
Collector groupingBy(Function classifier, Collector downstream)
Collector groupingBy(Function classifier, Supplier mapFactory, Collector downstream)
Collctor partitioningBy(Predicate predicate)
Collctor partitioningBy(Predicate predicate, Collector downstream)
public interface Collector<T, A, R>{
Supplier<A> supplier();
BiConsumer<A, T> accumulator();
BinaryOperator<A> combiner();
Function<A, R> finisher();
Set<Characteristics> characteristics(); // 컬렉터의 특성이 담긴 Set을 반환
...
}
supplier() : 작업 결과를 저장할 공간을 제공
accumulator() : 스트림의 요소를 수집(collect)할 방법을 제공
combiner() : 두 저장공간을 병합할 방법을 제공(병렬 스트림)
finisher() : 결과를 최종적으로 변환할 방법을 제공
public Function finisher(){
return Function.identity(); // 항등 함수를 반환
}
Characteristics.CONCURRENT : 병렬로 처리할 수 있는 작업
Characteristics.UNORDERED : 스트림의 요소의 순서가 유지될 필요가 없는 작업
Characteristics.IDENTITY_FINISH : finisher()가 항등 함수인 작업