6주차 배운점 느낀점 - stream, 파이프라인, 중간 연산, 최종 연산
6주차
강사님이 어제 수업결과가 많이 노답인가 싶으셨는지 오늘 어제 복습을 함께 나갔다.
어제 람다와 같이 사용했던 stream에대해 배우고 람다식 사용법을 복습했다.
stream의 설명과 , 장점, 연산, map의 사용 reduce사용, sorted사용 파일 출력과 같이 예제를 학습했다.
배운점
스트림
데이터의 흐름(stream)
데이터가 여러개 있어야 흐름을 만들 수 있다.
스트림 데이터 소스: 컬렉션, 배열, 주로 사용됨
스트림 데이터는 데이터소스로부터 추출한 연속적인 데이터다.
Java 8부터 컬렉션 및 배열의 요소를 반복 처리하기 위해 스트림 사용
요소들이 하나씩 흘러가면서 처리된다는 의미
List 컬렉션의 stream() 메소드로 Stream 객체를 얻고, forEach() 메소드로 요소를 어떻게 처리할지를 람다식으로 제공
스트림과 Iterator 차이점
둘다 반복자임 !
내부반복자 외부반복자 큰 차이 병렬처리.
내부반복자 : 알아서 인덱스처리 , 속도 빠름
1) 내부 반복자이므로 처리 속도가 빠르고 병렬 처리에 효율적
2) 람다식으로 다양한 요소 처리를 정의
3) 중간 처리와 최종 처리를 수행하도록 파이프 라인을 형성
java.util.stream 패키지에는 BaseStream 인터페이스를 부모로 한 자식 인터페이스들은 상속 관계
BaseStream에는 모든 스트림에서 사용할 수 있는 공통 메소드들이 정의
내부 반복자
요소 처리 방법을 컬렉션 내부로 주입시켜서 요소를 반복 처리
개발자 코드에서 제공한 데이터 처리 코드(람다식)를 가지고 컬렉션 내부에서 요소를 반복 처리
내부 반복자는 멀티 코어 CPU를 최대한 활용하기 위해 요소들을 분배시켜 병렬 작업 가능(스레드..)
외부 : for, hasnext()같은애들
컬렉션 내부에서 반복처리, 요소처리내용을 컬렉션으로 주입함
스트림API
연속적인 데이터의 흐름을 반복적으로 또는 연산하여 처리하는 기능
스트림 특징
스트림 연산은 기존 자료를 변경하지 않는다.
스트림 연산은 중간 연산과 최종연산 구분한다.
한 번 생성하고 사용한 스트림은 재사용 불가(close 됨) 1회용
중간 연산
filter() : 조건에 맞는 요소 추출
map() : 조건에 맞는 요소 변환
sorted() : 정렬
최종연산
스트림의 자료를 소모하면서 연산을 수행한다
최종연산 이후에 소모된 스트림은 더이상 다른 연산을 적용할 수 없다.
foreach(): 요소를 하나씩 꺼내옴
count(): 요소 개수
sum()
average()
배열의 합 구하기
Stream<Integer>는 컬렉션 자료형일때만 사용 가능하고, int배열인경우 기본자료형이니까 IntStream으로 사용함
Stream<Integer>하고 싶으면 boxed()붙여준다. sum 구할 시 mapToInt(Integer::intValue).sum(); 사용
public static void main(String[] args) {
//1. 정수형 배열 array 생성하고 1,2,3,4,5 로 초기화 하세요
int[] array = {1,2,3,4,5};
//2. array배열에 스트림 객체 stm1 을 생성하세요
IntStream stm1 = Arrays.stream(array);
Stream<Integer> stm11 = Arrays.stream(array).boxed();
//3. 중간연산 스트림객체 stm1에 대해 홀수만 추출해서 스트림객체 stm2에 저장하세요
IntStream stm2 = stm1.filter(a -> a%2==1);
Stream<Integer> stm22 = stm11.filter(a -> a%2==1);
//4. stm2 스트림객체의 총합을 구하세요
int sum = stm2.sum();
//int total = stm22.collect(Collectors.summingInt(Integer::intValue));
int total = stm22.mapToInt(Integer::intValue).sum();
//5. 출력하세요
System.out.println(sum);
System.out.println(total);
}
배열을 리스트에 추가하고, 정렬하기
배열을 스트림화 하고 .forEach(에다가 a->list.add(a)해줌
컬랙션 리스트에서 정렬할때 Collections.sort 사용 - sort
int 배열 stream이용할때 .sorted() 하면됨 - sorted
public static void main(String[] args) {
int[] array = {1, 5, 3, 2, 4};
List<Integer> list = new ArrayList<>();
//foreach를 이용하여 array의 숫자를 list에 추가하되 홀수만 추가해 주세요
for (int i: array ) {
if(i%2==1){
list.add(i);
}
}
Arrays.stream(array).filter(n->n%2==1).sorted().forEach(a->list.add(a));
Collections.sort(list); // ARrays.sort 아님
for (int i: array ) {
System.out.println(i);
}
Arrays.stream(array).filter(n->n%2==1).sorted()
.forEach(n-> System.out.println(n+"\t"));
}
정렬
컬랙션 리스트라 sorted()로 정렬가능
sorted(Comparator.reverseOrder())해주니까 역순됨
Collection.sort도가능
길이순으로 정렬: sorted(Comparator.comparing(String::length)).toList()
sorted 다음에 toList 써줄 것
sorted(Comparator.comparing(str->str.length()) 해주면 길이순 정렬가능
public static void main(String[] args) {
//1. sorted() : 스트림을 구성하는 데이터를 조건에 따라 정렬하는 연산
List<String> stringList = Arrays.asList("홍길동 제주도 도망","풍자 먹찌빠","강호동 아는형님","유재석 유퀴즈 온더 블록","서장훈 미운오리새끼","신동엽 토요일이 즐겁다");
//2. stream객체 사전순으로 정렬해 주세요.
stringList.stream().sorted().forEach(System.out::println);
Stream<String> s1 = stringList.stream();
s1.sorted().forEach(System.out::println);
System.out.println(stringList.stream().sorted(Comparator.comparing(str->str))
.collect(Collectors.toList()));
List<String> collect = stringList.stream().sorted().collect(Collectors.toList());
System.out.println(collect);
Collections.sort(stringList); // stream객체는 아님
//3. stream객체로 변환하여 글자 길이순으로 정렬해 주세요
stringList.stream().sorted(Comparator.comparing(String::length))
.toList()
.forEach(System.out::println);
stringList.stream().sorted((str1,str2)->str2.length() - str1.length()).forEach(System.out::println);
List<String> newStr1 = stringList.stream().sorted().sorted(Comparator.comparing(str->str.length())).toList();
System.out.println(newStr1);
}
map사용
문자열리스트 대문자 변경: map(String::toUpperCase)
Intstream.of(1,2,3,4,5): of 메소드 사용하여 직접 스트림 만들 수 있다.
sum 사용할 때 mapToInt(Integer::intValue) 해줘야함
ifPresent: 값이 존재할 때 주어진 포멧으로 출력
최대값, 최소값 : .min() .max()
.toArray() : 리스트를 mapToInt해주고 배열로 사용하려면 toArray()사용 (toList 같은거)
public static void main(String[] args) {
List<String> list = Arrays.asList("apple","pear","orange","banana","tomato");
//list의 문자열을 대문자로 변환하여 출력하세요.
list.stream().map(String::toUpperCase).forEach(System.out::println);
//list의 문자열을 소문자로 변환하여 출력하세요
// steam은 원본을 해치지 않기 때문에 따로 해줄 게 없음
list.stream().forEach(System.out::println);
List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,7,5,4,456,432,13);
//1. of메소드를 이용하여 직접 입력해 입력스트림을 만들 수 있습니다. 합계 구하기
int total = IntStream.of(1,3,5,7,8).sum();
System.out.println(total);
int total2 = numbers.stream().mapToInt(Integer::intValue).sum();
System.out.println(total2);
int total3 = IntStream.of(numbers).sum();
//2. 스트림 데이터 개수를 세는 최종 연산을 합니다. count() long 반환
long count = IntStream.of(3,4,5,6,6,4,3).count();
//3. 스트림 데이터 평균을 구하는 최종 연산을 합니다.
double avg = IntStream.of(2,3,45,6,4).mapToDouble(Double::valueOf)
.average()
.orElse(0.0);
IntStream.of(2,3,45,5,5,6,2).average()
.ifPresent(av-> System.out.println("average : "+av));
//ifPresent 값이 만약에있으면 정해전 포맷으로 출력
System.out.println(avg);
//4. 스트림 데이터의 최솟값을 구하는 최종 연산을 합니다.
IntStream.of(2,45,6,7,4).min()
.ifPresent(min-> System.out.println("최소값: "+min));
}
reduce() 최종연산
이름 제일 긴거 출력: 초기 "이순신"을 두고 s1.length>=s2.length()?s1:s2 로 하나씩 비교해준다.
.reduce("이순신",(s1,s2)->s1.length()>= s2.length()?s1:s2);
public static void main(String[] args) {
List<String> names = Arrays.asList("김진성","강호동동동","신동엽","서장훈","정휘제");
String name1 = names.stream()
.reduce("이순신",(s1,s2)->s1.length()>= s2.length()?s1:s2);
System.out.println(name1);
List<Integer> numbers = Arrays.asList(10,2,34,5,6,3);
//reduce()로 numbers의 누적합, 누적곱 출력하는 문장 구현 : 제출
//누적 합
System.out.println(numbers.stream().reduce(0,(x,y)->x+y));
System.out.println(numbers.stream().reduce(0,Integer::sum));
//누적 곱
System.out.println(numbers.stream().reduce(1,(x,y)->x*y));
}
병렬처리
parallelStream 사용
public static void main(String[] args) {
List<String> names = Arrays.asList("김진성","강호동","유재석","신동엽","서장훈");
Stream<String> parallelStream = names.parallelStream(); //병렬스트림 얻기
parallelStream.forEach(name -> {
System.out.println(name + " : "+ Thread.currentThread().getName());
});
}
스트림 파이프라인
컬렉션의 오리지널 스트림 뒤에 필터링 중간 스트림이 연결될 수 있고, 그 뒤에 매핑 중간
스트림이 연결될 수 있음
오리지널 스트림과 집계 처리 사이의 중간 스트림들은 최종 처리를 위해 요소를
걸러내거나(필터링), 요소를 변환시키거나(매핑), 정렬하는 작업을 수행
최종 처리는 중간 처리에서 정제된 요소들을 반복하거나, 집계(카운팅, 총합, 평균) 작업을 수행
<Student> 스트림이다. mapToDouble(Student::getScore)로 매핑 시켰기 때문에 그 후 double 스트림이 된다. 그리고 평균을 구한다.
Integer로 평균 계산한 후 .getAsDouble()사용하면 double로 반환된다.
mapToDouble을 사용하면 orElse(0.0)을 써준다.
public static void main(String[] args) {
Student stu1 = new Student("유재석",99);
Student stu2 = new Student("김재석",90);
Student stu3 = new Student("박재석",97);
Student stu4 = new Student("정재석",80);
List<Student> students = Arrays.asList(stu1,stu2,stu3,stu4);
// 방법1
Stream<Student> studentStream = students.stream();
IntStream scoreStream = studentStream.mapToInt(Student::getScore);
int sum = scoreStream.sum();
double averge = scoreStream.average().getAsDouble();
// 방법2 : 파이프라인
double average2 = students.stream()
.mapToInt(student -> student.getScore())
.average()
.getAsDouble(); // double로 반환
// 방법3
double average3 = students.stream()
.mapToDouble(student -> student.getScore()).average().orElse(0.0);
// 방법4
double average4 = students.stream()
.mapToDouble(Student::getScore).average().orElse(0.0);
}
상품정보출력
만원 이하 랜덤 가격: random.nextInt(10000)
(int)(Math.random() * 10000)+1
public static void main(String[] args) {
//1. 외부반복자 fori를 이용하여 Product를 생성할 때
// 제품번호=>i, 상품명 => "상품명"+i , "shinsegae",
// 가격 random()메소드로 책정하여 10개 생성하여 리스트컬렉션
// productList에 저장합니다
List<Product> productList = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < 10; i++) {
productList.add(new Product(i,"상품명"+i,"shinsegae",random.nextInt(10000)));
}
//2. 객체 스트림을 통하여 productList의 상품정보를 출력합니다.
productList.stream().forEach(System.out::println);
}
stream 객체 얻기
숫자 범위 stream얻기:
IntStream.range(1,100) 1~99까지 얻는다.
IntStream.rangeClosed(1,100); 1~100 까지 얻는다.
별찍기:
StringBuilder를 만들어준다.
column 만큼 forEach에서 ( s-> sb.append("*") ) 별을 추가해준다.
두째줄에서 row 만큼 sb를 출력시킨다.
파일로 스트림 얻기:
throws Exception 해준다.
Path path = Paths.get으로 txt 파일 경로를 가져온다.
filestream에 Files.lines 으로 라인별로 스트림을 생성한다.
forEach로 한줄 씩 출력해준다.
close를 반드시 해준다.
public static void main(String[] args) throws Exception {
//1.
String[] strings = {"초코파이", "몽쉘통통", "에이스", "홈런볼"};
//strings 스트림 객체를 얻어, strings의 각 요소값을 출력하세요.
Stream<String> st = Arrays.stream(strings);
st.forEach(System.out::println);
//2. Integer 스트림 객체 얻기
int[] scores = {90, 80, 77, 80, 65, 98};
//Integer 스트림 객체를 얻어, scores의 각 요소값의 총합과 평균과 해당 요소의 개수를 출력하세요
IntStream scoreStream = Arrays.stream(scores);
int sum = scoreStream.sum();
IntStream scoreStream1 = Arrays.stream(scores);
double avg = scoreStream1.average().getAsDouble();
IntStream scoreStream2 = Arrays.stream(scores);
long count = scoreStream2.count();
System.out.println(sum + " " + avg + " " + count);
//3. 숫자 범위로부터 스트림 얻기 (IntStream 또는 LongStream의 정적메소드 range(), rangedClosed())
IntStream stream = IntStream.range(1, 100);
IntStream stream1 = IntStream.rangeClosed(1, 100);
stream.forEach(number -> total += number);
System.out.println("총합: " + total); // 1~99까지더해짐
//별 찍기
StringBuilder sb = new StringBuilder();
int row = 3;
int column = 5;
IntStream.range(0, column).forEach(s -> sb.append("*"));
IntStream.range(0, row).forEach(i -> System.out.println(sb));
//4. 파일로부터 스트림 얻기
//java.nio.file.Files의 line() 이용하면, 텍스트 파일의 행 단위 스트림을 얻을 수 있다.
Path path = Paths.get(ResourceStreamEx.class.getResource("productlist.txt").toURI());
Stream<String> filestream = Files.lines(path, Charset.defaultCharset());
filestream.forEach(line -> System.out.println(line));
filestream.close();
}
회고
어제 배운내용이지만 처음 봤을 때와 달리 어제 복습 후 오늘 다시 예제를 풀어나가니 훨씬 익숙했다. 적어도 정렬, 합, 평균, 출력 부분에서는 stream을 잘 다룰 수 있을 것 같다.
코딩테스트 문제를 풀때 시간제한 때문에 더 처리속도가 더 빠른 내부 반복자인 stream을 사용한다. 이것과 함께 함수형인터페이스도 잘 연습해놓아서 코테에서 사용하고 싶다.