[TIL] 2월 22일

yeon·2021년 2월 22일
0

스트림이란?

  • 다양한 데이터소스(배열, 컬렉션(List, Set, Map)를 표준화된 방법으로 다루기 위한것
  • 스트림을 사용한 코드가 간결하고 재사용성도 높다.

스트림의 특징

  • 스트림은 데이터 소스를 읽기만 하고, 변경하지 않는다. (원본 변경 X, read only)
  • 스트림은 Iterator처럼 일회용이다.
    • 한번 사용하면 닫혀서 다시 사용할 수 없다. 필요하다면 스트림을 다시 생성해야한다.
  • 최종 연산이 수행되기 전까지는 중간 연산이 수행되지 않는다. → 지연된 연산
    • 중간 연산을 호출하는 것은 단지 수행되어야할 작업을 지정해주는 것
    • 최종연산이 수행되어야 비로소 스트림의 요소들이 중간 연산을 거친다.
  • 스트림은 작업을 내부 반복으로 처리한다.
    • forEach() 메서드가 해당
    • forEach()는 메서드 안으로 for문을 넣은 것이다.
    • 코드가 간결해진다.
  • 스트림의 작업을 병렬로 처리
    • 병렬스트림을 지원한다.
  • 기본형 스트림 제공 - IntStream, LongStream, DoubleStream 등등
    • Stream 대신에 IntStream을 사용하면 된다.

      → 오토박싱과 언박싱의 비효율이 줄어든다.

    • 숫자와 관련된 유용한 메서드를 IntStream이 Stream보다 더 많이 제공한다.


루카스 함수형 프로그래밍 실습문제

  1. CarTest클래스에 구현된 test코드 안에 있는 익명클래스를 람다로 바꿔본다.

    public interface MoveStrategy {
        boolean isMovable();
    }
    public class Car {
    	...
    	public Car move(MoveStrategy moveStrategy) {
            if (moveStrategy.isMovable()) {
                return new Car(name, position + 1);
            }
            return this;
      }
      ...
    }
    public class CarTest {
    
        @Test
        void 이동() {
            Car car = new Car("pobi", 0);
            Car actual = car.move(() -> true);
            assertEquals(new Car("pobi", 1), actual);
        }
    
        @Test
        void 정지() {
            Car car = new Car("pobi", 0);
            Car actual = car.move(() -> false);
            assertEquals(new Car("pobi", 0), actual);
        }
    }

    이렇게 하는게 맞나..?

  2. Lambda 클래스에 구현된 sumAll(), sumAllEven(), sumAllOverThree()의 중복을 제거한다.

    sum()이라는 메소드를 생성해서 매개변수로 List와 함수형 인터페이스 Conditional의 참조변수를 지정했다.

public class Lambda {
    ...
    public int sum(List<Integer> numbers, Conditional c){
        int total = 0;
        for(int num: numbers){
            if(c.test(num)){
                total += num;
            }
        }
        return total;
    }
		...
}
@FunctionalInterface
public interface Conditional {
    boolean test(Integer number);
}
public class LambdaTest {

    @Test
    void check_sum() {
        Lambda lambda = new Lambda();
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

        int test = lambda.sum(numbers, x -> true);
        int expected = lambda.sumAll(numbers);
        assertEquals(test, expected);

        test = lambda.sum(numbers, x -> x % 2 == 0);
        expected = lambda.sumAllEven(numbers);
        assertEquals(test, expected);

        test = lambda.sum(numbers, x -> x > 3);
        expected = lambda.sumAllOverThree(numbers);
        assertEquals(test, expected);
    }
}

테스트 코드를 신경써서 짜는게 좋겠지만 람다식 실습에 의의를 두기로..

실습에서 원하는게 이게 맞나?


자바의 정석 14장 람다와 스트림

스트림의 중간연산(823p)

스트림 자르기 - skip(), limit()

  • Stream skip(long n) : 앞에서부터 n개 건너뛰기
  • Stream limit(long maxSize) : maxSize 이후의 요소 걸러내기
  • 예제
IntStream intStream = IntStream.rangeClosed(1, 10);
intStream.skip(3).limit(4).forEach(System.out::print);

</> 실행 결과
4567

스트림 요소 걸러내기 -filter(), distinct()

  • Stream filter(Predicate<? super T> predicate) : 조건식에 맞지 않는 것 제거
    • boolean값을 반환하는 람다식을 매개변수로 줘도 된다.
  • Stream distinct() : 중복 제거
  • 예제
// distinct
IntStream intStream = IntStream.of(1, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5);
intStream.distinct().forEach(System.out::print);
System.out.println();

// filter
intStream = IntStream.rangeClosed(1, 10);
intStream.filter(i -> i % 2 == 0).forEach(System.out::print);
System.out.println();

intStream = IntStream.rangeClosed(1, 10);
intStream.filter(i -> i % 2 != 0).filter(i -> i % 3 != 0).forEach(System.out::print);
System.out.println();

</> 실행 결과
12345
246810
157

정렬 - sorted() (824p)

정렬을 할때는 정렬대상과 정렬 기준이 필요하다.

sorted()는 지정된 Comparator(정렬기준)로 스트림을 정렬한다.

→ sorted()는 Comparator 인터페이스에 구현된 메서드를 이용해서 정렬 기준을 제공할 수 있다.

  • Stream sorted()

    • 스트림 요소의 기본 정렬 기준(Comparable)으로 정렬한다.

    스트림의 요소가 Comparable을 구현한 클래스여야 한다.

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

    • int값을 반환하는 람다식을 사용할 수 있다.
  • Comparator 인터페이스에 구현된 메서드들

    • Comparator 인터페이스에 있는 메서드들을 이용하면 정렬이 쉽다.
    • 메서드들은 모두 Comparator를 반환한다.
    • 가장 기본적인 메서드는 comparing()이다.

    Comparator comparing(Function<T, U> keyExtractor)

    Comparator comparing(Function<T, U> keyExtractor, Comparator keyComparator)

    • 정렬기준이 여러개인 경우
      • thenComparing()을 붙여준다.
  • 예제

public class StreamEx1 {
    public static void main(String[] args) {
        Stream<Student> studentStream = Stream.of(
                new Student("이자바", 3, 300),
                new Student("김자바", 1, 200),
                new Student("안자바", 2, 100),
                new Student("박자바", 2, 150),
                new Student("소자바", 1, 200),
                new Student("나자바", 3, 290),
                new Student("감자바", 3, 180)
        );
        studentStream.sorted(Comparator.comparing(Student::getBan)  // 반별정렬
                .thenComparing(Comparator.naturalOrder()))          // 기본정렬 
                .forEach(System.out::println);
    }
}

class Student implements Comparable<Student> {
    String name;
    int ban;
    int totalScore;

    Student(String name, int ban, int totalScore) {
        this.name = name;
        this.ban = ban;
        this.totalScore = totalScore;
    }

    public String toString() {
        return name + ", " + ban + ", " + totalScore;
    }

    public String getName() {
        return name;
    }

    public int getBan() {
        return ban;
    }

    public int getTotalScore() {
        return totalScore;
    }

    // 총점 내림차순을 기본정렬로한다. Comparable 구현
    public int compareTo(Student s) {
        return s.totalScore - this.totalScore;
    }
}

</> 실행 결과
김자바, 1, 200
소자바, 1, 200
박자바, 2, 150
안자바, 2, 100
이자바, 3, 300
나자바, 3, 290
감자바, 3, 180
  • studentStream.sorted(Comparator.comparing(Student::getBan)
    .thenComparing(Comparator.naturalOrder()))
    .forEach(System.out::println);

    • Comparator.comparing(Student::getBan) 으로 반별로 정렬해주고

      thenComparing(Comparator.naturalOrder())로 기본 정렬을 해준다.

  • Student는 Comparable 인터페이스를 구현했다.

    • compareTo()를 오버라이딩 했다.
    • 총점 내림차순을 기본정렬로 한다.
  • 여기서 만약 studentStream.sorted()로 했으면

studentStream.sorted()
             .forEach(System.out::println);

</> 실행 결과
이자바, 3, 300
나자바, 3, 290
김자바, 1, 200
소자바, 1, 200
감자바, 3, 180
박자바, 2, 150
안자바, 2, 100
  • Comparable을 구현한 Student 클래스의 compareTo()가 기본 정렬 기준이된다.
    • 총점 내림차순 기준으로 정렬된다.

이전에 Comparable, Comparator 학습할때 무슨소린지 몰라서 넘어갔는데 좀 알거 같다. 다시 공부해봐야겠다.

변환 - map() (827p)

스트림의 요소들을 원하는 것들만 뽑아내거나, 특정 형태로 변환할 때 map()을 쓴다.

  • File 객체에서 파일의 이름(String)만 뽑아내기

    Stream<File> fileStream = Stream.of(new File("Ex1.java"), new File("Ex1),
    	new File("Ex1.bak"), new File("Ex2.java"), new File("Ex1.txt"));
    // map()으로 Stream<File>을 Stream<String>으로 변환
    Stream<String> filenameStream = fileStream.map(File::getName);
    • map으로 파일(Stream)에서 파일 이름(Strema)만 추출했다.

조회 - peek()

스트림의 요소를 소비하지 않고 엿보기

forEach()와 비슷한데 peek()은 중간연산이라서 스트림의 요소를 소모하지 않는다.

forEach()는 최종연산임

Optional와 OptionalInt (835p)

T타입 객체의 래퍼클래스

null을 간접적으로 다루기 위한 것

  • Optional
    • null을 직접 다루면 NullPointerException이 발생할 수 있어서 Optional을 통해 간접적으로 null을 다룬다.
    • null체크를 하게되면 if문이 꼭 필요하고 그럼 코드가 복잡해진다. Optional을 통해 해결할 수 있다.
  • Optional 사용의 장점
    • NullPointerException이 발생하지 않는다
    • 널체크를 위한 if문이 없어서 간결하고 안전한 코드작성 가능

Object result = getResult();

이때 result에 담기는 반환값은 (1) null 이거나 (2) 객체 일것이다.

→ 만약 result에 null이 담겼다면,

result.toString(); 이 경우에 NullPointerException 이 발생한다.

여기서 Optional 객체에 반환값을 담는다면,

→ 이 경우에 0x100 이라는 주소값이 있으니 result는 null이 될 수 없다.

public final class Optional<T> {
		private final T value;   // T타입의 참조변수
			...
}
  • T타입의 참조변수 value에는 null을 포함한 모든 타입의 객체를 저장할 수 있다.

Optional객체 생성하기 - of(), ofNullable(), empty()

  • Optional 객체 생성하는 법 - of()의 사용

    String str = "abc";
    Optional<String> optVal = Optional.of(str);
    Optional<String> optVal = Optional.of("abc");
  • 참조변수 값이 null일 가능성이 있으면 ofNullable()을 사용한다.

    Optional<String> optVal = Optional.of(null);  // NullPointerException 발생!!!
    Optional<String> optVal = Optional.ofNullable(null);  // OK
  • Optional타입의 참조변수를 기본값으로 초기화하는 경우 - empty() 사용

    Optional<String> optVal = null;  // null로 초기화, 바람직하지 않음
    Optional<String> optVal = Optional.empty();  // 빈객체로 초기화, 권장하는 방법 
    • 보통 String의 값을 초기화할 때 String str = null; 로 안하고 String str = "";로 하듯이 Optional타입의 참조변수를 초기화 할때도 empty()를 사용하는 것이 좋다.

Optional 객체의 값 가져오기 - get(), orElse(), orElseGEt(), orElseThrow()

  • get()과 orElse()의 사용

    Optional<String> optVal = Optional.of("abc");
    String str1 = optVal.get();    // optVal에 저장된 값을 반환, null이면 예외 발생
    String str2 = optVal.orElse("");    // optVal에 저장된 값이 null일때 ""반환
    • get() : optVal에 저장된 값을 반환한다.

      • null이면 예외가 발생한다.

        → 이러한 이유로 주로 get()보다는 orElse()를 쓴다.

    • orElse("") : optVal에 저장된 값이 null이면 인자를 넣어서 대체할 값을 지정할 수 있다.

  • T orElseGet(Supplier<? extends T> other)

    • orElse()의 변형으로 null 대신 대체할 값을 람다식으로 지정할 수 있다.
  • T orElseThrow(Supplier<? extends X> exceptionSupplier)

    • null일때 지정된 예외를 발생시킨다.
  • orElseGet()과 orElseThrow()의 사용

    Optional<String> optVal = Optional.of("abc");
    String str1 = optVal.orElseGet(String::new);
    String str2 = optVal.orElseThrow(NullPointerEception::new);   // null이면 예외발생 
    

Optional객체의 값 가져오기 - isPresent()와 ifPresent() (837p)

  • isPresent() : Optional 객체의 값이 null이면 false, 아니면 true 반환

    if(str != null) {
    		System.out.println(str);
    }
    • 아래의 코드는 위의 코드를 isPresent()를 활용해서 작성한 것이다.
    if(Optional.ofNullable(str).isPresent()) {
    		System.out.println(str);
    }
  • ifPresent() : Optional의 value가 null이 아닐때 수행할 작업을 람다식으로 지정해준다.

    Optional.ofNullable(str).ifPresent(System.out::println);
    • 메소드 참조 System.out::println 을 람다식으로 바꾸면
      • v → System.out.println(v);

OptionalInt, OptionalLong, OptionalDouble(838p

기본형값을 감싸는 래퍼클래스

Optional를 써도 되지만 성능때문에 이와 같은 것들을 사용한다.

11장

Comparator와 Comparable(628p)

객체 정렬에 필요한 메서드를 정의한 인터페이스

정렬 기준을 제공하는 것이 목적

  • Comparable : 기본 정렬 기준을 구현하는데 사용

    public interface Comparable {
    		 public int compareTo (Object o);
    }
    • compareTo(Object o) : 주어진 객체(o)를 자신(this)와 비교
  • Comparator : 기본 정렬 기준이외에 다른 기준으로 정렬할 때 사용

    public interface Comparator {
    		int compare(Object o1, Object o2);
    		boolean equals(Object obj);
    }
    • compare(Object o1, Object o2) : o1, o2 두 객체를 비교해서 어느쪽이 더 큰지 int값으로 반환
      • 결과값이 0이면 같음, 양수면 왼쪽이 큼, 음수면 오른쪽이 큼

오늘 한일

  • 학습을 하면 항상 개념서에 있는 내용과 강의에서 받아적은 내용을 거의 그대로 정리해서 더 빨리 잊혀지는 것 같다. 나만의 언어로 정리해야할거 같은데 개념을 이해하기에 급급한듯 하다.
  • 루카스 함수형프로그래밍에 나온 람다 실습을 해봤는데 맞게 했는지 모르겠다 ㅎㅎㅎ
  • 자바의 정석 스트림 중간연산
    • 스트림을 자르는 skip()과 limit(), 요소 걸러내는 filter()와 distinct(), 정렬하는 sorted()는 Comparator 인터페이스에 구현된 메서드를 이용해서 정렬 기준을 제공할 수 있다, 정렬기준을 제공하는 인터페이스 Comparator와 Comparable(기본 정렬 기준), 스트림의 요소를 변환하는 map(), forEach()와 비슷하지만 중간연산이여서 스트림의 요소를 소모하지 않고 System.out.print와 같은걸 이용해서 연산이 잘되었는지 확인하는 peek()
  • null을 간접적으로 다루기 위한 래퍼클래스 Optional, of()또는 ofNullable()을 이용해서 Optional 객체 생성하기, Optional.empty()를 이용해서 빈 Optional 객체 생성하기, orElse()와 orElseGet()을 이용하여 Optional 객체에 저장된 값 가져오기

Todo

(내일)

  • 스트림 최종연산 학습
  • 루카스 함수형 프로그래밍 실습 마저하기
  • 함수형 프로그래밍이란? 특징 나만의 언어로 정리하기
  • 미션5 도전..?

관심 있을 만한 포스트

0개의 댓글