본문 바로가기

개인적으로 공부한 것을 정리해 놓은 블로그입니다 틀린 것이 있으면 댓글 부탁 드립니다!


JAVA

JAVA 스터디 24 - 스트림(Stream)

반응형

자바에서는 다양한 데이터 소스를 표준화된 방법으로 다루기위해 이전의 데이터소스들(List,Set,Map, 배열 등.)은

각각 성격이 달라 데이터를 다루는 방법이 모두 달랐다.

JDK 1.8버전 이후부터  추가된 Stream은 데이터를 다루는 방법을 표준화 한 기능이다   

 

 

 

stream 작동 방식

구현방식은 위의 그림과 같다 데이터소스는 스트림으로 변환되고 중간 연산은( n번가능)  최종 연산(한번)을 거쳐 결과가 만들어진다 . 

 

 

 

 

스트림의 특징

 

-스트림은 데이터소스 (원본) 데이터를 읽기만할 뿐 (Read Only) 변경지 않는다.

List<Integer> list = Arrays.asList(3,1,5,1,4,2);
        List<Integer> sortedList = list.stream().sorted().collect(Collectors.toList());

        //[3, 1, 5, 1, 4, 2]
        System.out.println(list);
        //[1, 1, 2, 3, 4, 5]
        //원본을 변경하지 않고 새로운 리스트를 만들어낸다.
        System.out.println(sortedList);

 

-스트림은 Iterator 처럼 일회용이다. (필요하면 다시 스트림을 생성해야 한다.)

//스트림은 Iterator 처럼 일회용이다. (필요하면 다시 스트림으 생성해야 함)
        //최종 연산 단계에서 스트림의 요소를 소모해버리기 때문에
        //최종 연산이 끝나면 스트림은 닫혀버림
        Stream<Integer> s = list.stream();
        s.sorted().forEach(i-> System.out.println(i));
        //에러가 난다 
        s.forEach(System.out::println);
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
	at java.util.stream.AbstractPipeline.sourceStageSpliterator(AbstractPipeline.java:279)
	at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
	at StreamExam.main(StreamExam.java:26)

-스트림은 최종 연산 전까지 중간연산이 수행되지 않는다.- 지연된 연산

//스트림은 최종 연산 전까지 중간연산이 수행되지 않는다.- 지연된 연산
        IntStream intStream = new Random().ints(1,46);
        intStream.distinct().limit(6).sorted()
                .forEach(i-> System.out.println(i+","));
        //메서드가 호출되었을떄 스트림 처리를 바로하는게 아니라 표시만 해두었다가
        //나중에 수행한다. (지연된 연산)

 

-스트림은 내부 반복으로 처리된다.  forEach 메서드 안에 for 문이 감춰져 있다 . 성능은 비효율 적이지만 코드가 간결 하다 

 

-스트림의 작업을 병렬로 처리 - 병렬 스트림 지원 (parell stream)

Stream<String> stringStream = Stream.of("dd","aa","CC","ff","h");
        int sum = stringStream.parallel().mapToInt(str -> str.length()).sum();
        System.out.println("---------------");
        System.out.println(sum);
        System.out.println("---------------");

 

-기본형 스트림 제공 IntStream LongStream DoubleStream

 

//기본형 스트림 제공 - IntStream LongStream DoubleStream
        //오토박싱&언박싱의 비효율이 제거됨 (Stream<Integer> 대신 IntStream 사용)
        //기본형 데이터 타입에 최적화된 유용한 메서드를 제공한다.
        int [] intArr = {1,2,3,4,5,}; 
        IntStream stream = Arrays.stream(intArr);

위와 같이 기본형 배열인 intArr의 경우 Stream<Integer>를 사용하게 되면 기본형 데이터타입은  int가 Interger로 변환되면서 성능상에 문제가 일어날 수 있기 때문에  기본형 스트림을 제공한다.

스트림 작업 순서

1.스트림 만들기

- Collection 인터페이스에 stream()으로 컬렉션을 스트림으로 만들기

 

List<Integer> list = Arrays.asList(1,2,3,4,5);

Stream<Integer> intStream = list.stream();

int Stream.forEach(System.out::print);

//최종 연산이 끝나 스트림이 이미 닫혔기 때문에 에러가난다.

int Stream.forEach(System.out::print);

 -객체 배열로부터 스트림 생성하기

//인자를 넣어서 Stream 생성am 생성
Stream<T> Stream.of(T...values) // 가변 인자
//배열을 넣어서 Stream 생성
Stream<T> Stream.of(T [])
//배열을 생성하면서 Stream 함께 생성
Stream<T> Arrays.stream(T [])
//배열을 넣고 원하는 인덱스를 시작과 끝점을 지정해서 그 인덱스의 요소만으로 Stream을 생성
Stream<T> Arrays.stream(T [] array, int startInclusive , int endExclusive)

 

무한 난수 스트림

 

public class IntStreamExam {
    public static void main(String[] args) {
        //난수를 무한으로 생성하는 스트림 (무한 스트림)
        IntStream intStream = new Random().ints();
        intStream.forEach(System.out::println);
        //무한스트림은 limit()으로 잘라 주는게 좋다.
        //갯수와 범위를 직접 줄 수도 있다
        // 5개 나오게 하고 10 아래의 숫자  
        IntStream intStream2 = new Random().ints(5,10);
        //limit으로 자를 수도 있다.5개만 나온다
        intStream2 .limit(5);
    }
}

 

특정 범위의 정수를 요소로 갖는 스트림 생성 (intStream, LongStream)

 

		//1,2,3,4
        IntStream intStream1 = IntStream.range(1,5);
        //1,2,3,4,5
        IntStream intStream4 = IntStream.rangeClosed(1,5);

 

 

2. 스트림의 중간연산

 

스트림 자르기 

 

- 무한 스트림의 경우 값이 무한으로 생성되기 때문에 자르기가 필요하다.

 

skip(long n) - 앞에서부터 n개 건너뜀

//스트림 자르기
//skip(long n) - 앞에서부터 n개 건너뜀
IntStream intStream = IntStream.rangeClosed(1,10);
intStream.skip(3).limit(5).forEach(System.out::print);

limit(long maxSize) - maxSize 이후의 요소는 잘라냄

 //limit(int maxSize) - maxSize 이후의 요소는 잘라냄
IntStream intStream1 = IntStream.rangeClosed(1, 10);
intStream1.limit(5).forEach(System.out::print);

 

스트림의 요소 걸러내기

 

 filter() - 조건에 맞지 않는 요소 제거

 

 

//filter() - 조건에 맞지 않는 요소 제거
IntStream intStream3 = IntStream.rangeClosed(1,10);
//결과값 246810
intStream3.filter(i->i%2==0).forEach(System.out::print);

//중간연산이기 때문에 여러번 사용가능
IntStream intStream4 = IntStream.rangeClosed(1,10);
//결과값 2,4
intStream4.filter(i->i%2 == 0 ).filter(i->i<6).forEach(System.out::print);

 

distict()- 중복되는 요소를 제거

 

//distict()- 중복되는 요소를 제거
IntStream intStream2 = IntStream.of(1,2,2,3,3,3,4,5,5,6);
 //결과값 123456
intStream2.distinct().forEach(System.out::print);

 

스트림 정렬

 

 

	//스트림 정렬

        Stream<String> strStream = Stream.of("a","b","c","C","d","e");
        //결과값 Cabcde  <-sorted()에 Comparator를 따로 지정하지 않으면 기본정렬 됨
        //대문자가 우선으로 나온다
        strStream.sorted().forEach(System.out::print);

        //(s1,s2)->s1.compareTo(s2) <- 아래의 메서드 참조의 람다식
        //첫번째 파라미터를 기준으로 compareTo의 파라미터를 비교해서 정렬
        strStream.sorted(String::compareTo).forEach(System.out::print);

        //기본 정렬에서 역순으로 정렬
        //결과값 edcbaC
        strStream.sorted(Comparator.reverseOrder()).forEach(System.out::print);

        //대소문자 구분없이 정렬
        //String.CASE_INSENSITIVE_ORDER 스트링 클래스에 정의되 있는 Comparator다
        //결과값 abcCde
        strStream.sorted(String.CASE_INSENSITIVE_ORDER).forEach(System.out::print);

        //comparing의 파라미터인  Function을 기준으로 정렬
        Stream<String> strStream = Stream.of("a","bb","ccc","C","dddd","eeeee");
        //결과값 aCbbcccddddeeeee
        strStream.sorted(Comparator.comparing(String::length)).forEach(System.out::print);

 

comparing() - comparing의 파라미터 기준으로 정렬

 

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class ComparingExam{
    public static void main(String[] args) {
        //학생 리스트 생성
        List<Student> students = new ArrayList<>();
        //학생 인스턴스 생성수 학생리스트에 추가
        for(int i = 1; i<11; i++){
            Student student = new Student("student"+i, i);
            if(i%2==0){
                student = new Student("student"+(i), i-1);
            }
            students.add(student);
        }
        //반 번호를 기준으로 정렬 후 출력
        students.stream().sorted(Comparator.comparing(Student::getClassNum))
                .forEach(student -> System.out.println(
                                  "classNum="
                                  +student.getClassNum()
                                  +",studentName="
                                  +student.getName())
                                );
        //정렬 기준이 여러개일 때 thenComparing();
        students.stream().sorted(
        			 Comparator.comparing(Student::getClassNum)
        			.thenComparing(Student::getName))
                		.forEach(student -> System.out.println(
                                  "classNum="
                                  +student.getClassNum()
                                  +",studentName="
                                  +student.getName()));
      }
}

class Student{
    private String name;
    private int classNum;

    public String getName() {
        return name;
    }

    public int getClassNum() {
        return classNum;
    }

    public Student(String name, int classNum) {
        this.name = name;
        this.classNum = classNum;
    }
}

결과값

classNum=1,studentName=student1
classNum=1,studentName=student2
classNum=3,studentName=student3
classNum=3,studentName=student4
classNum=5,studentName=student5
classNum=5,studentName=student6
classNum=7,studentName=student7
classNum=7,studentName=student8
classNum=9,studentName=student9
classNum=9,studentName=student10

Process finished with exit code 0

 

스트림의 요소 변환

 

map() - 스트림의 요소 변환

peek() - 스트림의 요소를 소비하지 않고 엿볼 수 있다. 

import java.util.ArrayList;
import java.util.List;

public class StreamMapExam {
    public static void main(String[] args) {
        List<File> fileList = new ArrayList<>();
        fileList.add(new File("a.exe"));
        fileList.add(new File("b.exe"));
        fileList.add(new File("c.jpg"));
        fileList.add(new File("d.jpg"));
        fileList.add(new File("e.png"));
        fileList.add(new File("f.png"));
        fileList.add(new File("g"));
        fileList.add(new File("h"));

        //fileList에서 이름을 꺼내고
        fileList.stream().map(File::getFileName)
                //확장자가 있는지 확인 없으면 거른다
                .filter(s->s.contains("."))
                //중간 확인
                .peek(s -> System.out.println(s))
                //.다음까지만 자르고
                .map(s -> s.substring(s.indexOf('.')+1))
                //대문자로 바꾸고
                .map(String::toUpperCase)
                //중복제거해서
                .distinct()
                //출력
                .forEach(System.out::println);
    }
}

class File{
    private String fileName;

    public String getFileName() {
        return fileName;
    }

    public File(String fileName) {
        this.fileName = fileName;
    }
}

 

flatMap() - 스트림의 스트림을 스트림으로 변환

public class FlatMapExam {
    public static void main(String[] args) {
        String [] arr1= {"a","b","c"};
        String [] arr2 = {"d","e","f"};

        //위 두개의 배열로 스트림을 만들면  아래처럼 String [] 의 스트림이 된다.
        //스트림에서 사용하는 데이터는 {"a","b","c"} ,{"d","e","f"} 이런 형태일 것이다.
        Stream<String[]> arrStream = Stream.of(arr1, arr2);

        //flatMap은 위와같은 배열의 타입 스트림을 flat 하게 만들어준다.
        //(arr)->Arrays.stream(arr);
        //flatMap의 데이터는 "a","b","c","d","e","f"의 형태이다.
        //아래처럼 String stream 하나로 합쳐졌다.
        Stream<String> stringStream = arrStream.flatMap(Arrays::stream);

    }
}

 

flatMap() 활용 -단어별로 문자열 출력 

 

import java.util.Arrays;
import java.util.stream.Stream;

public class FlatMapExam2 {
    public static void main(String[] args) {
        String [] arr = {"The Spring Framework is an application framework and " +
                " inversion of control container for the Java platform",
                " The framework's core features can be used by any Java application"};

        //String [] arr 의 2개의 라인으로 Stream이 생성
        Stream<String> lineArr = Arrays.stream(arr);
        //1.line.split(" +")으로 공백이 하나 이상인 것마다 나눠지고 문자열 배열로 리턴된다.
        //2.1번의 문자열 배열들을 flatMap으로 하나로 만들고
        Stream<String> stringStream = lineArr.flatMap(line -> Stream.of(line.split(" +")));
        //출력
        stringStream.forEach(System.out::println);

        /* 결과 - 단어 단위로 잘려서 출력되었다.
            The
            Spring
            Framework
            is
            an
            application
            framework
            and
            inversion
            of
            control
            container
            for
            the
            Java
            platform

            The
            framework's
            core
            features
            can
            be
            used
            by
            any
            Java
            application
         */
    }
}

 

 

 

반응형