본문 바로가기

JAVA공부(이것이 자바다)

스트림

람다식은 데이터가 많을 때는  느리다. 하지만 데이터가 적을 때는 직관적이고 좋다.

 

스트림은 병렬처리를 해서 속도를 극대화하는 용도이다. 

 

컬렉션의 정보를 전달해서 컬렉션을 반복 처리하기 위해서 사용한다

Stream<String> stream = list.stream();
stream.forEach(item -> {...});

 

 

stream과 iterator의 차이

1.내부반복자 이므로 처리 속도가 빠르고 병렬처리에 효율적이다.

-> 콜백함수인 인터페이스 참조변수를 통해 전달되기 때문에 속도가 빠르지 않다 하지만 병렬처리를 했을때는 빠르다.

2.람다식으로 다양한 요소처리를 정의 할 수 잇다.

3.중간처리와 최종처리를 수행하도록 파이프라인을 형성할 수 잇다.

--> 스트림은 멀티스레드 병렬 처리시 fork와 join을 자동으로 수행한다.

 

 

외부반복자

컬렉션을 순회하기 위해서 while문이나 for문을 돌면서 컬렉션의 내용을 하나 씩 가져오는 것 향상된 for-loop문일때는 내부반복자보다 빠르다. 

 

 

내부반복자

forEach에 처리 함수를 등록하고 컬렉션을 하나씩 가져다 처리해준다 외부반복자보다는 느리지만 데이터를 공유하지 않고 자체 데이터를 사용하는 멀티스레드를 구현 할 수 잇다는 점에서 장점이 있다. 병렬처리시 향상된 for-loop문보다 빨라진다.

 

스트림 파이프 라인

 

스트림생성 중간처리 최종처리

파이프를 나눴다고 해서 각각 따로 돌아가는게 아니라 중간처리 부분에서 최종처리를 위해서 요소를 걸러내는 필터링(클래스중에 내가 쓰고 싶은 데이터를 get해온다.)을 진행하거나, 요소를 변환시키거나(person -> integer), 정렬하는 작업을 수행한다.

//스트림의 매핑을 사용하지 않을 때 원본코드
int total = 0;
for (Student s : list){
    total += s.getScore();
 }
double avg2 = (double) total / list.size();

//스트림의 매핑방법 두가지
//방법1
Stream<Student> studentStream = list.stream();
//중간 처리(학생 객체를 점수로 매핑)
IntStream scoreStream = studentStream.mapToInt(student -> student.getScore());
//최종 처리(평균 점수)
double avg = scoreStream.average().getAsDouble();


//방법2
double avg = list.stream()
        .mapToInt(student -> student.getScore())
        .average()
        .getAsDouble();

System.out.println("평균 점수: " + avg);

 

스트림 얻기

컬렉션으로부터 스트림 얻기

list.stream()

//객체 스트림 얻기
Stream<Product> stream = list.stream();
stream.forEach(p -> System.out.println(p));

배열로부터 스트림 얻기

Arrays.stream()

String[] strArray = { "홍길동", "신용권", "김미나"};
Stream<String> strStream = Arrays.stream(strArray);
//Stream<String> strStream = Arrays.stream(new String[] {"홍길동", "신용권", "김미나"});
strStream.forEach(item -> System.out.print(item + ","));

숫자범위로부터 스트림얻기

IntStream에서 rangeClosed는 1~100까지 딱 나오는데 range()는 1~99까지 나옴

IntStream stream = IntStream.rangeClosed(1, 100);
stream.forEach(a -> sum += a);

 

 

파일로부터 스트림얻기

파일의 경로를 Paths클래스의 get에 URI를 전달해서 파일을 가져와 지정된 문자셋으로 읽어오도록한다.

Path path = Paths.get(StreamExample.class.getResource("data.txt").toURI());
Stream<String> stream = Files.lines(path, Charset.defaultCharset());
stream.forEach(line -> System.out.println(line) );
stream.close();

 

필터링

중간처리 기능으로 요소를 걸러낸다 

distinct()

- 중복을 제거한다 사용하려면 equals와 hashCode가 재정의 되어 있어야한다.

--> 중복을 허용하지 않으려면 애초에 set에다가 대입하는 방법도 있다.

filter()

함수형 인터페이스를 재정의하여 조건으로 스트림을 필터링한다.

 

filter()

hashCode와 equals를 반드시 재정의 해주어야한다. 안에 추상메소드로 test가 boolean값을 반환한다.

//신으로 시작하는 요소만 필터링
list.stream()
 .filter(n -> n.startsWith("신"))
 .forEach(n -> System.out.println(n));
System.out.println();

//2번째 방법
list.stream()
    .filter(new Predicate<String>() {
       @Override
       public boolean test(String s) {
          return s.startsWith("신");
       }
    })
    .forEach(n -> System.out.println(n));
System.out.println();


//for 루프 문으로 사용시
for (String name : list){
//         if (name.indexOf("신") == 0){
 if (name.startsWith("신")){ //해당 문자로 시작하니?
    System.out.println(name);
 }
}
System.out.println();

 

매핑

중간처리 기능으로 스트림의 요소를 다른 요소로 변환하는 역할을 한다.

map() : T -> R

mapToInt() : T ->int

mapToDouble() : T -> double

 

 

매핑

map은 함수형 인터페이스를 매개변수로 추상메소드 apply()를 갖고 있다.

//매핑~
studentList.stream().mapToInt(new ToIntFunction<Student>() {

    @Override
    public int applyAsInt(Student value) {
        // TODO Auto-generated method stub
        return value.getScore();
    }

})
.forEach(score -> System.out.println(score));

//Student를 score 스트림으로 변환
studentList.stream()
    .mapToInt(student -> student.getScore())
    .forEach(score -> System.out.println(score));

기본타입요소를 래퍼 객체요소로 변환하는 것도 있음

asLongStream(): int->long

asDoubleStream() : int ->double or long->double

boxed() : int->Integer, long->Long , double ->Double

 

 

 

요소를 복수개의 요소로 변환

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

 

flatMapXXX()

//기본 생김새
for(String str: list1) {
    String[] tokens = str.split(" ");
    for(String word : tokens) {
        System.out.println(word);
    }
}
//익명 객체로 구현
list1.stream()
    .flatMap(new Function<String,Stream<String>>() {

        @Override
        public Stream<String> apply(String str) {
            // TODO Auto-generated method stub
            return Arrays.stream(str.split(" "));
        }

    }).forEach(word ->System.out.println(word));
//람다식으로 구현
list1.stream().
flatMap(data -> Arrays.stream(data.split(" ")))
.forEach(word -> System.out.println(word));

System.out.println();

 

 

요소정렬

요소를 오름차순이나 내림차순으로 정렬하는 중간 처리 기능이다. Comparable을 사용자정의 자료형(내가만든클래스)에 대해서 정의해 주어야 정렬을 수행할수 있다. 기본형 타입들은 클래스에서 이미 구현하고 있기 때문에 comparable을 안 만들어도 된다.

comparator 정렬

 

//점수를 기준으로 오름차순으로 정렬한 새 스트림 얻기
//오름차순 람다식
studentList.stream()
    .sorted((s1, s2) -> s1.getScore()- s2.getScore())
    .forEach(s -> System.out.println(s.getName() + ": " + s.getScore()));	
System.out.println();
//오름차순 익명객체
studentList.stream()
.sorted(new Comparator<Student>() {

    @Override
    public int compare(Student o1, Student o2) {
        // TODO Auto-generated method stub
        return o1.getScore()-o2.getScore();
    }

})
.forEach(s -> System.out.println(s.getName() + ": " + s.getScore()));	
System.out.println();

역순정렬

compareTo의 결과 값을 내부적으로 반대로 쓴다.

stream.sorted(Comparator.reverseOrder());

 

comparator를 이용한 정렬

요소 객체가 comparable을 구현하고 있지 않다면 비교자를 제공한다.

sorted((s1, s2) -> s1.getScore()- s2.getScore())

 

 

루핑

스트림에서 요소를 하나씩가져와 반복해서 가져와 처리하는 것을 말한다. 

 

peek() 

중간처리자로 원본데이터를 변경하지 않고 모니터링 할때 사용한다. 최종처리 메소드를 뒤에 붙여주어야한다. 최종함수인 집계함수 나 forEach써준다. accept()함수 재정의해서 사용한다. 

 

forEach()

최종처리메소드

 

//값도 구하고 싶고 값을 찍어보고도 싶고
int sum2 = Arrays
        .stream(intArr)
        .filter(n -> n % 2 == 0)
        .sum();

System.out.println("sum = " + sum2);

//최종 처리 메소드 forEach()를 이용해서 반복 처리
Arrays.stream(intArr)
    .filter(a -> a%2==0)
    .forEach(n -> System.out.println(n));
    
//peek함수로 한꺼번에 할 수 있지롱
//중간 처리 메소드 peek()을 이용해서 반복 처리
int total = Arrays.stream(intArr)
    .filter(a -> a%2==0)
    .peek(n -> System.out.println(n)) 		//동작함
    .sum(); //최종 처리
System.out.println("총합: " + total + "\n");

 

매칭 

요소들이 특정 조건에 만족하는지 여부를 조사하는 최종처리 기능이다.

allMatch() -and

모든 요소가 만족하는지 여부

anyMatch() -or

최소한 하나의 요소가 만족하는지

noneMatch() -not

모든요소가 만족하지 않는지 여부

int[] intArr = { 2, 4 ,6 };
//allMatch를 걍 쓰면 
boolean result1=true;
for(int value : intArr) {
    if(value %2==1) {
        result1 = false;
        break;
    }
}
if(result1) {
    System.out.println("모든 값이 짝수");
}else {
    System.out.println("값 중에 홀수 가 존재함");
}
//allMatch로 구현
boolean result = Arrays.stream(intArr)
    .allMatch(a -> a%2==0);
System.out.println("모두 2의 배수인가? " + result);

 

요소기본집계

optional리턴 - 스트림에 내용이 없으면 null값을 리턴하는 애들

 

findFirst() -첫번째 요소 출력

max()

min()

average()

 

일반 숫자 타입리턴 - 스트림에 내용이 없으면 0을 리턴한다

count()

sum()

 

Optional클래스

isPresent() -집계값이 있는지 여부

orElse() - 집계값이 없을 경우 디폴트 값 설정

ㄴ집계값을 산출할 수없을때 나올수 잇는 예외를 방지하기 위함

ifPresent() - 집계값이 있을 경우 consumer에서 처리

 

요소 커스텀 집계

reduce(초기값, (인자)-> ..)

1~3덧셈

s=0 초기값

s= s+1

s=s+2

s=s+3

이러한 과정으로 굴러가는 함수이다.

 

 

요소 수집

필요한 요소들만 챙기기

collect()메소드에 collector를 통해 어떤 요소를 수집할 것인지 결정한다.

toList() -> 리스트로~

toSet() -> set으로~

toMap() -> Map으로~

 

요소그룹핑

Collectors.groupingBy() 함수를 통해 그룹핑키 리턴

 

Collectors

//이 기본 코드를 
List<Student> maleList2 = new ArrayList<>();
		for(Student student : totalList) {
			if(student.getSex().equals("남")) {
				maleList2.add(student);
			}
		}
//collect를 통해서 리스트로 넘김
List<Student> maleList = totalList.stream()
		 		.filter(s->s.getSex().equals("남"))
		 		.collect(Collectors.toList());

//collector를 이용해 map을 통해서 map으로 변환
//학생 이름을 키, 학생의 점수를 값으로 갖는 Map 생성
Map<String, Integer> map = totalList.stream()
        .collect(
            Collectors.toMap(
                s -> s.getName(), //Student 객체에서 키가 될 부분 리턴
                s -> s.getScore() //Student 객체에서 값이 될 부분 리턴
            )
    );

 

 

병렬 스트림사용

parallelStream()메소드는 컬렉션(List,set)으로부터 병렬스트림을 바로 리턴한다. 스레드를 코어 갯수만큼 만들어 각자 수행하고 join으로 합쳐온다. 멀티스레드의 fork()와 join()을 대신 해주기 때문에 개발자가 신경쓸 필요없다.

Stream<Integer> parallelStream = scores.parallelStream();

'JAVA공부(이것이 자바다)' 카테고리의 다른 글

입출력스트림  (0) 2023.06.14
컬렉션 자료구조  (0) 2023.06.12
스레드  (3) 2023.06.09
java.base 이어서  (0) 2023.06.08
java.base 모듈  (2) 2023.06.07