Java

[JAVA] 람다식과 스트림 Stream()

HSRyuuu 2023. 5. 25. 16:41

함수형 프로그래밍

함수의 구현과 호출만으로 프로그래밍이 수행되는 방식

순수함수(pure function)를 구현하고 호출함으로써, 외부 자료에 side effect를 주지 않도록 구현

순수함수 : 매개변수만을 사용하여 만드는 함수
→ 따라서 함수 외부에 있는 변수를 사용하지 않아 외부에 영향을 주지 않음

 

함수를 처리할 때 외부 자료를 사용하지 않아서 외부에 영향을 미치지 않기 때문에 여러 자료가 동시에 수행되는 병렬처리가 가능하다.


1. 람다식

  • 익명 함수
  • 매개변수와 매개변수를 이용한 실행문을 이루어진다.
  • (매개변수) -> { 실행문 };

람다식의 장점

  • 코드가 간결해짐
  • 코드 가독성이 높아짐
  • 생산성이 높아짐

람다식의 단점

  • 코드 재사용 불가
  • 디버깅이 어려움

람다식 표현 규칙

메서드 이용

int add(int x, int y){
	return x+y;
}

람다식으로 변환

(int x, int y) -> {return x+y;}

1. 매개변수가 하나인 경우

자료형과 괄호 생략 가능

str -> {System.out.println(str);}

매개변수가 두개 이상인 경우 괄호를 생략할 수 없음

(x,y) -> {System.out.println(x+y);}

x,y -> {System.out.println(x+y);} // 오류

2. 실행문이 한 문장인 경우

중괄호 생략 가능

str -> System.out.println(str);

실행문이 한 문장이라도 반환문은 중괄호 생략 불가능

str -> return str.length(); // 오류

실행문이 한문장인 반환문의 경우 return과 중괄호를 모두 생략

str -> str.length();

2. 함수형 인터페이스

  • 람다식을 선언하기 위해 만드는 인터페이스
  • 인터페이스 내에 메서드는 하나만 선언해야 함
  • 이를 방지하기 위해 @FunctionalInterface 애노테이션 사용
@FunctionalInterface
public interface Calc {
    public int calc(int x, int y);
}

해당 인터페이스의 메서드를 오버라이딩 하여 사용하거나, 람다식으로 선언한 뒤 사용할 수 있다.

@override

Calc c1 = new Calc() {
    @Override
    public int calc(int x, int y) {
        return x+y;
    }
};
System.out.println(c1.calc(1,2));

람다식으로 선언

Calc multiple = (x,y) -> x*y;
System.out.println(multiple.calc(1,2));
Calc getMax = (x,y) -> (x>y)?x:y;
System.out.println(getMax.calc(1,2));

3. 스트림(stream)

  • Stream은 배열, 컬렉션 등을 대상으로 데이터를 하나씩 참조하여 연산을 수행한다.
  • for문의 사용을 줄여 코드를 간결하게 한다.

배열 스트림

Arrays.stream(arr)

String[] arr = new String[]{"hello", "world", "!!"};
Stream stream = Arrays.stream(arr);

컬렉션 스트림

list.stream()

List<Integer> list = new ArrayList<>(Arrays.asList(1,2,3));
Stream stream = list.stream();

중간연산 / 최종연산

스트림은 중간연산과 최종연산을 구분된다.

  • 중간연산 : filter(), map(), sorted() 등 조건에 따라 요소를 추출하거나 변환한다.
  • 최종연산 : 결과를 반환한다.

중간연산

forEach()

요소들을 반복 처리한다.

int[] arr = {1,2,3,4,5};
List<Integer> list = new LinkedList<>();
Arrays.stream(arr).forEach(n -> list.add(n*10));
System.out.println(list.toString());  //[10, 20, 30, 40, 50]

filter()

조건에 맞는 요소들을 추출한다.

IntStream intStream = IntStream.range(1, 10).filter(n -> n % 2 == 0 && n>3);

예제

List<Integer> list = new LinkedList<>(Arrays.asList(1,2,3,4,5));
List<Integer> copyList = new LinkedList<>();

list.stream().filter(n -> n>3)
        .forEach(n-> copyList.add(n));
System.out.println(copyList.toString()); // [4, 5]

map()

각각의 요소들을 변환한다.

IntStream intStream = IntStream.range(1, 10).map(n -> n+1);

예제

List<Member> list = new LinkedList<>(
        Arrays.asList(new Member("m1",10), new Member("m2", 15),
        new Member("m3", 25), new Member("m4", 30)));


List<Integer> ageList = new LinkedList<>();

list.stream().map(member -> member.name).forEach(name -> System.out.print(name+" "));
// m1 m2 m3 m4
list.stream().map(member -> member.age).filter(age -> age<100).forEach(age -> ageList.add(age));
// ageList : [ 10,15,25,30 ]
class Member{
    String name;
    int age;
    public Member(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

sorted()

요소들을 정렬한다.

List<Integer> list = new LinkedList<>(Arrays.asList(5,2,3,4,1));
list.stream().sorted().forEach(n -> System.out.print(n+" "));
//1 2 3 4 5

최종연산

sum(), average()

List<Integer> list = new LinkedList<>(Arrays.asList(1,2,3,4,5));

int sum = list.stream().mapToInt(Integer::intValue).sum(); //15
double avg = list.stream().mapToInt(Integer::intValue).average().getAsDouble();//3.0

int sum2 = IntStream.range(1, 5).sum(); //10, 1부터 4까지
double avg2 = IntStream.range(1, 5).average().getAsDouble(); //2.5, 1부터 4까지

max(), min()

List<Integer> list = new LinkedList<>(Arrays.asList(1,2,3,4,5));

int max = list.stream().mapToInt(Integer::intValue).max().getAsInt(); //5
int min = list.stream().mapToInt(Integer::intValue).min().getAsInt(); //1

IntStream.range(1,6).max().getAsInt(); // 5
IntStream.range(1,6).min().getAsInt(); // 1

reduce()

앞서 계산한 결과에 이어서 계산한다.

(reduce는 Optional로 반환함)

List<Integer> listRD = new LinkedList<>(Arrays.asList(1,2,3,4,5));
System.out.println(listRD.stream().reduce((x,y) -> x*y).get());
//120

스트림 직접 사용

builder()

Stream builder = Stream.builder().add(1).add(2).add(3).build();
builder.forEach(n-> System.out.println(n));
1
2
3

generate()

Stream gen = Stream.generate(()->"hello").limit(3); //limit : 반복 횟수
gen.forEach(System.out::println);
hello
hello
hello

iterate()

iterate(초기값, 람다식) 으로 구성

초기값부터 시작하여 람다식에 따라 연산한다.

Stream iter = Stream.iterate(10, n->n*2).limit(5); //limit : 반복 횟수
iter.forEach(System.out::println);
10
20
40
80
160
반응형