해보자

[Java] Java8 API의 default 메소드, static 메소드 본문

Java

[Java] Java8 API의 default 메소드, static 메소드

안댕 2020. 9. 7. 22:58

이 글은 인프런, 백기선 - 더 자바, Java 8강의인터페이스 변화, 인터페이스 기본 메소드와 스태틱 메소드강의의 내용을 공부하고 작성한 글입니다.


 

Iterable의 default 메소드

다음과 같이 List를 만들어놓고 시작하자.

List<String> fruits = new ArrayList<>();
fruits.add("apple");
fruits.add("banana");
fruits.add("grape");
fruits.add("melon");

forEach()

default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
        action.accept(t);
    }
}

iterable의 forEach method를 열어보면 위와 같다.

  • Consumer
    • 값을 1개 받기만 핟고 리턴은 하지 않음
    • Consumer
      • 객체 T를 받아서 소비
      • accept method를 정의해야 하는 interface
        • 값을 받아옴
  • requireNonNull
    • null인지 확인

즉, this를 순회하면서 t값을 하나하나 받는다.

forEach문을 이용한 출력 순회

간단하게 for문을 사용해서 출력할 수 있다.

for(String fruit: fruits) {
    System.out.println(n);
}

결과

apple
banana
grape
melon

코드를 실행하면 위와 같은 값을 출력한다.

아래와 같이 forEach를 사용해서 이런식으로 표현할 수 있다.

fruits.forEach( fruit -> System.out.println(fruit));

위의 코드는 다음처럼 간단하게 사용할 수있다.

  • 특정 객체의 인스턴스 메소드 참조 (객체 래퍼런스::인스턴스 메소드) 방식을 이용
fruits.forEach(System.out::println);

spliterator()

  • java8부터 추가된 기능
Spliterator<String> spliterator = fruits.spliterator();
while(spliterator.tryAdvance(System.out::println));
  • Spliterator
    • tryAdvance를 필수로 구현해야 하는 interface
      • 요소가 남아있으면 계속해서 수행한다.
      • 있으면 true, 없으면 false를 반환

forEach와 동일한 내용을 출력한다.

Spliterator<String> spliterator = fruits.spliterator();
Spliterator<String> spliterator1 = spliterator.trySplit();
while(spliterator.tryAdvance(System.out::println));
System.out.println("============");
while(spliterator1.tryAdvance(System.out::println));

결과

grape
melon
============
apple
banana
  • trySplit : spliterator를 분할 할 수있는 경우 Spliterator를 반환(반으로 쪼개기)
    • parallel하게 쓸 때 유용

 

Collection의 기본 메소드

stream()

이어서 쓸 수 있음

default Stream<E> stream() {
        return StreamSupport.stream(spliterator(), false);
    }

Collection 클래스에 정의된 stream을 열어보면 위와 같다.

  • spliterator로 만들어져 있다.
  • StreamSupport 클래스에 있는 stream static 메서드를 반환한다.

다음과 같이 여러개의 메서드를 functional하게 사용 가능하다.

long e = fruits.stream().map(String::toUpperCase)
                .filter(s->s.endsWith("E"))
                .count();
System.out.println(e);

결과

2

parallelStream()

fruits.forEach(System.out::println);
System.out.println("=============");
fruits.parallelStream().forEach(System.out::println);

결과

apple
banana
grape
melon
=============
grape
melon
banana
apple

stream이 병렬로 실행될 때, Java runtime은 stream을 여러 하위 stream으로 분할한다. 집계 작업은 이런 하위 stream을 반복하고 병렬로 처리한 다음에 결과를 결합하는 식으로 사용한다.

forEach에서는 stream의 순서를 보장할 수 없다. 그렇다면 forEachOrdered를 사용할 수 있지만 이렇게하면, 병렬적으로 처리했다고 할 수 없다. 따라서, 순서를 보장받는 것이 중요하지 않을 떄만 parallel하게 사용하는 것이 좋다.

removeIf(Predicate)

default boolean removeIf(Predicate<? super E> filter) {
        Objects.requireNonNull(filter);
        boolean removed = false;
        final Iterator<E> each = iterator();
        while (each.hasNext()) {
            if (filter.test(each.next())) {
                each.remove();
                removed = true;
            }
        }
        return removed;
    }
  • Predicate
    • 어떤 인자값을 하나 받아서 true/false를 리턴해주는 함수 인터페이스
    • not, or, and를 사용해 조합도 가능
  • 해당 컬렉션의 주어진 조건을 만족하는 모든 요소를 제거
  • 리턴 형식은 boolean
fruits.removeIf(s->s.startsWith("a"));
fruits.forEach(System.out::println);

결과

banana
grape
melon

 

Comparator의 default 메소드 및 static 메소드

reversed()

Comparator<String> compareToIgnoreCase = String::compareToIgnoreCase;
fruits.sort(compareToIgnoreCase.reversed());
fruits.forEach(System.out::println);
  • compareToIgnoreCase
    • 특정 객체의 인스턴스 메소드 참조 방식 : 객체 레퍼런스::인스턴스 메소드
    • 대소문자를 무시하고 사전적으로 두 문자열을 비교
  • reversed
    • 역순 정렬(reverseOrder)된 comparator를 반환

결과

melon
grape
banana
apple

thenComparing()

thenComparing을 사용하기에 앞서 Fruit class를 정의한다.

public class Fruit {
    String name;
    String color;

    public Fruit(String name, String color) {
        this.name = name;
        this.color = color;
    }

    public String getName() {
        return name;
    }

    public String getColor() {
        return color;
    }
}

String으로 이뤄졌던 Fruits List를 Fruit객체를 사용하여 재정의한다.

List<Fruit> fruits = new ArrayList<>();
fruits.add(new Fruit("grape", "purple"));
fruits.add(new Fruit("grape", "green"));
fruits.add(new Fruit("apple", "red"));
fruits.add(new Fruit("melon", "green"));
fruits.add(new Fruit("banana", "yellow"));

Collections.sort(fruits, Comparator.comparing(Fruit::getName)
        .thenComparing(Comparator.comparing(Fruit::getColor)));

fruits.forEach(fruit -> System.out.println(fruit.getName() + " : " + fruit.getColor()));

결과

apple : red
banana : yellow
grape : green
grape : purple
melon : green
  • Comparator를 인자로 받는다.
  • 이런식으로 복수개의 comparator 조건을 줄 수 있다.
  • comparing
    • java.lang.Comparable을 상속받는다.
    • 정렬 키를 인자로 받는다.

static reverseOrder()

  • 역순 정렬
Comparator<String> cmp = Collections.reverseOrder();
Collections.sort(fruits, cmp);
fruits.forEach(System.out::println);

결과

melon
grape
banana
apple
Comparator<Fruit> cmp = Collections.reverseOrder(Comparator.comparing(Fruit::getName))
        .thenComparing(Comparator.comparing(Fruit::getColor));
Collections.sort(fruits, cmp);
fruits.forEach(fruit -> System.out.println(fruit.getName() + " : " + fruit.getColor()));

마찬가지로, 이런식으로 thenComparing과 같이 사용 가능하다.

reversed() 는 default 메서드고, reverseOrder()는 static 메서드라는 차이가 있다.

static naturalOrder()

  • 정방향으로 정렬
fruits.sort(Comparator.naturalOrder());
fruits.forEach(System.out::println);

결과

apple
banana
grape
melon

static nullsFirst()

아까 사용한 String List에서 null값을 임의로 넣어 보겠습니다.

List<String> fruits = new ArrayList<>();
fruits.add("banana");
fruits.add("apple");
fruits.add("grape");
fruits.add(null);
fruits.add("melon");
fruits.sort(Comparator.nullsFirst(Comparator.naturalOrder()));
fruits.forEach(System.out::println);

위와 같이 사용했을 때 맨 앞에 null값을 보내고 정렬

결과

null
apple
banana
grape
melon

static nullsLast()

fruits.sort(Comparator.nullsLast(Comparator.naturalOrder()));
fruits.forEach(System.out::println);

nullsFirst()와 반대로 null값을 맨 뒤로 보내고 정렬

apple
banana
grape
melon
null

'Java' 카테고리의 다른 글

[Java] Data Structure | Deque  (0) 2020.09.08
[Java] 디렉토리 조회 기능 구현하기  (0) 2020.07.22
[Java ] Appending ObjectOutputStream  (0) 2020.06.22