데브코스 백엔드 3기 4일차


Collection

추상체, 여러데이터의 묶음(덩어리)

  • Collection의 구상체 : List -> List의 구상체 ArrayList, LinkedList, Vector, Stack
  • Collection을 다루는 방법
    • Iterator
    • Stream


MyCollection.java 에서 foreach(), map(), filter() 구현해보기

public class MyCollection<T> {
  private List<T> list;

  public MyCollection(List<T> list) {
      this.list = list;
  }


  • forEach()
    • Consumer의 accept() : 인자는 존재, 반환값은 존재안함
public void foreach(Consumer<T> consumer) {
  for (int i = 0; i < list.size(); i++) {
      T data = list.get(i);
      consumer.accept(data);
  }
}


  • map() : 데이터를 변경한다 = 이쪽 데이터에서 저쪽 테이터로 매핑한다
    • <U> : 이 메서드 안에서만 유효한 제너릭 타입이라고 명시
    • list들을 function을 통해 U타입으로 바꾼다음에 U타입들만 모아진 MyCollection return
public <U> MyCollection<U> map(Function<T, U> function) {
  List<U> newList = new ArrayList<>();
  foreach(d -> newList.add(function.apply(d)));
  return new MyCollection<>(newList);
}
  • map() 사용
public class Main {
  public static void main(String[] args) {
      MyCollection<String> c1 = new MyCollection<>(Arrays.asList("A", "BC", "DEF"));
      MyCollection<Integer> c2 = c1.map(String::length);
      c2.foreach(System.out::println);
  }
}

or

new MyCollection<>(Arrays.asList("A", "BC", "DEF"))
        .map(String::length)
        .filter(i -> i % 2 == 0)
        .foreach(System.out::println);
  1. .map() 하고나면 String type 받아서 그 String의 length를 Integer로
  2. .filter()로 그 Integer 받아서 짝수면
  3. foreach()로 출력


  • filter()
    • Predicate의 test() : 인자를 받고 boolean return
public MyCollection<T> filter(Predicate<T> predicate) {
  List<T> newList = new ArrayList<>();
  foreach(d -> {
      if(predicate.test(d)) newList.add(d);
  });
  return new MyCollection<>(newList);
}



Iterator

데이터를 하나씩 떨어트려놓음
= Iterator라는 방식으로 여러 데이터의 묶음을 풀어서 하나씩 처리할 수 있는 수단 제공

  • next() 통해서 다음데이터 조회가능 (but, 이전 데이터 조회 불가)
    • 끝났는데 계속 next() -> exception
      => 데이터 있는지 확인필요 : if(iter.hasNext()) iter.next()


public interface MyIterator<T> {
  boolean hasNext();
  T next();
}
public class Main {
  public static void main(String[] args) {
  
    MyIterator<String> iter2 =
            new MyCollection<String>(Arrays.asList("A", "BC", "DEF", "GHIJ"))
            .iterator();

    while(iter.hasNext()) {
        System.out.println(iter.next());
    }

  }
}


  1. Iterator로는 전체 묶음 중에 위치 알 수 없음
  2. next()로 주어지는 현재데이터만 가지고 활용함
  3. Collection에서 쓰는 고차함수(forEach, map, filter)를 써서 데이터를 처리할 수는 없을까?

=> Stream


참고) 고차함수
함수형인터페이스를 사용해서 함수를 인자로 받는 함수



Stream

끊어지지 않는 연속되는 데이터들의 흐름
Stream도 데이터의 연속상에서 제공되는 데이터 하나만 취급
그러나 한건씩 처리하면서 고차함수 사용 가능


  • 이미 우리가 쓰던 Stream
    • InputStream in = System.in;
    • OutputStream out = System.out;
  • 자바 8부터 제공하는 Collections.stream()은 조금 다름
    • Collection안의 데이터를 연속된 흐름으로 취급가능 + 고차함수 사용 가능 + 간결하게 표현가능


Stream() + 고차함수 사용

public class Main {
  public static void main(String[] args) {
    Arrays.asList("A", "BC", "DEF", "GHIJ")
            .stream()
            .map(String::length)
            .filter(i -> i % 2 == 1)
            .forEach(System.out::println);
  }
}


  • List의 stream()
Stream<Integer> stream = Arrays.asList(1,2,3).stream();
  • Arrays의 stream()
    • primitive type은 IntStream type으로 반환
    • boxed() 이용해서 wrapper
    • collect(Collectors.toList()) 이용해서 List로 반환
    • toArray()
      • Object[] return (primitive type array로 받을 때는 그냥 받아도 됨)
      • toArray(Integer[]::new); : Integer[] return
IntStream intStream = Arrays.stream(new int[]{1,2,3});
Stream<Integer> stream2 = Arrays.stream(new int[]{1,2,3}).boxed();

List<Integer> list = Arrays.stream(new int[]{1,2,3}).boxed().collect(Collectors.toList());

Arrays.stream(new int[]{1,2,3}).boxed().toArray();  //Object[]
Arrays.stream(new int[]{1,2,3}).boxed().toArray(Integer[]::new);  //Integer[]


  • Stream 만드는 방법
    • .generate()
    • .iterate() : 초기값 필요 -> 다음에 어떤 결과값 만들어 낼건지
Stream.generate(() -> 1)
    .limit(10)
    .forEach(System.out::println);

Stream.iterate(0, (i) -> i + 1)
    .forEach(System.out::println);



Optional

  • NPE(Null Pointer Exception)
    • 자바에서는 거의 모든것이 레퍼런스 = 거의 모든것이 null이 될 수 있음
      => 항상 null 확인할 필요 있음 => 이제부터 null 쓰지마! 서로 약속! = 계약하고 프로그래밍한다


null 안쓰는 법

  1. Empty 객체 사용
  2. Optional : null이 될 수 있는 값들을 운반할 수 있는 캐리어
    1) Optional : User라는 객체를 래핑해서 운반해주는 운반체 운반체 안에 들어갈 수 있는 내용은 유저고 실제고 값이 있을수도 없을수도 있음 만약 실제 User가 없으면 null return이 아닌 null이 담긴 Optional 객체를 반환


Optional 사용

  • null인 데이터 표시 : Optional.empty()
    • Optional<User> optionalUser = Optional.empty();
  • 데이터 넣어주기 : Optional.of({DATA})
    • optionalUser = Optional.of(new User(1, "taehee"))
  • 데이터 있는지
optionalUser.isPresent();
  
optionalUser.ifPresent(user -> {
    //do 1
});


=> Optional type만으로 null일수도 있다는 걸 알 수 있음



[세션] 진유림님 Github 강의

  • 협업 = Git(분산 버전관리) + Github(클라우드 저장소)
    • Git : 원하는 시점마다 깃발꽂고(= 버전 만들고) 그 사이를 자유롭게 돌아다님


  • branch : 하나의 원본저장소에서 분기 나눔
    • 다수의 사용자가 다수의 브랜치 -> 관리힘듬
    • 대부분의 회사가 사용하는 방식(토스도 이 방식 사용!)
  • fork : 여러 원격저장소 만들어 분기나눔
    • 원본저장소 이력을 보려면 따로 주소 추가해주어야 함


PR(Pull Request) = Merge Request

  • feat/기능이름
  • fix/버그이름
  • hotfix/급한버그

=> 직접 커밋은 여기서만..!
  이후부터는 merge만

-> dev(or main) -> release(or latest) -> 실서버에 배포


낯선 기능들

  • amend : 커밋메세지만 수정
  • stash : 변경사항 킵해두고 커밋은 아직 안하고 싶을 때
    = 서랍에 넣어두기 + 메세지남기기 + 브랜치 왔다갔다
    • 브랜치 이동시에는 클린한 상태로(commit or stash해서)
    • tracked 인 것만 들어감
    • apply stash 후 stash 목록에서 지우기
  • reset : 옛날 커밋으로 돌리기
    • soft
    • mixed : 과거로 돌아감 + 기억은 남김(working directory에 uncommited changes에)
    • hard : 과거로 돌아감 + 아무것도 안남음
  • revert : 이 커밋의 변경사항을 되돌리고 싶어! = 되돌리는 커밋을 만드는 것
  • cherry-pick : 커밋 하나만 떼어서 지금 브랜치에 붙이는
    • bug fix시 많이 사용



Stream 사용예제

1. 주사위를 100번 던져서 6이 나오는 횟수

  • limit(숫자) : 숫자만큼 데이터를 가져와 새로운 스트림 생성해 반환
Random r = new Random();
long count = Stream.generate(() -> r.nextInt(6) + 1)
        .limit(100)
        .filter(n -> n == 6)
        .count();

System.out.println(count);  


2. 1-9 사이 값 중에서 겹치지 않게 3개를 출력

  • distinct() : 중복제거
Random r2 = new Random();
int[] arr = Stream.generate(() -> r.nextInt(9) + 1)
        .distinct()
        .limit(3)
        .mapToInt(i -> i)
        .toArray();  //primitive type 인 경우에는 그냥 나옴 toArray(Integer[]::new); 안해줘도됨

System.out.println(Arrays.toString(arr));


3. 0-200 사이 값 중에서 랜덤값 5개를 뽑아 큰 순서대로 출력

  • sorted() : 작은순서대로
    • sorted(Comparator.reverseOrder()) : 큰순서대로
Random r3 = new Random();
int[] arr2 = Stream.generate(() -> r.nextInt(200))
        .limit(5)
        .sorted(Comparator.reverseOrder())
        .mapToInt(i -> i)
        .toArray();

System.out.println(Arrays.toString(arr));



일기(회고)

  • 20일 TIL을 12시가 넘은 21일 새벽 2시에 쓰는중 ㅎㅎ
    3시간 세션듣고(핑계대기) 오늘 많이 나태했고 와중에 컬렉션 내용은 너무 어렵고
    그래도 깃헙 새로운 기능을 파악해서 뿌듯했다 나도 토스 가고싶다..!


  • Iterator, Stream 너무너무 낯설어서 난 여태까지 무슨 자바공부를 했다는걸까 싶었던 어제오늘이었다
    곰튀김님의 설명은 물흐르듯이 흘러가서 너무 신기하다
    듣다보면 이래서 이렇게 되는구나! 한다 내공!
    많이 써봐야 익숙해진다는 말 알아도 또 마음에 새기기


  • 계산기 과제
    fork한 repo에는 push해도 잔디가 안심기길래 다른방법을 이것저것 해보려다가 결국 원래 repo를 날렸다 ㅎㅎ
    오늘 이거때문에도 시간을 많이 날림 Git 어렵워,,
    내일부턴 설계 강의 듣고 설계 시작해봐야지


  • 오늘의 귀여움
    • 바텐더 나! 한잔에 삼만원

45


업데이트: