티스토리 뷰

  • sorted 메서드

Stream 클래스에서는 정렬 메서드를 제공한다.


Stream<T> sorted()

Stream<T> sorted(Comparator<? super T> comparator)



위 두메서드가 Stream클래스의 중간 연산 정렬 메서드인 sorted이다.


매개변수가 없는 sorted() 메서드는 Comparable타입을 구현하는 클래스만을 대상으로 정렬을 하기 때문에 만일 스트림의 요소 타입이 Comparable을 구현하고 있지 않다면 컴파일시 다음과 같은 에러가 발생한다. 

(개발자가 정의한 클래스라도 적절한 Comparable구현이 이루어지면 sorted()의 사용이 가능하다)


Exception in thread "main" java.lang.ClassCastException: 람다식.Board cannot be cast to java.lang.Comparable



String타입은 comparable을 구현하고 있다. 그렇기 때문에 기본정렬인 sorted()메서드의 사용이 가능하다.

기본정렬은 오름차순이기 때문에 만약 반대인 내림차순으로 정렬하고 싶다면 sorted(Comparator c)메서드의 인자에 Comparator.reverseOrder()를 집어 넣으면 된다.

         String[] arrStr = new String[]{"konan","paul","jodan","jhone","michele","joy","kim"};
 
         //스트림의 요소가 comparable을 상속받는 타입일 때 기본 정렬(오름차순)
         Stream<String> streamm = Stream.of(arrStr);
         streamm.sorted().forEach((s) -> {
             System.out.print(s+",");
         });
         
         System.out.println();
         //스트림의 요소가 comparable을 상속받는 타입일 때 기본 정렬(내림차순)
         streamm = Stream.of(arrStr);
         streamm.sorted(Comparator.reverseOrder()).forEach((s) -> {
             System.out.print(s+",");
         });
cs



스트림의 요소타입이 Comparable을 구현하지 않을 때는 두 번째 방법인 sorted(Comparator c)메서드를 사용해야 한다. JAVA 8부터 Comparator인터페이스에 정렬과 관련된 메서드가 추가 되었으며, 이를 이용하면 스트림의 정렬이 더욱 쉬워진다.


         class Board{
            private int no;
            private String title;
    
            public int getNo() {
                return no;
            }
            public void setNo(int no) {
                this.no = no;
            }
            public String getTitle() {
                return title;
            }
            public void setTitle(String title) {
                this.title = title;
            }
    
            public String toString(){
                return this.no + " : " + this.title;
            }
         }
 
 
 
         Board[] bArr =  new Board[6];
         String[] titles = new String[]
                 {"불가능은 없다","빚더미 오른 대학원생들","변호사의 부동산 중개는 합법일까"
                 ,"현재 상영 영화","요요현상의 원인","소프트뱅크 ARM 인수"};
         for(int i=0; i<bArr.length; i++){
             bArr[i] = new Board();
             bArr[i].setNo(i+1);
             bArr[i].setTitle(titles[i]);
         }
         
         
         Stream<Board> bStream = Stream.of(bArr);
         //Comparator
         Comparator<Board> compare = Comparator.comparing((Board b) -> b.getTitle()).reversed();
         bStream.sorted(compare).forEach(System.out::println);
cs

Comparator.comparing((Board b) -> b.getTile()) : title에 대하여 오름차순 정렬

Comparator.comparing(Board::getTitle)로도 표현가능

Comparator.comparing((Board b) -> b.getTile()).reversed() : title에 대하여 내림차순 정렬



그리고 정렬 조건을 추가 할 때는 thenComapring()메서드를 통해서 추가할 수 있다.

Comparator<Board> compare = Comparator.comparing(Board::getTitle).reversed().thenComparing(Board::getNo);
cs







  • map 메서드

스트림의 요소에 저장된 값에서 원하는 변수 값만 추출하거나 특정 형태로 다시 스트림을 반환 해야할 필요가 있는데 이때에 map()을 사용한다.


Stream<R>   map(Function<super T,? extends R> mapper)
cs

보는 것 처럼 map메서드의 매개변수는 Function이라는 함수형 인터페이스다. 또한 generic 인터페이스 이기도하다.

이 인터페이스를 보면 R apply(T t)라는 메서드를 제공하는데 R타입변수는 리턴타입이고, T타입변수는 매개변수로 제공되는 타입이다.



        File file = new File("c:\\tmps\\");
        Stream<File> fileArrStream = Stream.of(file.listFiles()); 
         
        fileArrStream.map(File::getName)
                     .map(String::toUpperCase)
                     .filter(str -> {if(str.indexOf("JPG"> 0return falseelse return true; })
                     .forEach(System.out::println);
cs

map메서드를 사용하는 법이다. 

map은 스트림의 중간연산 메서드이기 때문에 연달아서 사용할 수 있다.



처음 map을 호출할 때 map(File::getName)을 사용하고 있는데.. 위의 메서드 형태에서 보자면  R은 String, T는 File이 된다.

String apply(File f);를 호출하면서 최종적인 형태는 다음과 같이 된다.

String apply(File f){
   return f.getName();
}
cs


또한 map의 반환타입이 Stream<R>이니 이것도 Stream<File>에서 Stream<String>이 될것이다.

Stream<String> st = fileArrStream.map(File::getName);  
cs






  • peek 메서드

중간 연산 메서드 사이사이에 연산 결과를 확인하고 싶다면 peek을 사용한다.

이 메서드는 forEach와는 달리 요소를 소모하지 않는 중간 연산이기 때문에 여러번 호출 할 수 있다.




Stream<T>    peek(Consumer<super T> action)
cs

보시다시피 peek메서드는 매개변수로 Consumer타입을 가지고 있으며 이 타입역시 함수형 인터페이스다.

내부적으로는 리턴타입이 없는(void) 매개변수가 T인 메서드를 호출 하기 때문에 이에 맞는 람다식을 작성하면 된다.




앞서 코딩한 map코드에서 peek을 추가 해보겠다. 중간 연산 메서드 사이사이에 콘솔로 결과를 확인하고 싶을 때 이런 식으로 하면된다.

        File file = new File("c:\\tmps\\");
        Stream<File> fileArrStream = Stream.of(file.listFiles()); 
         
        fileArrStream.map(File::getName)
                     .peek(System.out::println)
                     .map(String::toUpperCase)
                     .peek(System.out::println)
                     .filter(str -> {if(str.indexOf("JPG"> 0return falseelse return true; })
                     .forEach(System.out::println);
cs






  • flatMap 메서드


Stream에 있는 중간 연산 메서드인 flatMap메서드는 어떤 스트림의 요소가 배열일 때. 즉, Stream<Integer[]>일 때 이 스트림에 대하여 Stream<Integer>와 같이 변경을 해준다.


다음 코드를 보자.

        Integer[][] arr = new Integer[][]
                {
                    { 123 },
                    { 456 }
                };
        
        Stream<Integer[]> st = Arrays.stream(arr);
        
        
        Stream<Stream<Integer>> st1 = st.map(Stream::of);
        System.out.println(st1.count()); //출력값 2
        
        st = Arrays.stream(arr);
        Stream<Integer> st2 = st.flatMap(Stream::of);
        System.out.println(st2.count()); //출력값 6
cs

2차원 배열을 스트림으로 변환하면 요소를 Integer[] 배열처럼 갖게 된다.


여기에 다시 map메서드를 사용해본다. 

map은 알고 있는 것처럼 스트림 요소의 타입을 원하는 필드에 대응하여 다시 반환하는 기능을 하지만 map의 매개변수로 있는 Stream.of메서드의 반환은 스트림이고, map메서드 자체의 반환타입도 스트림이다. 

그렇기 때문에 코드에서 처럼 스트림을 요소로 가지는 스트림타입의 참조 변수가 이 map메서드의 최종 반환 타입이 된다.


하지만 flatMap메서드를 사용하면 대상 스트림의 타입요소가 배열타입 일지라도 하나의 타입이 타입 요소가 되는 스트림을 반환해준다.






Stream<Stream<Integer>> st1 = st.map(Stream::of);

다음과 같이 스트림을 요소로 갖는 스트림도 map과 flatMap메서드를 이용해서 단독 타입을 가지는 스트림으로 변환할 수 있다.




        Stream<Integer> ss = st1.map(array -> array.toArray(Integer[]::new)).flatMap(Stream::of);
        ss.forEach(System.out::println);
cs

map에서 인자에 위치한 람다식에서 array는 위 코드에서 보면 {1,2,3}그리고 {4,5,6}이 된다. 스트림의 toArray메서드를 사용하면 해당 스트림을 배열로 반환한다. 매개변수를 적지 않으면 Object타입의 배열을 반환하고 매개변수를 지정하면 해당 타입의 배열을 반환한다.


어쨋든 map메서드를 사용함으로써

Stream<Integer[]>타입의 스트림이 되었으며, 다시 여기에서 배열타입을 요소로 가지는 스트림을 가지고 flatMap을 호출하면 배열타입이 아닌 단일 타입을 요소로 가지는 스트림을 반환해준다.







  • Optional 객체


Stream에는 중간연산 메서드와 최종연산 메서드가 존재한다고 얘기했다..

중간연산 메서드는 스트림에 대해서 여러번 호출할 수 있는 메서드를 말하고, 최종연산 메서드는 마지막에 한번 호출 할 수 있는 메서드를 말한다.



최종연산 메서드에는 다음과 같이 return type이 Optional인 메서드가 있다.

Optional<T> findAny()

Optional<T> findFirst()

Optional<T> max(Comparator<? super T> comparator)

Optional<T> min(Comparator<? super T> comparator)

Optional<T> reduce(BinaryOperator<T> accumulator)



이 메서드들은 타입을 바로 반환하는게 아니라 Optional이라는 곳에 타입을 넣어서 반환한다. 이 같이 객체에 담아서 반환을 하면, 반환된 결과가 null인지 매번 체크하는 대신 Optional에 정의된 메서드를 통해서 간단히 처리할 수 있다.

class ContentStr{
    
    private static final String message = "값이 없네요";
    
    static String getMessage(){
        return message;
    }
}
 
public class OptionalEx {
    public static void main(String[] args) throws Exception{
        
        Optional<String> o = Optional.empty(); //Optional은 null값을 지정하기 보단 empty()를 쓰는 것이 좋다.
        
        String str = "Monster";
        String strNull = null;
        Optional<String> o1 = Optional.of(str);
        //Optional<String> o2 = Optional.of(strNull); //null값인데 ofNullable을 사용하지 않아 익셉션 발생 
        Optional<String> o3 = Optional.ofNullable(strNull);
        
        String str1 = o1.get();
        System.out.println(str1);
        //o3.get(); //null값을 가져오려고 하면 NoSuchElementException발생!!
        
        String str3 = o3.orElse("값이 없습니다."); //get대신 orElse메서드를 사용하면 null값일 때 인자에 지정한 값으로 대체된다.
        System.out.println(str3);
        
        
        String message = o3.orElseGet(ContentStr::getMessage); //orElseGet을 사용하면 람다식으로 대체 문자를 지정할 수 있다.
        System.out.println(message);
        
        //orElseThrow메서드를 이용하여 null일경우 람다식으로 예외를 발생시키고 있다.
        //String throwmessage = o3.orElseThrow(() -> { return new Exception("값이 없습니다."); });
        
        
        //isPresent는 값이 있으면 true를 null이면 false를 반환한다.
        if(o3.isPresent()){
            System.out.println("o3의 값은"+o3.get());
        }else{
            System.out.println("값이 null");
        }
        
        
    }
}
cs


'JAVA > 정리' 카테고리의 다른 글

[Stream] 스트림 part1  (0) 2016.07.16
[Lambda Expression] 람다식  (0) 2016.05.02
[generics] 제네릭스  (0) 2016.04.21
익명 클래스  (0) 2016.04.07
Thread  (0) 2016.01.14
Comments
최근에 올라온 글
최근에 달린 댓글
TAG
more
Total
Today
Yesterday