[이것이 자바다] 17장 정리

자바에서 스트림 요소 처리는 내부 반복자, 중간 처리, 최종 처리를 통해 이루어집니다. 스트림은 리소스로부터 얻을 수 있으며, 필터링, 매핑, 정렬, 루핑, 매칭, 집계, 수집 등의 작업을 수행할 수 있습니다. 또한, 병렬 처리를 통해 작업 처리 시간을 줄일 수 있습니다.
Jan 19, 2024
[이것이 자바다] 17장 정리

Student.java

package ch17.se7; import lombok.*; @AllArgsConstructor @Getter @ToString public class Student { private String name; private int score; }

SortingExam.java

package ch17.se7; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class SortingExam { public static void main(String[] args) { List<Student> list = new ArrayList<>(); list.add(new Student("홍길동", 30)); list.add(new Student("신용권", 10)); list.add(new Student("유미선", 20)); list.stream() .sorted((o1, o2) ->Integer.compare(o1.getScore(), o2.getScore())) .forEach(e -> System.out.println(e)); Integer[] arr = {1,5,7,8,3,3,5,6}; Arrays.stream(arr).sorted((o1,o2)->o1-o2).forEach(e->System.out.println(e)); } }
 

핵심 키워드

  • 스트림은 하나 이상 연결될 수 있으며, 컬렉션의 오리지널 스트림 뒤에 필터링 중간 스트림이 연결될 수 있고, 그 뒤에 매핑 중간 스트림이 연결될 수 있다. 이와 같이 스트림이 연결되어 있는 것을 스트림 파이프라인이라고 한다.
  • 스트림 파이프라인을 구성할 때 주의할 정은 파이프라인의 맨 끝에는 반드시 최종 처리 부분이 있어야 한다는 것이다.
 

Product.java

package ch17.se4; public class Product { private int pno; private String name; private String company; private int price; public Product(int pno, String name, String company, int price) { this.pno = pno; this.name = name; this.company = company; this.price = price; } public int getPno() { return pno; } public void setPno(int pno) { this.pno = pno; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCompany() { return company; } public void setCompany(String company) { this.company = company; } public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } @Override public String toString() { return "Product [pno=" + pno + ", name=" + name + ", company=" + company + ", price=" + price + "]"; } }

StreamExam.java

package ch17.se4; import java.util.ArrayList; import java.util.List; public class StreamExam { public static void main(String[] args) { List<Product> list = new ArrayList<>(); for (int i = 1; i <= 5; i++) { list.add(new Product(i, "상품" + i, "회사" + i, i * 10000)); } list.stream().forEach(p -> System.out.println(p)); } }
 

핵심 키워드

  • java.util .Collection 인터페이스는 스트림과 parallelStream() 메소드를 가지고 있기 때문에 자식 인터페이스인 List와 Set 인터페이스를 구현한 모든 컬렉션에서 객체 스트림을 얻을 수 있다.
 

StreamExam2.java

package ch17.se4; import java.util.stream.IntStream; public class StreamExam2 { static int sum = 0; public static void main(String[] args) { IntStream.range(1, 100).forEach(i -> sum += i); System.out.println(sum); } }
 

핵심 키워드

  • IntStream 또는 LongStream의 정적 메소드인 range()와 rangeClosed() 메소드를 이용하면 특정 범위의 정수 스트림을 얻을 수 있다.
  • 첫 번째 매개값은 시작 수이고, 두 번째 매개값은 끝 수인데, 끝 수를 포함하지 않으려면 range(), 포함하면 rangeClosed()를 사용한다.
 

Data.txt

{"pno":1, "name":"상품1"} {"pno":2, "name":"상품2"} {"pno":3, "name":"상품3"} {"pno":4, "name":"상품4"} {"pno":5, "name":"상품5"}

StreamExam2.java

package ch17.se4; import java.net.URI; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; public class StreamExam3 { public static void main(String[] args) throws Exception { URI uri = StreamExam3.class.getResource("data.txt").toURI(); Path path = Paths.get(uri); Files.lines(path, Charset.defaultCharset()).forEach(line -> System.out.println(line)); } }
 

핵심 키워드

  • java.nio.file.Files의 lines() 메소드를 이용하면 텍스트 파일의 행 단위 스트림을 얻을 수 있다. 이는 텍스트 파일에서 한 행씩 읽고 처리할 때 유용하게 사용할 수 있다.
 

FilteringExam.java

package ch17.se5; import java.util.ArrayList; import java.util.List; public class FilteringExam { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("홍길동"); list.add("신용권"); list.add("김자바"); list.add("신용권"); list.add("신민철"); list.stream().distinct().forEach(e -> System.out.println(e)); System.out.println(); list.stream().filter(e->e.startsWith("신")).forEach(e->System.out.println(e)); System.out.println(); list.stream().distinct().filter(e->e.startsWith("신")).forEach(e->System.out.println(e)); } }
 

핵심 키워드

  • 필터링은 요소를 걸러내는 중간 처리 기능이다. 필터링 메소드에는 distinct()와 filter()가 있다.
  • distinct()는 요소의 중복을 제거한다. 객체 스트림일 경우, equals() 메소드의 리턴값이 true이면 동일한 요소로 판단한다.
  • filter() 메소드는 매개값으로 주어진 Predicate가 true를 리턴하는 요소만 필터링한다.
 

Student.java

package ch17.se6; import lombok.*; @AllArgsConstructor @Getter public class Student { private String name; private int score; }

MappingExam.java

package ch17.se6; import java.util.ArrayList; import java.util.List; public class MappingExam { public static void main(String[] args) { List<Student> list = new ArrayList<>(); list.add(new Student("홍길동", 85)); list.add(new Student("홍길동", 92)); list.add(new Student("홍길동", 87)); list.stream().mapToInt(e -> e.getScore()).forEach(e -> System.out.println(e)); } }
 

핵심 키워드

  • 매핑은 스트림의 요소를 다른 요소로 변환하는 중간 처리 기능이다. 매핑 메소드는 mapXxx(), asDoubleStream(), asLongStream(), boxed(), flatMapXxx()등이 있다.
  • mapXxx() 메소드는 요소를 다른 요소로 변환한 새로운 스트림을 리턴한다.
 

MappingExam2.java

package ch17.se6; import java.util.Arrays; public class MappingExam2 { public static void main(String[] args) { int[] arr = { 1, 2, 3, 4, 5 }; Arrays.stream(arr) .asDoubleStream() // stream 내부 요소 double 원시타입 .boxed() // Double 래퍼 객체 .forEach(e->System.out.println(e.getClass())); } }
 

핵심 키워드

  • 기본 타입 간의 변환이거나 기본 타입요소를 래퍼 객체 요소로 변환하려면 다음과 같은 간편화 메소드를 사용할 수 있다.
    • asLongStream()
    • asDoubleStream()
    • boxed()
 

FlatMapExam.java

package ch17.se6; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class FlatMapExam { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("this is java"); list.add("i am a best developer"); list.stream() .flatMap(data -> Arrays.stream(data.split(" "))) .forEach(e -> System.out.println(e)); List<String> list2 = new ArrayList<>(); list2.add("10, 20, 30"); list2.add("40, 50"); list2.stream() .flatMapToInt(data-> { String[] arr = data.split(", "); int[] intArr = new int[arr.length]; for(int i=0; i<arr.length; i++) { intArr[i] = Integer.parseInt(arr[i]); } return Arrays.stream(intArr); }) .forEach(e-> System.out.println(e)); } }
 

핵심 키워드

  • FlatMapXxx() 메소드는 하나의 요소를 복수 개의 요소들로 변환한 새로운 스트림을 변환한다.
 

Student.java

package ch17.se7; import lombok.*; @AllArgsConstructor @Getter @ToString public class Student { private String name; private int score; }

SortingExam.java

package ch17.se7; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class SortingExam { public static void main(String[] args) { List<Student> list = new ArrayList<>(); list.add(new Student("홍길동", 30)); list.add(new Student("신용권", 10)); list.add(new Student("유미선", 20)); list.stream() .sorted((o1, o2) ->Integer.compare(o1.getScore(), o2.getScore())) .forEach(e -> System.out.println(e)); Integer[] arr = {1,5,7,8,3,3,5,6}; Arrays.stream(arr).sorted((o1,o2)->o1-o2).forEach(e->System.out.println(e)); } }
 

핵심 키워드

  • 스트림의 요소가 객체일 경우 객체가 Comparable을 구현하고 있어야만 sorted() 메소드를 사용해서 정렬할 수 있다. 그렇지 않다면 ClassCastException이 발생한다.
  • 만약 내림차순으로 정렬하고 싶다면 Comparable.reverseOrder() 메소드가 리턴하는 Comparator를 매개값으로 제공하면 된다.
  • 요소 객체가 Comparable을 구현하고 있지 않다면, 비교자를 제공하면 요소를 정렬시킬 수 있다. 비교자는 Comparator 인터페이스를 구현한 객체를 말하는데, 다음과 같이 람다식으로 간단하게 작성할 수 있다.
    • sorted((o1, o2) → {…});
 

LoopingExam.java

package ch17.se8; import java.util.Arrays; public class LoopingExam { public static void main(String[] args) { int[] arr = { 1, 2, 3, 4, 5 }; // Arrays.stream(arr).filter(i -> i % 2 == 0).forEach(e -> System.out.println(e)); System.out.println(Arrays.stream(arr) .filter(i -> i % 2 == 0) .peek(e -> System.out.println(e)) .allMatch(e->e%2!=0)); int[] arr2 = { 2, 4, 6, 8, 10 }; System.out.println(Arrays.stream(arr2).allMatch(e -> e % 2 == 0)); } }
 

핵심 키워드

  • 루핑은 스트림에서 요소를 하나씩 반복해서 가져와 처리하는 것을 말한다. 루핑 메소드에는 peek()과 forEach()가 있다.
  • peek()과 forEach()는 동일하게 요소를 루핑하지만 peek()은 중간 처리 메소드이고, forEach()는 최종 처리 메소드이다.
  • 매칭은 요소들이 특정 조건에 만족하는지 여부를 조사하는 최종 처리 기능이다.
  • allMatch(), anyMatch(), noneMatch() 메소드는 매개값으로 주어진 Predicate가 리턴하는 값에 따라 true, false를 리턶한다.
 

StreamExam.java

package ch17.se10; import java.util.Arrays; import java.util.OptionalDouble; import java.util.OptionalInt; public class StreamExam { public static void main(String[] args) { int[] arr = { 1, 2, 3, 4, 5 }; long count = Arrays.stream(arr).filter(e -> e % 2 == 0).count(); System.out.println(count); long sum = Arrays.stream(arr).filter(e -> e % 2 == 0).sum(); System.out.println(sum); double average = Arrays.stream(arr).filter(e -> e % 2 == 0).average().getAsDouble(); System.out.println(average); int max = Arrays.stream(arr).filter(e -> e % 2 == 0).max().getAsInt(); System.out.println(max); int min = Arrays.stream(arr).filter(e -> e % 2 == 0).min().getAsInt(); System.out.println(min); OptionalInt first = Arrays.stream(arr).filter(e -> e % 2 == 0).findFirst(); System.out.println(first); } }

Student.java

package ch17.se10; import lombok.*; @AllArgsConstructor @Getter public class Student { private String name; private int score; }

StreamExam2.java

package ch17.se10; import java.util.ArrayList; import java.util.List; import java.util.OptionalInt; public class StreamExam2 { public static void main(String[] args) { List<Student> list = new ArrayList<>(); list.add(new Student("홍길동", 92)); list.add(new Student("신용권", 95)); list.add(new Student("김자바", 88)); int sum1 = list.stream().mapToInt(Student::getScore).sum(); int sum2 = list.stream() .mapToInt(Student::getScore) .reduce((a, b) -> a + b) // reduce의 매개변수가 람다식 하나면 옵셔널 반환 .orElse(0); // orElse로 디폴트 선언 int sum3 = list.stream().mapToInt(Student::getScore).reduce(0, (a, b) -> a + b); // 파라미터 2개면 원시타입 반환 } }
 

핵심 키워드

  • 집계는 최종 처리 기능으로 요소들을 처리해서 카운팅, 합계, 평균값, 최대값, 최소값등과 같이 하나의 값으로 산출하는 것을 말한다. 즉 대량의 데이터를 가공해서 하나의 값으로 축소하는 리덕션이라고 볼 수 있다.
  • 스트림은 기본 집계 메소드은 sum(), average(), count(), max(), min()을 제공하지만, 다양한 집계 결과물을 만들 수 있도록 reduce() 메소드도 제공한다.
  • reduce()는 스트림에 요소가 없을 경우 예외가 발생하지만, identity 매개값이 주어지면 이 값을 디폴트 값으로 리턴한다.
 

Student.java

package ch17.se12; import lombok.*; @AllArgsConstructor @Getter @ToString public class Student { private String name; private String gender; private int score; }

CollectExam.java

package ch17.se12; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class CollectExam { public static void main(String[] args) { List<Student> list = new ArrayList<>(); list.add(new Student("홍길동", "남", 92)); list.add(new Student("김수영", "여", 87)); list.add(new Student("김자바", "남", 95)); list.add(new Student("오해영", "여", 93)); List<Student> list1 = list.stream() .filter(e->e.getGender().equals("남")) .collect(Collectors.toList()); for(Student s: list1) System.out.println(s); Map<Object, Object> map = list.stream() .collect(Collectors.toMap(s -> s.getName(), s->s.getScore())); System.out.println(map); } }
 

핵심 키워드

  • 스트림은 요소들을 필터링 또는 매핑한 후 요소들을 수집하는 최종 처리 메소드인 collect()를 제공한다. 이 메소드를 이용하면 필요한 요소만 컬렉션에 담을 수 있고, 요소들을 그룹핑한 후에 집계도 할 수 있다.
 

ParallelExam.java

package ch17.se13; import java.util.*; public class ParallelExam { public static void main(String[] args) { Random r = new Random(); List<Integer> scores = new ArrayList<>(); for (int i = 0; i < 100000000; i++) { scores.add(r.nextInt(101)); } double startTime = 0; double endTime = 0; double time = 0; startTime = System.nanoTime(); scores.stream().mapToInt(i -> i.intValue()).average().getAsDouble(); endTime = System.nanoTime(); time = (endTime - startTime)/1000000000; System.out.println(time); startTime = System.nanoTime(); scores.parallelStream().mapToInt(i -> i.intValue()).average().getAsDouble(); endTime = System.nanoTime(); time = (endTime - startTime)/1000000000; System.out.println(time); } }
 

핵심 키워드

  • 요소 병렬 처리란 멀티 코어 CPU 환경에서 전체 요소를 분할해서 각각의 코어가 병렬적으로 처리하는 것을 말한다. 요소 병렬 처리의 목적은 작업 처리 시간을 줄이는 것에 있다.
  • 자바는 요소 병렬 처리를 위해 병렬 스트림을 제공한다.
  • parallelStream() 메소드는 컬렉션(List, Set)으로부터 병렬 스트림을 바로 리턴한다. parallel() 메소드는 기존 스트림을 병렬 처리 스트림으로 변환한다.
 

결론

해당 문제를 풀면서 자바에서의 스트림 요소 처리와 내부 반복자, 중간처리와 최종 처리, 리소스로부터 스트림 얻기, 필터링, 매핑, 정렬, 루핑, 매칭, 집계, 수집, 병렬 처리 등을 익힐 수 있었다.
 
Share article

More articles

See more posts
RSSPowered by inblog