나온지 한참되었고 늦었지만 자바 1.8부터 등장한 Stream에 대한 공부를 하고 있으며 이에 대한 내용을 정리하려고 합니다.
여태까지 개발을 하면서 왜 사용하는지 모른 채 그냥 새로운 기술이니까, 있어보이니까 사용하려고 했던 지난 날의 시간을
반성하며 왜? 라는 질문을 던지고 답을 찾아나가면서 공부를 해야겠다는 생각을 하게되었고 이에 기반하여 Stream에 대해
정리를 해보도록 하겠습니다.
그래서 Stream이란 무엇인지, 그리고 왜 사용하는지 그리고 어떻게 사용하는지에 대해서 이야기해보도록 하겠습니다.
그럼 출발해볼까요??!!!
1. Stream이 뭔데?
데이터를 다루는데 자주 사용되는 메서드를 정의 해 놓은 Interface 입니다.
2. Stream을 사용해야하는 이유가 뭘까?
먼저 코드는 항상 깔끔하고 재사용성이 높아야한다는 생각으로 접근을 해보겠습니다.
보통 다수의 데이터를 다룰 때 배열이나 컬렉션(List, Set, Map)을 사용합니다.
유사한 기능임에도 불구하고 배열, 컬렉션 등의 자료형이 다르다는 이유로 같은 기능을 하는 메서드들이
중복으로 정의되어 있는데 이러면 재사용성이 낮아져서 비효율적인 문제가 발생합니다.
그래서 자료형에 대한 추상화를 기반으로 일반적으로 개발자들이 자주 사용하는 메서드들을 정의한 Interface를 통해
재사용성을 높이고 자료형에 의존적인 문제를 해결하려고 했고 그 결과로 탄생한 것이 Stream입니다.
3. 그래서 Stream은 어떤 특성을 가지고 있는데?
1) Stream은 일회용품이다.
Stream은 다수의 데이터를 품은 채 중간연산을 해야한다면 중간연산을 거쳐 최종연산을 하게되면 닫히게 됩니다.
중간연산? 최종연산은 또 뭐야? 라는 생각이 들기 시작하는데요!! 그래서 코드를 보면서 설명하도록 하겠습니다!!
public class StreamStudy { public static void main(String[] args) { // int 배열 생성 int[] numbers = new int[] {1,3,4,5,2}; // Arrays.stream(numbers) => int 배열로부터 스트림 생성 IntStream intStream = Arrays.stream(numbers); // .sorted() => 스트림의 내용을 오름차순 정렬 (중간연산) // .forEach((number) -> System.out.println(number)) => 스트림을 순회하며 데이터 출력 (최종연산) intStream.sorted().forEach((number) -> System.out.println(number)); } }
위의 코드에서 sorted()라는 메서드는 중간연산 관련 메서드 중 하나이며 forEach()라는 메서드는 최종연산 관련 메서드 중
하나입니다.
중간연산과 최종연산 관련 메서드는 너무 많아서 필요한 기능에 대한 연산을 담당하는 메서드를 검색해서 사용하는 것을
추천드립니다^~^
다음글에서는 응용편이라는 제목으로 조금 더 많은 중간연산과 최종연산을 다룰 예정이니 너무 걱정마세욤!!ㅎㅎ
여기서 말씀드리고 싶은 뽀인트는 바로바로 저기 forEach이후에 다시 intStream을 사용하려고하면 해당 스트림은
이미 닫혔다는 에러를 만나게 되실거에요!!!
똑같은 데이터에 관련해서 다른 연산을 다시 하려면 스트림 생성을 다시 해서 연산을 진행하셔야한다는 뜻 입니다!!
2) Stream은 원본 데이터를 변경하지 않는다.
스트림은 원본 데이터를 기반으로 데이터를 복제해서 사용하기 때문에 정렬과 같은 연산을 수행해도
원본 데이터에는 영향이 없다는 특성이 있습니다.
예를들어서 다음의 코드를 보도록 하겠습니다.
public class StreamStudy { public static void main(String[] args) { // int 배열 생성 int[] numbers = new int[] {1,3,4,5,2}; System.out.println("---------- 원본 데이터(int[] numbers = new int[] {1,3,4,5,2}) 기반 스트림 생성 후 스트림 오름차순 정렬 ----------"); // Arrays.stream(numbers) => int 배열로부터 스트림 생성 IntStream intStream = Arrays.stream(numbers); // int 배열로부터 생성한 스트림을 오름차순 정렬 intStream = intStream.sorted(); intStream.forEach((number) -> System.out.println(number)); System.out.println("---------- 원본 데이터(int[] numbers = new int[] {1,3,4,5,2}) 기반 스트림 재생성 ----------"); // Arrays.stream(numbers) => int 배열로부터 스트림 생성 intStream = Arrays.stream(numbers); intStream.forEach((number) -> System.out.println(number)); } }
위의 코드와 결과 화면을 보면 기존의 원본 데이터인 numbers라는 배열 속 데이터의 변경은 Stream의 동작과는
아무런 연관이 없음을 알 수 있습니다.
4. 어떻게 사용하는데?
1) 스트림 생성
public class CreateStream { public static void main(String[] args) { // 배열에서 스트림 생성 int[] numbers = new int[]{1,2,3,4,5}; IntStream numbersStream = Arrays.stream(numbers); Integer[] integers = new Integer[]{1,2,3,4,5}; Stream<Integer> integersStream = Arrays.stream(integers); // List에서 스트림 생성 List<Integer> numberList = Arrays.asList(1,2,3,4,5); Stream<Integer> integerListStream = numberList.stream(); // Set에서 스트림 생성 Set<Integer> numberSet = new HashSet<>(); numberSet.add(1); numberSet.add(2); numberSet.add(3); numberSet.add(4); numberSet.add(5); Stream<Integer> numberSetStream = numberSet.stream(); // Map에서 스트림 생성 Map<String, String> map = new HashMap<>(); map.put("firstKey", "firstValue"); map.put("secondKey", "secondValue"); map.put("thirdKey", "thirdValue"); map.put("forthKey", "forthValue"); map.put("fifthKey", "fifthValue"); Stream<String> keyStream = map.keySet().stream(); Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream(); } }
2) 중간연산
public class CreateStream { public static void main(String[] args) { // 배열에서 스트림 생성 int[] numbers = new int[]{1,2,3,4,5}; IntStream numbersStream = Arrays.stream(numbers); // 중간 연산 메서드 sorted() 수행 numbersStream = numbersStream.sorted(); Integer[] integers = new Integer[]{1,2,3,4,5}; Stream<Integer> integersStream = Arrays.stream(integers); // 중간 연산 메서드 sorted() 수행 integersStream = integersStream.sorted(); // List에서 스트림 생성 List<Integer> numberList = Arrays.asList(1,2,3,4,5); Stream<Integer> integerListStream = numberList.stream(); // 중간 연산 메서드 sorted() 수행 integerListStream = integerListStream.sorted(); // Set에서 스트림 생성 Set<Integer> numberSet = new HashSet<>(); numberSet.add(1); numberSet.add(2); numberSet.add(3); numberSet.add(4); numberSet.add(5); Stream<Integer> numberSetStream = numberSet.stream(); // 중간 연산 메서드 sorted() 수행 numberSetStream = numberSetStream.sorted(); // Map에서 스트림 생성 Map<String, String> map = new HashMap<>(); map.put("firstKey", "firstValue"); map.put("secondKey", "secondValue"); map.put("thirdKey", "thirdValue"); map.put("forthKey", "forthValue"); map.put("fifthKey", "fifthValue"); Stream<String> keyStream = map.keySet().stream(); // 중간 연산 메서드 sorted() 수행 keyStream = keyStream.sorted(); Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream(); // Comparable이 아니므로 중간 연산 메서드 sorted() 수행 불가 // entryStream = entryStream.sorted(); } }
3) 최종연산
public class CreateStream { public static void main(String[] args) { // 배열에서 스트림 생성 int[] numbers = new int[]{1,2,3,4,5}; IntStream numbersStream = Arrays.stream(numbers); // 중간 연산 메서드 sorted() 수행 numbersStream = numbersStream.sorted(); // 최종 연산 메서드 forEach() 수행 numbersStream.forEach((element) -> System.out.println(element)); Integer[] integers = new Integer[]{1,2,3,4,5}; Stream<Integer> integersStream = Arrays.stream(integers); // 중간 연산 메서드 sorted() 수행 integersStream = integersStream.sorted(); // 최종 연산 메서드 forEach() 수행 integersStream.forEach((element) -> System.out.println(element)); // List에서 스트림 생성 List<Integer> numberList = Arrays.asList(1,2,3,4,5); Stream<Integer> integerListStream = numberList.stream(); // 중간 연산 메서드 sorted() 수행 integerListStream = integerListStream.sorted(); // 최종 연산 메서드 forEach() 수행 integerListStream.forEach((element) -> System.out.println(element)); // Set에서 스트림 생성 Set<Integer> numberSet = new HashSet<>(); numberSet.add(1); numberSet.add(2); numberSet.add(3); numberSet.add(4); numberSet.add(5); Stream<Integer> numberSetStream = numberSet.stream(); // 중간 연산 메서드 sorted() 수행 numberSetStream = numberSetStream.sorted(); // 최종 연산 메서드 forEach() 수행 numberSetStream.forEach((element) -> System.out.println(element)); // Map에서 스트림 생성 Map<String, String> map = new HashMap<>(); map.put("firstKey", "firstValue"); map.put("secondKey", "secondValue"); map.put("thirdKey", "thirdValue"); map.put("forthKey", "forthValue"); map.put("fifthKey", "fifthValue"); Stream<String> keyStream = map.keySet().stream(); // 중간 연산 메서드 sorted() 수행 keyStream = keyStream.sorted(); // 최종 연산 메서드 forEach() 수행 keyStream.forEach((element) -> System.out.println(element)); Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream(); // Comparable이 아니므로 중간 연산 메서드 sorted() 수행 불가 // entryStream = entryStream.sorted(); } }