본문 바로가기

Backend Study/Java

[JAVA] 람다와 스트림

1.1 람다식이란? 

함수를 간단한 식(Expression)으로 표현하는 방법이다.

 

int max(int a, int b){
	return a>b ? a : b ;
}

// expression으로 표현
(a,b) -> a > b ? a : b

[매개변수] -> [내용]

 

* 함수와 메서드의 차이

함수는 일반적 용어이고, 메서드는 객체지향개념 용어이다. 함수는 클래스에 독립적이며, 메서드는 클래스에 종속적이다. 

함수는 여러 문장들이 하나의 기능을 구현하도록 구성한 것이라고 할 수 있다.

그 함수 중에서 클래스 내부에 정의한 함수 메소드라고 부르는 것이다. 

 

1.2 람다식 작성하기 

예시)

 

int max(int a, int b){
	return a > b ? a : b ;
}

 

- 메서드의 이름과 반환타입을 제거하고 '->'를 블록 {} 앞에 추가한다.

 

(int a, int b) -> {
	return a > b ? a : b ;
}

 

 

- 반환값이 있는 경우, 식이나 값만 적고 return 문 생략 가능 

 

(int a, int b) -> a > b ? a : b

 

- 매개변수의 타입이 추론 가능하면 생략 가능 (대부분 가능 )

 

(a,b) -> a > b ? a: b

 

*  주의사항

- 매개변수가 하나인 경우, 괄호 () 생략 가능한데 이는 타입이 없을때만 해당한다.

 

(a) -> a * a
a -> a * a 
//가능

(int a) -> a * a
int a -> a * a      // 에러
//불가능

 

- 블록 안의 문장이 하나뿐 일 때, 괄호 {} 생략 가능 (끝에 ; 안붙임)

 

(int i) -> {
	System.out.println(i);
}

(int i) -> System.out.println(i)

 

-> 단, 하나뿐인 문장이 return 문이면 생략할 수 없다. 

 

2.1 스트림이란?

다양한 데이터 소스(ex. 배열, 컬렉션) 를 표준화된 방법으로 다루기 위한 것이다.  

스트림을 이용하면

 

배열 , 컬렉션(List, Set, Map)  =>  Stream  =>  [    중간연산 (여러번)   ]  =>  [    최종연산(한번)    ] 

 

과정: 스트림 만들기, 중간 연산, 최종 연산, 결과

 

흐름이 되면서 각자 달랐던 배열, list, set 들을 하나의 stream 으로 작업할 수 있다. 

스트림은 자바 8부터 지원되기 시작한 기능으로 컬렉션에 저장되어 있는 엘리먼트들을 하나씩 순회하면서 처리할 수 있는 코드 패턴이다. 

 

- 스트림 생성 

 

List<Integer> list = Arrays.asList(1,2,3,4,5);
Stream<Integer> intStream = list.stream();
Stream<String> strStream  Stream.of(new String[] {"a", "b", "c"});

 

2.2 스트림이 제공하는 기능

중간 연산 - 연산 결과가 스트림인 연산, 반복적으로 적용 가능

최종 연산 - 연산 결과가 스트림이 아닌 연산, 단 한번만 적용 가능

 

ex. 

 

stream.distinct().limit(5).sorted().forEach(System.out.println();

// distinct, limit, sorted: 중간연산
// forEach: 최종연산

 

2.2 스트림의 특징

- 스트림은 데이터 소스로부터 데이터를 읽기만할 뿐 변경하지 않는다. 

 

ex. 

 

List<Integer> list = Arrays.asList(3,1,5,4,2);
List<Integer> sortedList = list.stream().sorted()
							.collect(Collectors.toList());
System.out.println(list);				//[3,1,5,4,3]
System.out.println(sortedList); 		//[1,2,3,4,5]

 

- 스트림은 Iterator처럼 일회용이다. (필요하면 다시 스트림 생성해야함.)

 

strStream.forEach(System.out.println);
int numOfStr = strStream.count(); //에러

 

- 최종 연산 전까지 중간연산이 수행되지 않는다. : 지연된 연산

 

IntStream intStream = new Random().ints(1,46); // 1~45 범위의 무한 스트림이다.
intStream.distinct().limit(6).sorted() // 중간연산
		 .forEach(i->System.out.println(i + ",")); // 최종연산

 

-> 무한 스트림일 경우 중간연산(무한 스트림을 중복제거 할 수 없다.)이 불가능한데, 스트림을 가지고 어떤 연산을 할지 체크만 해두고  최종연산까지 진행후 나중에 수행한다. 이것이 지연 연산이다. 

 

- 스트림은 작업을 내부 반복으로 처리한다.

 

for (String str : strList)
	System.out.println(str);
    
//대신 

stream.forEach(System.out::println);

//사용한다.

 

- 스트림의 작업을 병렬로 처리한다. - 병렬 스트림

*병렬 스트림이란 각각의 스레드에서 처리할 수 있도록 스트림 요소를 여러 청크로 분할한 스트림이다. 때문에 병렬 스트림을 이용하면 멀티 코어 프로세서가 각각의 청크를 처리하도록 할당이 가능해진다.

 

Stream<String> strStream = Stream.of("dd","aaa","CC","cc","b");
int sum = strStream.parallel() // 병렬 스트림으로 전환(속성만 변경)
		.mapToInt(s -> s.length()).sum(); // 모든 문자열의 길이의 합

 

- 기본형 스트림 [IntStream, LongStream, DoubleStream]

오토박싱&언박싱의 비효율이 제거된다. (Stream <Integer> 대신 IntStream 사용, 숫자와 관련된 유용한 메서드를 Stream<T>보다 더 많이 제공한다.)

 

 

 

참고) JAVA의 정석