java

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

소리소리솔소리 2023. 5. 3. 17:52

스트림(Stream)이란?

스트림(Stream)은 자바 8부터 추가된 컬렉션(배열 포함)의 저장 요소를 하나씩 참조해서 람다식으로 처리할 수 있도록 해주는 반복자이다.

Iterator과 비슷한 역할을 하지만 람다식으로 요소 처리 코드를 제공하여 코드가 좀 더 간결하다는 점과 내부 반복자를 사용하므로 병렬처리가 쉽다는 점에서 차이가 있다.

Iterator과 Stream의 코드 비교

ArrayList<Integer> list = new ArrayList<Integer>(Arrays.asList(1,2,3));
Iterator<Integer> iter = list.iterator();
while(iter.hasNext()) {
    int num = iter.next();
    System.out.println("값 : "+num);
}

자바 7 이전까지는 ArrayList에서 요소를 순차적으로 처리하기 위해 Iterator 반복자를 위와 같이 사용했다.

 

ArrayList<Integer> list = new ArrayList<Integer>(Arrays.asList(1,2,3));
Stream<Integer> stream = list.stream();
stream.forEach(num -> System.out.println("값 : "+num));

하지만 위와 같이 자바 8 이후부터 추가된 스트림을 사용하면 훨씬 단순하게 코딩을 할 수 있다. 자바 8 이후에 작성된 코드에서는 람다식으로 기술된 부분엔 꼭 Stream이 들어가는 부분이 많다.

위에서는 stream() 메서드로 스트림 객체를 얻고 stream.forEach(num → System.out.println(”값 : “+num));에서 ArrayList에 있는 요소들을 하나씩 출력한다.

stream.forEach() 메서드는 Consumer 함수적 인터페이스 타입의 매개값을 가지므로 컬렉션의 요소를 소비할 코드를 람다식으로 기술할 수 있다.

 

스트림(Stream) 사용법

배열에서 스트림 사용

String[] strArray = {"홍길동", "이순신", "임꺽정"};
Stream<String> strStream = Arrays.stream(strArray);
strStream.forEach(a -> System.out.println(a + ", "));
System.out.println();

 

클래스에서 스트림 사용

class Student {
		private String name;
		private int score;

		public Student(String name, int score) {
				this.name = name;
				this.score  score;
		}

		public String getName() { return name; }
		public int getScore() { return score; }
}


public class FromCollectionExample {
		public static void main(String[] args) {
				List<Student> studentList = Arrays.asList(
						new Student("홍길동", 10),
						new Student("이순신", 20),
						new Student("임꺽정", 30)
				);

				Stream<Student> stream = studentList.stream();
				stream.forEach(s -> System.out.println("이름 : "+s.getName()));
		}
}

 

 

람다식이란?

메서드를 하나의 간결한 식(expression)으로 표현한 것

메서드를 람다식으로 표현하면 메서드의 이름과 반환값이 없어지므로 람다식을 ‘익명 함수(anonymous function)’ 라고도 한다.

람다식은 FunctionInterface의 조건을 충족해야 사용이 가능하다.

FunctionInterface : 오직 하나의 메서드 선언을 갖는 인터페이스

 

 

람다식 사용법

메서드의 이름과 반환타입을 제거하고 매개변수 선언부와 body{ } 사이에 → 를 추가한다.

 

기존

ReturnType methodName(Parameter p) { 
	// body
}

 

람다식

(Parameter p) -> {
	// body
}

 

  • 두 값 중에 큰 값을 반환하는 메서드 max()를 람다식으로 변환하기

기존

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

 

람다식

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

 

  • return문 대신 식(expression)으로 대신할 수 있다. 식의 연산 결과가 자동으로 반환값이 된다. 문장이 아닌 식으로 끝에 세미콜론( ; )을 붙이지 않는다.

 

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

 

  • 매개변수의 타입은 추론이 가능한 경우(대부분의 경우) 생략 가능하다.
    참고로 반환 타입을 제거할 수 있는 이유도 항상 추론이 가능하기 때문이다.
(a, b) -> a > b ? a : b

 

 

람다식 작성 문법 정리

[ 기본적인 작성 규칙 ]

  • 이름과 반환타입은 작성하지 않는다. (anonymous function)

[ 매개변수 ]

  • 추론이 가능한 매개변수의 타입은 생략할 수 있다. 단, 매개변수가 두 개 이상일 경우 일부의 타입만 생략하는 것은 허용되지 않는다.

생략 전

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

 

생략 후

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

 

  • 선언된 매개변수가 하나의 경우 괄호( )를 생략할 수 있다. 단, 매개변수의 타입을 작성한 경우엔 매개변수가 하나라도 괄호( )를 생략할 수 없다.
a -> a * a	// OK
 
int a -> a * a	// Error

 

[ body { } ]

  • return문(return statement) 대신 식(expression)으로 대체할 수 있다. 단, 식(expression)의 끝에 세미콜론( ; )은 붙이지 않는다.

생략 전

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

 

생략 후

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

 

  • 괄호{ } 안의 문장이 하나일 때는 괄호{ }를 생략할 수 있다. 이 때, 문장의 끝에 세미콜론( ; )은 붙이지 않는다. 그러나 return 문은 괄호를 생략할 수 없다.

생략 전

(String name, int i) -> { System.out.println(name + "=" + i); }

 

생략 후

(String name, int i) -> System.out.println(name + "=" + i)