[Java 8] Stream API 면접 질문과 답변Language/Java2024. 7. 30. 13:08
Table of Contents
반응형
Java 면접을 준비하고 계신다면 이 글이 가장 유용할 것입니다. Java 버전의 지속적인 업그레이드 이후 면접 질문도 증가했기 때문입니다. 여기에서는 가장 자주 묻는 Java 8 Stream API 면접 질문과 답변을 정리했습니다.
Stream API
Stream API는 면접에서 면접관이 가장 많이 선택하는 주제 중 하나입니다. 가장 많이 사용되는 Stream API 질문의 개념을 알아보세요. Java 8의 Stream API에 대한 몇 가지 질문과 답변을 살펴보겠습니다.
1. Stream API란 무엇인가요?
- Java 8은
java.util.stream
이라는 새로운 추가 패키지를 제공합니다. 이 패키지는 클래스, 인터페이스, 열거형 등으로 구성되어 요소에 대한 함수형 연산을 허용합니다. java.util.stream
패키지를 사용하여 Stream API를 사용할 수 있습니다.- Stream을 사용하여 한 데이터 구조에서 다른 데이터 구조로 filter, collect, print 및 변환할 수 있습니다.
- Stream API는 요소를 저장하지 않습니다. Stream을 사용하여 수행한 작업은 소스를 수정하지 않습니다.
2. Collection과 Stream의 차이점은 무엇인가요?
- Collection과 Stream의 주요 차이점은 Collection에는 요소가 포함되어 있지만 Stream에는 요소가 포함되어 있지 않다는 것입니다.
- Stream은 요소가 Collection 또는 배열로 저장되는 뷰에서 작동하지만 다른 뷰와 달리 Stream에 대한 변경 사항은 원래 Collection에 반영되지 않습니다.
3. Stream API의 중간 연산(Intermediate operation)이란 무엇인가요?
- 중간 연산은 Stream을 출력으로 반환하고 Stream에서 터미널 연산이 호출될 때까지 중간 연산이 실행되지 않습니다. 이를 지연 평가라고 합니다.
- Stream API의 중간 연산은 현재 데이터를 처리한 다음 새 Stream을 반환합니다.
- 중간 연산이 실행되면 새 Stream만 생성됩니다.
예: map(), limit(), filter(), skip(), flatMap(), sorted(), distinct(), peek()
4. Stream API에서 터미널 연산(Terminal operation)이란 무엇인가요?
- 이름에서 알 수 있듯이 터미널은 Stream 파이프라인의 마지막 작업을 의미합니다. 터미널 연산은 Stream을 통과하여 결과 또는 Collection을 생성하지만 새 Stream은 생성하지 않습니다.
- 터미널 연산은 모든 중간 연산이 적용된 후 Stream의 결과를 생성하며, 터미널 연산이 수행되면 더 이상 Stream을 사용할 수 없습니다.
- Stream 파이프라인은 소스(Collection, array, function 또는 I/O channel)로 구성되며, 파이프라인에서 중간 작업을 호출하고 마지막으로 터미널 작업이 수행되어 Stream 파이프라인이 소비되고 닫힌 것으로 표시됩니다.
- 파이프라인의 끝에는 하나의 터미널 작업만 있을 수 있습니다. 닫힌 Stream에서 작업을 수행하면 Stream이 이미 작업되었거나 닫혔기 때문에
java.lang.IllegalStateException
이 발생합니다.
예:
- collect()
- forEach()
- forEachOrdered()
- findAny()
- findFirst()
- toArray()
- reduce()
- count()
- min()
- max()
- anyMatch()
- allMatch()
- noneMatch()
5. map() 함수는 어떤 기능을 하나요? 왜 사용하나요?
map()
함수는 자바에서 map 함수 연산을 수행합니다. 즉, 한 유형의 객체를 다른 유형으로 변환할 수 있습니다.- 문자열 목록이 있고 정수 목록을 변환하고 싶다면,
map()
함수를 사용하여 문자열을 정수로 변환합니다. 예를 들어,parseInt()
를map()
에 적용하면 목록의 모든 요소에 해당 변환을 적용하여 정수 목록을 반환합니다.
List<Integer> list = pattern.splitAsStream(ints)
.map(Integer::valueOf)
.collect(Collectors.toList());
6. filter() 메서드는 어떤 기능을 하나요? 언제 사용하나요?
filter()
는 조건부 함수를 사용하여 지정한 특정 조건을 만족하는 요소를 필터링하는 데 사용됩니다.- predicate 함수는 객체를 받아 boolean을 반환하는 함수에 불과합니다.
- 예를 들어, 정수 목록이 있고 짝수 정수 목록을 원한다고 가정해 보겠습니다.
List<Integer> list = Arrays.asList(10, 15, 8, 49, 25, 98, 32);
list.stream()
.filter(n -> n % 2 == 0)
.forEach(System.out::println);
7. flatMap() 메서드는 어떤 기능을 하나요?
flatMap()
는 map 함수의 확장입니다. 한 객체를 다른 객체로 전송하는 것 외에도 객체를 평평하게 만듭니다.- 예: 목록 데이터의 목록이 있고 목록의 모든 요소를 하나의 목록으로 결합하고 싶다고 가정해 보겠습니다. 이 경우
flatMap()
을 사용할 수 있습니다.
List<Integer> evens = Arrays.asList(2, 4, 6);
List<Integer> odds = Arrays.asList(3, 5, 7);
List<Integer> primes = Arrays.asList(2, 3, 5, 7, 11);
List<Integer> numbers = Stream.of(evens, odds, primes)
.flatMap(list -> list.stream())
.collect(Collectors.toList());
System.out.println("flattend list: " + numbers) ;
// Output: flattend list: [2, 4, 6, 3, 5, 7, 2, 3, 5, 7, 11]
8. Java Stream에서 predicate Interface란 무엇인가요?
- Predicate는 객체를 받아서 boolean 값을 반환하는 함수를 나타내는 함수형 인터페이스입니다.
- 이는 원하지 않는 요소를 필터링하기 위해 Predicate를 사용하는
filter()
와 같은 여러 Stream 함수에서 사용됩니다.
9. Stream API에서 peek() 메서드는 어떤 기능을 하나요?
- Stream 클래스의
peek()
메서드를 사용하면 Stream 파이프라인을 들여다볼 수 있습니다. peek()
는 각 요소에 대해 지정된 작업을 수행한 후 Stream의 요소로 구성된 Stream을 반환합니다. 이 함수는 각 중간 작업 후에 값을 인쇄할 때 유용합니다.- 각 단계를 들여다보고 콘솔에 의미 있는 메시지를 인쇄할 수 있습니다. 일반적으로 람다 표현식 및 Stream 처리와 관련된 문제를 디버깅하는 데 사용됩니다.
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
List<Integer> ans = list.stream()
.filter(value -> value % 2 == 0)
.peek(value -> System.out.println("Filtered " + value))
.map(value -> value * 10)
.collect(Collectors.toList());
System.out.println(Arrays.toString(ans.toArray()));
// Output:
Filtered 2
Filtered 4
[20, 40]
10. Stream API의 map()과 flatMap()의 차이점은 무엇인가요?
Java 8에서 map()
과 flatMap()
의 주요 차이점은 다음과 같습니다.
map()
연산에 전달하는 함수는 단일 값을 반환합니다.flatMap()
연산에 전달하는 함수는 값의 Stream을 반환합니다.flatMap()
은 map과 flat 연산의 조합입니다.map()
은 변환에만 사용되지만flatMap()
은 변환과 평탄화 모두에 사용됩니다.
map()
예시
List<String> names = Arrays.asList("Saket", "Trevor", "Franklin", "Michael");
List<String> upperCase = names.stream()
.map(String::toUpperCase)
.collet(Collectors.toList());
upperCase.forEach(System.out::println);
flatMap()
예시
List<List<String>> names = Arrays.asList(Arrays.asList("Saket", "Trevor"),
Arrays.asList("Shawn", "Franklin"),
Arrays.asList("johnty", "Sean"));
List<String> start = names.stream()
.flatMap(firstName -> firstName.stream())
.filter(s -> s.startsWith("s"))
.collet(Collectors.toList());
start.forEach(System.out::println);
11. 배열을 Stream으로 변환할 수 있나요?
- 예, Java를 사용하여 배열을 Stream으로 변환할 수 있습니다.
- Stream 클래스는 가변 매개변수를 허용하는
Stream.of(T...)
와 같이 배열에서 Stream을 만드는 팩토리 함수를 제공하며, 배열을 제공할 수도 있습니다.
String[] languages = {"Java", "Python", "JavaScript"};
Stream stream = Stream.of(languages);
stream.forEach(System.out::println);
12. Stream API의 Stateful 및 Stateless 중간 작업이란 무엇인가요?
- Stateful 연산은
skip()
,distinct()
,limit()
,sorted()
입니다. 나머지 모든 Stream 연산은 stateless입니다. - 연산이 현재 요소를 처리하기 위해 지금까지 처리한 요소의 정보를 유지해야 하는 경우, 이는 stateful 연산입니다.
- 예시: Distinct 연산은 지금까지 처리한 모든 값을 추적해야 하며, 이 정보를 기반으로 현재 값이 고유한 값인지 또는 이전에 처리된 적이 있는 값인지를 판단하여 현재 값을 새 Stream(Distinct 연산 출력)에 추가하거나 값을 무시하고 새 Stream에 추가하지 않을 수 있습니다.
13. Stream의 findFirst()와 findAny()의 차이점은 무엇인가요?
- 이름에서 알 수 있듯이
findFirst()
함수는 Stream에서 첫 번째 요소를 찾는 데 사용되는 반면,findAny()
함수는 Stream에서 모든 요소를 찾는 데 사용됩니다. findFirst()
는 pre-deterministic이지만findAny()
는 non-deterministic입니다. 프로그래밍에서 결정적(deterministic)이란 출력이 시스템의 입력 또는 초기 상태에 기반한다는 것을 의미합니다.
14. Parallel Stream이란 무엇인가요? 목록을 Parallel Stream으로 변환하는 방법은 무엇인가요?
- Java 8의 눈에 띄는 기능 중 하나는 Java Parallel Stream입니다. 이는 프로세서의 다양한 코어를 활용하기 위한 것입니다.
- 기본적으로 모든 Stream 작업은 명시적으로 병렬로 지정되지 않는 한 Java에서 순차적으로 이루어집니다. Java에서 Parallel Stream은 두 가지 방법으로 생성됩니다.
- 순차 Stream에서
parallel()
메서드를 호출합니다. - Collection에서
parallelStream()
메서드를 호출합니다.
- Parallel Stream은 프로그램 실행 시간을 최소화하기 위해 동시에 처리할 수 있는 독립적인 작업이 많을 때 유용합니다.
- 모든 Java 코드에는 일반적으로 순차적으로 실행되는 하나의 처리 Stream만 있습니다. 하지만 Parallel Stream을 사용하면 Java 코드를 두 개 이상의 스트림으로 분리하여 각각의 코어에서 병렬로 실행할 수 있으며, 그 결과는 개별 결과의 조합으로 나타납니다.
- 이러한 Stream이 실행되는 순서는 당사가 통제할 수 없습니다. 따라서 개별 항목의 실행 순서가 최종 결과에 영향을 미치지 않는 경우에는 Parallel Stream을 사용하는 것이 좋습니다.
parallel()
: 기존 순차 Stream에서 parallel() 메서드를 호출하여 병렬 스트림으로 만듭니다.
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
stream.parallel().forEach(System.out::println);
Output:
3
5
4
1
2
parallelStream()
: List, Set 등과 같은 Java Collection에서 parallelStream()을 호출하여 병렬 스트림으로 만듭니다.
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
list.parallelStream().forEach(System.out::println);
Output:
3
1
5
4
Java Stream에 대해 기억해야 할 중요한 사항
- Stream은 데이터 구조가 아니라 연산 파이프라인에서 처리할 수 있는 요소의 시퀀스입니다.
- Stream은 중간 연산과 터미널 연산이라는 두 가지 유형의 연산을 지원합니다. 중간 연산은 Stream을 변환하거나 필터링하는 반면, 터미널 연산은 결과 또는 부작용을 생성합니다.
- Stream은 지연 처리되도록 설계되었기 때문에 터미널 연산이 실행될 때 요소들이 on-demand 방식으로 처리됩니다.
- 적절한 리소스 관리를 위해
close()
메서드를 사용하거나try-with-resources
를 활용하여 I/O 채널 또는 리소스에서 열린 Stream을 닫는 것이 중요합니다. - Java Stream이 모든 시나리오에 적합한 것은 아닙니다. 경우에 따라서는 루프를 사용하는 전통적인 반복이 더 적절하고 효율적일 수 있습니다.
반응형
'Language > Java' 카테고리의 다른 글
[Java] Logger 이해 (0) | 2024.08.08 |
---|---|
Java 8 코딩 및 프로그래밍 면접 질문과 답변 (1) | 2024.07.31 |
[Java] Iteration 예 (0) | 2022.11.25 |
[Java] 스트림(Stream) 사용 방법 (0) | 2022.11.25 |
Java 8 람다(Lambda) 적용 예 (0) | 2022.11.24 |
@고지니어스 :: 규니의 개발 블로그
IT 기술과 개발 내용을 포스팅하는 블로그
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!