본문 바로가기
JAVA

221028 Stream API

**Stream API. 
1.개요
=>Collection 데이터를 다룰 때 내부 반복자를 만들어서 접근하기 위한 API 
내부 반복자를 이용해서 접근하면 반복문이나 빠른 열거를 사용하는 것보다 빠르게 작업을 수행할 수 있음
=>How보다 What. 어떻게 할지보다 무엇을 할 것인가.
=>작업의 과정
생성->중간 작업->최종 작업
중간 작업은 여러 개를 묶어도 됩니다.
생성은 배열이나 List를 가지고 수행 

중간 작업은 스트림의 데이터를 순회하면서 작업을 수행해서 다시 스트림을 리턴하기 때문에 다른 중간 작업을 연속해서 배치하는 것이 가능합니다.

최종 작업은 1번만 수행 가능합니다.
집계를 수행하는 함수나 forEach처럼 하나씩 순회하면서 작업을 수행하기 위한 함수
또는 collect처럼 배열이나 List를 생성해주는 함수를 사용합니다.



/*
Enumeration-자바 : Fast Enumeration /용어 바꿈 ->Iterator-반복자
향상된 for문이 이런 식. 배열을 하나하나 출력하거나 get을 쓰는 것 보다 향상된 for문을 사용하는 것이 빠름

데이터가 많아질 경우, 데이터 안에서 만들면 참조하고 찾는 시간보다 더 적게 걸린다
Stream API. 
-Mapping : 변환
-Filtering : 원하는 데이터 추출
-Reduce : 연산

Join : 데이터를 모으는 작업
지금은 Join하는데 시간이 너무 걸려서 데이터에서 작업을 거치고 결과를 합산하는 구조.
*/

2.스트림 생성
1)Collection 인터페이스로부터 상속받은 객체는 stream()이나 parallelStream()호출
=>parallelStream()을 호출하면 병렬 처리가 가능한 스트림

2)배열의 경우는 Arrays.stream(배열)을 이용해서 생성

3)일련번호 형태의 정수 스트림은 IntStream.range(start,end)나 rangeClosed(start, end)를 이용해서 생성하는 것이 가능

3.중간 작업-작업을 수행한 후 Stream을 리턴
1)distinct() : 중복을 제거
2)filter(매개변수를 1개 받아서 Boolean을 리턴하는 람다) : 람다가 true를 리턴하는 데이터만 모아서 스트림을 생성
3)map(매개변수를 1개 받아서 리턴하는 람다) : 리턴하는 데이터를 모아서 새로운 스트림을 리턴
4)sorted(비교처리를 수행해주는 람다) : 람다를 기준으로 정렬
5)skip(long n) : n만큼 건너뜀
6)limit(long n) : n개만큼 추출

4.최종 연산
=>count() : 데이터 개수 리턴
=>max() : 최대값 리턴
=>min() : 최소값 리턴
=>average() : 평균 리턴
=>forEach() : 데이터를 순회하면서 작업
=>collect() : 데이터를 모아서 다른 자료형으로 변경할 계획

==>★계산에 의해서 나오는 max, min, average는 리턴 타입이 Optional 입니다. sum()과 count()는 아님.
Optional은 isPresent()로 데이터의 존재 여부를 확인할 수 있고 get으로 실제 데이터를 가져올 수 있음

Optional 사용 이유?
String str="Hello";
System.out.println(str);-ok

String str = null;
System.out.println(str);-ok

System.out.println(str.toUpperCase());->에러 발생. NullPointerException
null이라서 발생하는 에러가 아님! null이 멤버를 호출해서 발생시키는 예외임.
null.이라서 에러다-> if(str!=null)을 매번 해야 함.
근데 이게 굉장히 불편함. 메모리 낭비도 발생
그래서 null이 저장되는 자리에 하나의 비트를 만들어 0과1로 null인지 아닌지 확인하는 방법 사용. 
이걸 Optional이라고 한다.
그래서 isPresent()메서드로 판단.

사례)DB연동 primarykey-Optional. 

 

 

실습 효율성 비교

package java_1028.Stream;

import java.util.ArrayList;

public class LoopingMain {
	public static void main(String[] args) {
		ArrayList<String> list = new ArrayList<>();
		
		list.add("프로그래밍 언어");
		list.add("데이터베이스");
		list.add("프레임워크");
		list.add("소프트웨어 공학");
		list.add("Toy Project");
		
		//전체 데이터 출력 -실행 속도는 가장 빠르지만 list의 데이터 개수가 변경되면 
		//수정을 해야 함
		System.out.println(list.get(0));
		System.out.println(list.get(1));
		System.out.println(list.get(2));
		System.out.println(list.get(3));
		System.out.println(list.get(4));
		
		//반복문 이용
		//list 데이터 개수를 이용해서 순회를 하면 list 데이터 개수가 변경되도 수정할 필요 없음
		for(int i=0;i<list.size();i++) {
			System.out.println(list.get(i));
		}
		
		//변하지 않는 메서드의 호출 결과를 반복문에서 여러 번 호출하는 것은 자원의 낭비
		int len =list.size();//반복문 안에 있으면 6번 불러야 함. stack을 6번 만들고 지우고 함. 낭비.  
		for(int i=0;i<len;i++) {
			System.out.println(list.get(i));
		}
		
		//모든 데이터를 순회하는 경우라면 빠른 열거를 이용하는 것이 효율적
		for(String subject : list) {//size 계산도 안함.
			System.out.println(subject);
		}
		
		
		//Stream API
		//빠른 열거는 반복자를 외부에 만들어서 사용하는데 스트림 API는 내부 반복자를 사용
		//데이터가 많을 때 효율적
		//stream()호출, forEach()호출, 안에 람다 집어넣기
		list.stream().forEach(subject->{
			System.out.println(subject);
		});
		//속도로만 따지면 제일 빠름
	}

}
프로그래밍 언어
데이터베이스
프레임워크
소프트웨어 공학
Toy Project
프로그래밍 언어
데이터베이스
프레임워크
소프트웨어 공학
Toy Project
프로그래밍 언어
데이터베이스
프레임워크
소프트웨어 공학
Toy Project
프로그래밍 언어
데이터베이스
프레임워크
소프트웨어 공학
Toy Project
프로그래밍 언어
데이터베이스
프레임워크
소프트웨어 공학
Toy Project

 

 

Stream 실습

package java_1028.Stream;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.OptionalDouble;

public class StreamMain {

	public static void main(String[] args) {
		//숫자 형태의 문자열의 리스트
		ArrayList<String> list = new ArrayList<>();
		list.add("28");
		list.add("2");
		list.add("3");
		list.add("6");
		list.add("5");
		list.add("9");
		
		//최종 연산을 이용해서 출력
		//forEach는 매개변수 1개를 받고 리턴이 없는 메서드를 매개변수로 받음
		//Collection의 모든 데이터를 매개변수에 대입해서 내용을 수행
		
		//list안의 데이터를 순차적으로 e에 대입해서 {}안의 내용을 수행
		//최종 작업만 수행해서 데이터 출력
		//list.stream().forEach(e->{System.out.println(e);});
		
		//데이터 3개만 출력
		//list.stream().limit(3).forEach(e->{System.out.println(e);});//forEach가 데이터를 순회하면서 작업하므로 add한 순서대로 limit 한 3개가 나옴
		
		//데이터 정렬 후 출력
		//list.stream().sorted().forEach(e->{System.out.println(e);});
		
		//내림차순 정렬
		//sorted 메서드에 내림차순 정렬을 위한 Comparator 인터페이스를 구현한 클래스의 객체를 설정하면 됨
		//Comparator 인터페이스는 매개변수가 2개이고 정수를 리턴하는 메서드 1개만 존재함
		list.stream().sorted(((o1,o2)-> o2.compareTo(o1))).forEach(e->{System.out.println(e);});
		
		//데이터를 정수로 변환
		//중간 처리 메서드 중에는 Int로 리턴해주는 mapToInt라는 메서드가 존재하고
		//이 메서드를 사용할 때는 변환에 사용하는 메서드를 설정만 해주면 됨 
		//클래스이름::메서드이름
		//문자열을 정수로 변환해서 합계 구하기
		int result=list.stream().mapToInt(Integer::parseInt).sum();
		System.out.println(result);
		
		//홀수의 합
		//filter() :조건에 맞는 데이터만 추출
		//조건에 맞는 데이터를 추출하고자 할 때는 하나의 매개변수를 받아서 boolean을 리턴하는 람다를 만들어서 대입하면 됨
		int result1=list.stream().mapToInt(Integer::parseInt).filter(o->o%2!=0).sum();
		System.out.println(result1);
		
		
		//홀수의 평균 구하기
		//일반적으로 생각하기에는 평균의 결과가 정수나 실수가 나와야 하는데,
		//자바에서는 OptionalDouble이 됩니다.
		//Optional이 붙으면 null을 저장할 수 있는 자료형이 되며
		//isPresent라는 메서드를 이용해서 null여부를 판단하고
		//get이라는 메서드로 데이터를 가져옵니다.
		OptionalDouble result2=list.stream().mapToInt(Integer::parseInt).filter(o->o%2!=0).average();//조건에 맞는게 하나도 없으면 0인데, 평균을 구할때 0으로 나눌 수 없다. 그래서 Optional이 붙는 것
		
		//연산을 정상적으로 수행한 경우
		if(result2.isPresent()) {
			System.out.println(result2.getAsDouble());
		}else{
			System.out.println("평균 계산 실패-아마도 데이터가 없는 것 같음");
		}
		
		
		//데이터를 정수로 변환해서 내림차순으로 정렬
		//숫자의 경우는 크기 비교가 가능해서 별도의 인스턴스를 대입하지 않아도 정렬이 되고
		//내림차순을 하고자 하는 경우 reverse옵션을 설정해주면 됨.
		list.stream().map(Integer::parseInt).sorted(Comparator.reverseOrder()).forEach(e->{System.out.println(e);});
      	//.mapToInt()는 int로 받는데 Comparator가 int를 받지 못함. Integer나 Object로 받아야 함. 
		
		
		
	}

}
9
6
5
3
28
2
53
17
5.666666666666667
28
9
6
5
3
2

'JAVA' 카테고리의 다른 글

221028 Daemon Thread  (0) 2022.10.28
221028 Thread  (0) 2022.10.28
221028 Lambda  (0) 2022.10.28
221028 Anonymous Class  (0) 2022.10.28
221028 Nested Class(내포 클래스)-(Embedding)  (0) 2022.10.28