콘텐츠로 이동

2021 02 16

2021-02-16

학습 Log

  • 스스로 기록하는 학습 이력 - 학습한 내용에 가중치를 부여해 미션에 어떤 공부를 중점적으로 했는지 기록

TDD

  • TDD란? - 테스트를 먼저 개발하기 - 동작하는 production 코드 만들기 - 리팩토링 진행하기 - 각 테스트 별로 필요한 만큼 구현할 것
  • TDD의 장점 - 자동화된 테스트 + 배포 부담을 줄이면서 Business Logic에 집중

일급 컬렉션

  • 참고: https://jojoldu.tistory.com/412
  • 객체지향 생활체조 (규칙 8: 일급 컬렉션 사용) - 컬렉션을 포함한 클래스는 반드시 다른 멤버 변수가 없어야 한다. - 그 자체로 포장되어 있으므로, 콜렉션과 관련된 동작은 근거지가 마련된 셈이다. - 두 그룹을 같이 묶는 다던가, 그룹의 각 원소에 규칙을 적용하는 등의 동작을 처리할 수 있다.
  • 정의 - Collection을 Wrapping하면서, 그 외 다른 멤버 변수가 없는 상태
  • 장점
    - [비즈니스에 종속적인 자료구조] - 검증 조건이 필요한 모든 곳에 검증을 해왔던 기존 방식 - 해당 조건으로만 생성할 수 있는 자료구조를 만들자!

    - [Collection의 불변성을 보장] - 불변의 중요성: 절대 값이 바뀔 일이 없다 == 사이드 이펙트 최소화 - final은 재할당만 금지 - 컬렉션의 값을 변경할 수 있는 메소드가 없다면? -> 불변 컬렉션 - List라는 컬렉션에 접근할 수 있는 방법이 없기 때문에 값 변경/추가가 안된다!

    - [상태/행위를 한 곳에서 관리] - 값과 로직이 함께 존재 - ex) 컬렉션과 계산 로직이 관계가 있는데, 코드로 표현이 안 될 수 있다. - 똑같은 기능을 하는 메서드 중복 생성 가능 - 계산 메서드 누락할 수도

    - [이름이 있는 컬렉션] - 그냥 List 자료구조 였다면... - 검색이 어렵다 - 변수명으로만 검색 가능 - 변수명은 개발자마다 다르게 지음 - 명확한 표현이 불가능 - 변수명이기에 의미를 부여하기 어렵다 - 일급 컬렉션을 통해 컬렉션 기반으로 용어사용검색 지원한다

BigDecimal

  • 필요성 - double형 변수에 대해 연산, 자릿수 조정, 반올림, 비교, 형 변환, 해싱 등에 대해 연산 제공 - 아주 큰/아주 작은 부동 소숫점 연산을 아주 정확하게 수행할 수 있다. (시간을 조금 희생한다) - Java에서 숫자를 정밀하게 저장하고 표현하는 유일한 방법 - 돈/소수점을 다룬다면 필수! - 내부적으로 수를 십진수로 저장하여, 거의 무한한 정밀도를 보장!
  • 구조 - 정수x10^(-scale) 로 표현
  • 용량 - double: 8 bytes - Double: 16 bytes (8 bytes overhead for the class, 8 bytes for the double) - BigDecimal: 32 bytes
  • 성능 - Java5 기준, 백만번의 연산을 수행 시, BigDecimal 연산이 double에 비해 3~4배 느림
  • 기본 용어 - precision: 숫자를 구성하는 전체 자릿 수 (0 제외) - ex) 12345 --precision--> 5 - ex) 012345.67890 --precision--> 9 - scale: 전체 소수점 자리수, BigDecimal은 32bit의 소수점 크기를 가진다 - ex) 012345.67890 --scale--> 4 - ex) 0.00 --scale--> 1 - Decimal128: 34자리까지 표현 가능한 10진수를 저장 - 기본형 변환 메서드: Number로 부터 상속받음 - int intValue(); - long longValue(); - float floatValue(); - double doubleValue(); - 변환한 결과가 변환한 타입의 범위에 속하지 않는다면: ArithmeticException 발생 - byte byteValueExact(); - short shortValueExact(); - int intValueExact(); - long longValueExact(); - BigInteger toVigIntegerExact();
  • 초기화 - new BigDecimal("0.001"); - String을 넘겨줘서 초기화를 해주자! - double 타입을 넘겨준다면 이진수의 근사치를 넘겨줘 예상과 다른 값이 나온다!

정적 팩토리 메서드

  • 정의 - 객체 생성의 역할을 하는 클래스 메서드 - 객체 생성을 캡슐화하는 기법 - 객체 생성하는 메서드를 만들고, static으로 선언하는 것
  • 장점 - [이름이 있어, 가독성이 좋아진다] - ex) Character warrior = Character.newWarrior();

    - [호출할 때마다 새로운 객체를 생성하지 않아도 된다] - 불변 객체를 캐시해서 사용중이라면, new 연산을 사용 안해도 된다. - private으로 생성자를 설정하면, 정해진 범위를 벗어나는 로또 번호의 생성을 막을 수 있다. - ex)

    public static BigInteger valueOf(long val) {
        if (val == 0) 
            return ZERO; // 미리 만들어 둔 객체
    }     
    

    - [하위 자료형 객체를 반환할 수 있다] - 생성자와 다르게 정적 팩토리 메서드는 리턴할 객체를 정할 수 있다 - 분기처리를 통해 하위 타입의 객체를 반환할 수 있다. - ex)

    public static Discount createDiscountItem(String discountCode) {
        if (isUsableCoupon(discountCode)) {
            return new Coupon(1000);
        } 
        if (isUsablePoint(discountCode)) {
            return new Point(500);
        }
    }
    

    - [객체 생성을 캡슐화 할 수 있다] - DTO와 Entity간에 자유롭게 형 변환이 가능하도록 변경 가능하다. - 내부 구현을 모르더라도 쉽게 변환할 수 있다. - ex) CarDto carDto = CarDto.from(car);

  • 단점 - 하위 클래스를 못 만든다 - 다른 정적 메서드와 잘 구분되지 않는다
  • 흔히 사용되는 이름 - from: 하나의 매개변수를 받아, 객체를 생성 - of: 여러개의 매개변수를 받아, 객체를 생성 - getInstance: 인스턴스를 생성 - newInstance: 새로운 인스턴스를 생성

stream()

  • 2.1 스트림이란? - [정의] - 데이터 소스를 추상화하고, 데이터를 다루는데 자주 사용되는 메서드들을 정의 - 데이터 소스가 무엇이던 같은 방식으로 다루며, 코드의 재사용성을 높인다

    - [스트림은 작업을 내부 반복으로 처리한다] - 반복문을 메서드의 내부에 숨길 수 있다는 것 - 조건문도 메서드 내부에 숨길 수 있다

    - [스트림의 연산] - 중간 연산은 연산결과를 스트림으로 반환, 연속해서 연결 가능 - 최종 연산은 스트림의 요소를 소모하면서 연산을 수행, 단 한번의 연산

    - [지연된 연산] - 최종 연산이 수행되어야 비로소 스트림의 요소들이 중간 연산 거쳐 최종 연산에서 소모

    - [Stream 와 IntStream] - 오토박싱&언박싱으로 인한 비효율을 줄이기 위해 데이터 소스의 요소를 기본형(int, long, double)으로 다루는 스트림 제공 - IntStream, LongStream, DoubleStream

    - [병렬 스트림] - 병렬 처리에 용이함 - .parallel() 메서드를 호출하도록 하자

  • 2.2 스트림 만들기 - [컬렉션] - 컬렉션의 최고 조상인 Collection에 Stream()이 정의되어 있음 - Collection의 자손인 List와 Set에서 사용 가능

    - [배열] - Stream과 Arrays에 static 메서드로 정의되어 있음 - ex1) Stream.of("a", "b", "c"); - ex2) Stream.of(new String[]{"a", "b", "c"}); - ex3) Arrays.stream(new String[]{"a", "b", "c"});

    - [특정 범위의 정수] - IntStream과 LongStream은 지정된 범위의 연속된 정수를 스트림으로 생성하는 range(), rangeClosed() 지원 - IntStream.range(int begin, int end); - IntStream.rangeClosed(int begin, int end);

    - [임의의 수] - 난수 생성하는 Random 클래스에서 다음과 같은 인스턴스 메서드 지원 - IntStream ints() - LongStream longs() - DoubleStream doubles() - 해당 메서드들은 무한 스트림이라, 스트림 크기를 제한해야 한다. - 매개변수로 스트림의 크기를 지정할 수 있다. - 지정된 범위의 난수를 발생시킬 수 있다.

    IntStream intStream = new Random().ints();
    intStream.limit(5).forEach(System.out::println);
    
    IntStream intStream = new Random().ints(5); //크기가 5인 난수 스트림
    
    IntStream intStream = new Random().ints(6, 1, 46); //크기가 6인 1~45 사이의 난수 스트림
    

    - [람다식 - iterate(), generate()]
    - iterate() - seed로 지정된 값부터, 람다식 f에 의해 계산된 결과를 다시 seed값으로 해서 계산을 반복 - ex) Stream evenStream = Stream.iterate(0, n -> n+2); //0, 2, 4, 6... - generate() - 람다식에 의해 계산되는 값을 요소로 하는 무한 스트림이지만, 이전 결과 이용해 계산은 X - ex) Stream oneStream = Stream.generate(() -> 1); //1, 1, 1, 1...

    - [빈 스트림] - 요소가 하나도 없는 비어있는 스트림 생성 가능 - 연산 수행 후 결과가 하나도 없다면, null이 아닌 빈 스트림 반환하자

        Stream emptyStream = Stream.empty();
        long count = emptyStream.count(); // count 값은 0
    

    - [두 스트림의 연결] - 스트림의 static 메서드인 concat() 사용하면, 두 스트림을 하나로 연결 가능 - 두 스트림의 요소는 같은 타입일 것

  • 2.3 스르팀의 중간연산 - [스트림 자르기 - skip(), limit()] - skip(3): 3개의 요소 건너뛰기 - limit(5): 스트림 요소 5개로 제한
        IntStream intStream = IntStream.rangeClosed(1, 10); // 1~10 스트림
        intStream.skip(3).limit(5).forEach(System.out::print); // 45678
    

    - [스트림의 요소 걸러내기 - filter(), distinct()] - distinct(): 중복된 요소 제거 - filter(): 주어진 조건에 맞는 요소만 남김

        IntStream intStream = IntStream.of(1, 2, 2, 3, 3, 3, 4, 5, 5, 6);
        intStream.distinct().forEach(System.out::print): // 123456
    
        IntStream intStream = IntStream.rangeClosed(1, 10); // 1~10
        intStream.filter(i -> i%2 == 0).forEach(System.out::print); // 246810
    

    - [정렬 - sorted()] - sorted(): 정렬할 때 사용 - comparator를 구현하여 적용할 수도 있음. 추후 학습 예정

        Stream<String> strStream = Stream.of("dd", "aaa", "CC", "cc", "b");
        strStream.sorted().forEach(System.out::print); // CCaaabccdd
    

    - [변환 - map()] - map(): 스트림의 요소에 저장된 값 중 원하는 필드 뽑아내거나, 특정 형태로 변환할 때 사용

        Stream<File> fileStream = Stream.of(new File("Ex1.java"), new File("Ex2.java"));
        Stream<String> fileNameStream = fileStream.map(File::getName);
        fileNameStream.forEach(System.out::println); // Ex1.java Ex2.java
    

    - [조회 - peek()] - peek(): 연산과 연산 사이에 올바르게 처리가 되었는지 확인

    - [mapToInt(), mapToLong(), mapToDouble()] - Stream 타입의 스트림을 기본형 스트림으로 변환할 때 사용

        Stream<Integer> studentScoreStream = StudentStream.map(Student::getTotalScore);
        >> VS
        IntStream studentScoreStream = StudentStream.mapToInt(Student::getTotalScore);
        int allTotalScore = studentScoreStream.sum(); 
    

      - 기본형 스트림은 숫자를 다루는 메서드들이 지원됨
          - sum(): 스트림 요소의 총합
          - average(): sum() / (double)count()
          - max(): 스트림 요소 중 제일 큰 값
          - min(): 스트림 요소 중 제일 작은 값
    
  • 2.4 Optional와 OptionalInt - 정의 - 최종 연산의 결과 타입이 Optional인 경우가 있다 - Optional은 지네릭 클래스로 'T타입의 객체'를 감싸는 래퍼 클래스 - value를 가리키는 참조형을 반환 - null을 가리켜도 반환받은 것은 참조값이라 Exception이 안 뜬다!
    - 반환된 결과가 null인지 매번 if문으로 확인하지 않고, 정의된 메서드를 사용한다 - if문 없이도 NullPointerException 발생하지 않는 간결하고, 안전한 코드 작성 가능

    - [Optional 객체 생성하기] - of(): Optional 객체 생성 - ofNullable(): 참조 변수의 값이 null일 가능성이 있다면 사용

    - [Optional 객체의 값 가져오기] - get(): 저장된 값 가져오기, null이면 예외 발생 - orElse(""): 저장된 값이 null이면, ""를 반환 - orElseGet(() -> new String()): 저장된 값이 null이면, new String()을 반환 - orElseThrow(NullPointerException::new): null이면 예외 발생

    - [Optional 객체에 사용되는 filter(), map()] - Optional 객체의 값이 null 이면, 아무 일 하지 않음

    int result = Optional.of("123")
                    .filter(x->x.length()>0)
                    .map(Integer::parseInt).orElse(-1); // result = 123
    
    int result = Optional.of("")
                    .filter(x->x.length()>0)
                    .map(Integer::parseInt).orElse(-1); // result = -1
    

    - [OptionalInt, OptionalLong, OptionalDouble] - 기본형을 값으로 하는 Optional

  • 2.5 스트림의 최종연산 - 특징 - 스트림의 요소를 소모해 결과를 만들어냄 > 최종 연산 이후의 스트림은 사용할 수 없다!!
      - 결과는 스트림 요소의 합과 같은 단일 값, 스트림의 요소가 담긴 배열, 컬렉션 등
    

    - [forEach()] - 반환 타입이 void, 하나씩 수행

    - [조건 검사 - allMatch(), anyMatch(), nonMatch(), findFirst(), findAny()] - 스트림의 요소에 대해 지정된 조건에 "모두 일치" / "일부 일치" / "하나도 안 일치" 검증 - 매개변수 Predicate, 연산결과 boolean 반환 - 스트림의 요소 중 조건에 맞는 첫 번째 것 반환하는 findFirst(), findAny() //병렬 스트림일시 사용 - 주로 .filter()와 함께 쓰인다

    boolean noFailed = stuStream.anyMatch(s->s.getTotalScore() <= 100); 
    Optional<Student> stu = stuStream.filter(s->s.getTotalScore()<=100).findFirst();
    

    - [통계 - count(), sum(), average(), max(), min()] - 기본형 스트림이 아닐 경우, count() / min() / max() 만 가능

    - [리듀싱 - reduce()] - 스트림의 요소를 줄여나가면서 연산을 수행, 최종 결과를 반환 - sum()의 구현은 reduce()로 되어있다

    IntStream intStream = IntStream.range(1, 5);
    int sum1 = intStream.reduce(0, (a, b) -> a + b);
    System.out.println("sum1 = " + sum1); // sum1 = 10
    

  • 2.6 collect() - 정의 - 스트림의 최종 연산 중 하나 - reduce()와 유사 - Collector 인터페이스를 구현한 것 - Collectors 클래스는 다양한 static 메서드를 통해 기능 제공

    - [스트림을 컬렉션과 배열로 변환 - toList(), toSet(), toMap(), toCollection(), toArray()] - 스트림의 모든 요소를 컬렉션에 수집하기 - List, Set, Map이 아닌 특정 컬렉션을 지정하려면, toCollection()에 해당 컬렉션의 생성자 참조를 매개변수로 넣자

    List<String> personName = persons.stream()
            .map(person -> person.getName())
            .collect(Collectors.toList());
    
    Map<Integer, String> personInfo = persons.stream()
            .collect(Collectors.toMap(person -> person.getId(), person -> person.getName()));
    

    - [통계 - counting(), summingInt(), averagingInt(), maxBy(), minBy()] - 최종 연산으로도 제공되지만, collect()를 사용하여 얻을 수도 있다. - 이는 collect()는 그룹을 나눠서 계산이 가능하기 때문

    long count = persons.stream().collect(counting());
    int count2 = persons.stream().collect(summingInt(Person::getId));
    

    - [리듀싱 - reducing()] - 최종 연산으로 제공되는 reduce()를 collect()를 통해 얻을 수 있다.

    - [문자열 결합 - joining()] - 문자열 스트림의 요소를 하나의 문자열로 연결하여 반환한다

    String allName = persons.stream().map(Person::getName).collect(joining());
    

    - [그룹화와 분할 - groupingBy(), partitioningBy()] - groupingBy(): 스트림을 n분할 하기 - partitioningBy(): 스트림을 2분할 하기

    Map<Boolean, List<Person>> personBySex = persons.stream()
            .collect(partitioningBy(Person::isMale));
    List<Person> malePerson = personBySex.get(true);
    List<Person> femalePerson = personBySex.get(false);
    

Git Commands

Git 브랜치 전략