Java/JavaCafe Study

4주차 컬렉션 API

향찡 2021. 6. 27. 17:38

컬렉션 API
- 컬렉션 프레임워크
- List
- Map
- Set

발표자 : 박세종

발표 내용 + 부연설명 

🔍 왜 컬렉션 프레임워크를 사용할까?

컬렉션 프레임워크란

  • 여러 개의 데이터를 저장 및 표현이 가능한 클래스들의 집합
  • 표준화된 인터페이스를 제공
  • 각 구현체들이 어떻게 구성되어있는지에 신경 쓸 필요 없음
  • 필요(성능)에 따라서 구현체를 선택가능

컬렉션 프레임워크 대표 3가지

  • List<E>
    • 순서대로 데이터를 저장할 수 있어야 한다. (중복 가능)
    • 일반 적으로 가장 많이 사용
  • Set<E>
    • 데이터를 중복 없이 저장할 수 있어야 한다.
    • 유일한 데이터를 담을 수 있다.
  • Map<K, V>
    • 키와 값을 한쌍으로 데이터를 저장하는 경우
    • 키의 경우 중복을 허용하지 않는다.

👆 List<E> : 순서대로 데이터가 들어가야 한다. (중복 가능)

  • get : 특정 인덱스의 데이터를 반환한다.
  • add : 데이터를 추가한다. (인덱스 지정 가능)
  • remove : 데이터를 삭제한다. (인덱스 혹은 데이터 지정가능)
  • size: 아이템 개수를 반환
  • contains: 특정 데이터가 존재하는지 여부 반환

 Set<E> : 데이터를 중복 없이 저장할 수 있어야 한다.

  • add : 데이터를 추가한다.
  • remove : 데이터를 삭제한다.
  • size: 아이템 개수를 반환
  • contains: 특정 요소가 존재하는지 여부 반환

👌 Map<K, V> : 키와 값을 한쌍으로 데이터를 저장하는 경우

  • put : 키와 값 추가한다.
  • remove : 키를 기준으로 값을 삭제한다.
  • size: 데이터 개수를 반환
  • containsKey: 특정키가 존재하는지 여부 반환
  • containsValue: 특정 데이터가 존재하는지 여부 반환

실습 문제 1번

정수 배열에서 중복 값을 제거 후 오름차순으로 정렬하여 출력하시오

[5,2,7,1,2] 인 경우 [1,2,5,7]로 출력

실습 문제 2번

정수 값이 저장된 리스트가 있다.

각 정수들이 몇 개가 존재하는지를 찾고 출력해보자 (Map을 이용해보자)

ex) [1,2,1,4,2,1] :: 1=>3개, 2=>2개 4=>1개

풀이 1

public class CollectionExercise01 {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>(asList(5, 2, 7, 1, 2));
    
        // 중복제거
        Set<Integer> eraseSet = new HashSet<>(numbers);
        ArrayList<Integer> newNembers = new ArrayList<>(eraseSet);
        
        // 정렬
        Collections.sort(newNembers);

        System.out.println(newNembers);
    }
}

풀이 1-1

public class CollectionExercise01 {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>(asList(5, 2, 7, 1, 2));

        List<Integer> collect = numbers.stream()
                .distinct()
                .sorted()
                .collect(Collectors.toList());

        System.out.println(collect);
    }
}

풀이 2

public class CollectionExercise02 {

    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>(asList(1, 2, 1, 4, 2, 1));

        Map<Integer, Integer> resultMap = new HashMap<>();
        for(int number : numbers) {
            if(!resultMap.containsKey(number)) {
                resultMap.put(number, 0); // 없으면 초기값 0
            }

            resultMap.put(number, (resultMap.get(number) + 1));
        }

        System.out.println(resultMap);
    }
}

풀이 2-1

public class CollectionExercise02 {

    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>(asList(1, 2, 1, 4, 2, 1));
        
        // 출력 1
        System.out.println(
                numbers.stream().collect(Collectors.groupingBy(key -> key, Collectors.counting())));
        // 출력 2
        System.out.println(
                numbers.stream().collect(Collectors.groupingBy(key -> key, Collectors.toList())));

    }
}

출력 결과

풀이 2-2 (스터디원 풀이)

getOrDefault() - 값이 있으면 데이터를 넣고 없으면 default값을 넣는 함수

public class CollectionExcercise02_1 {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>(asList(1, 2, 1, 4, 2, 1));

        Map<Integer, Integer> map = new HashMap();

        for(int number : numbers) {
            map.put(number, map.getOrDefault(number, 0)+ 1);
        }

        System.out.println(map);
    }
}

List(리스트) 생성 방법 3가지

public static void main(String[] args) {

        // java 8 이하
        List<String> list1 = Arrays.asList("Java", "Kotlin", "C++");
        
        // java 9 이상
        List<String> list2 = List.of("Java", "Kotlin", "C++");

        // stream 이용
        List<String> list3 = Stream.of("AAA", "BBB", "CCC").collect(toList());

        System.out.println(list1);
        System.out.println(list2);
        System.out.println(list3);
    }

Set 생성 방법 3가지

// 자바 8 이하
HashSet set1 = new HashSet(Arrays.asList("JAVA", "KOTLIN", "C++"));

// 자바 9 이상
Set<String> set2 = Set.of("JAVA", "KOTLIN", "C++");

// stream 이용
Set<String> set3 = Stream.of("JAVA", "KOTLIN").collect(Collectors.toSet());

System.out.println(set1);
System.out.println(set2);
System.out.println(set3);

Map.of

 Map<Integer, String> map1 = Map.of(1, "Aaa", 2, "Bbb");
 
 System.out.println(map1);
  • List<E> : 순서대로 데이터를 저장할 수 있어야 한다. (중복가능)
    • ArrayList, LinkedList,
  • Set<E> : 데이터를 중복없이 저장할 수 있어야 한다. (순서 보장 x)
    • HashSet, TreeSet
  • Map<K, V> : 키와 데이터를 한쌍으로 데이터를 저장하여야 한다.
    • HashMap, LinkedHashMap, TreeMap

ArrayList

  • 가장 많이 사용되는 List의 구현체
  • 배열을 기반으로 데이터를 저장
  • 장점 : 순차적인 데이터 추가/삭제/조회 빠름
  • 단점 : 중간의 데이터 추가/삭제는 배열 복사가 필요(속도가 느림)

LinkedList

  • 각 노드들은 다음 노드가 무엇인지를 저장
  • 노드들의 링크를 통해서 리스트를 구성
  • 장점 : 저장/삭제가 빈번한 경우 유리(빠름)
  • 단점 : 특정 위치의 값을 가져오는 작업에선 불리함(처음부터 순차적으로 찾아야 함)

equals & hashcode

  • 객체의 동일함을 비교할 수 있는 메서드
  • Object에 기본적으로 설정되어있지만..
  • 객체 자체의 값을 기준으로 동일함을 제대로 알려주기 위해서는
  • equals와 hashcode를 오버 라이딩하여야 하여야 함
  • 왜 두 개일까??
    • hashcode > 결과 값이 int로 반환되어 단순. 하지만 충돌 가능성을 내포..
    • equals > 정확하게 비교 가능(정말 같은지 비교)

equals와 hashcode는 IDE를 통해 자동완성할 수 있지만 클래스 변수가 새로 추가되면 다시 만들어야 한다.

자바 15에서는 record를 통해서 좀 더 편하게 할 수 있으며, 롬복을 이용해서 @EqualsAndHashCode를 사용하면 편리하다.

HashMap

  • key / value 형태로 데이터를 저장
  • 키를 해싱하여 저장
  • 키를 이용, 데이터에 빠르게 접근 가능
  • 데이터가 계속 추가되면.. 느려요
    • 초기 사이즈 지정으로 성능 이슈 줄일 수 있음
  • 키에 대한 순서 자체에 대한 보장이 되지 않는다.

LinkedHashMap

  • 해시맵과 기본적으로 동일
  • 엔트리들끼리 링크로 연결
  • 키를 순서대로 저장하고 가져오는데 유리 (키에 대한 순서를 보장 한다.)

HashSet

  • HashMap을 내부적으로 이용
    • HashMap<E, ?>
  • 원하는 요소에 빠르게 접근가능
  • 만약 정렬이 필요하다면..
    • TreeSet을 이용하자

이외에도..

  • 동시성이 필요한 환경에서
    • ConcurrentHashMap 
    • CopyOnWriteArrayList
    • 동시성 이란? 하나의 객체에 여러 개의 스레드가 한번에 접근하는 경우를 생각
    • 톰캣은 200개 스레드가 돌고 있다.
    • 극단적인 환경인 경우 동시성 보장이 필요한 경우에 사용한다.
  • Queue
    • 입력한 순서대로 데이터 꺼내야 하는 경우
  • Stack
    • 마지막에 저장한 데이터를 가장 먼저 꺼내야 하는 경우

null보다는 empty를 이용하자..

  • 컬렉션의 경우 값이 존재하지 않는다를 두 가지로 표현 가능.
  • null or empty
  • 비어있다를 emptyList로 표현해주면 사용하는 좀 더 편하게 사용 가능
  • Collections.emptyList / Collections.emptyMap / Collections.emptySet을 사용하거나 
  • List<String> list = new ArrayList(); 처럼 비워 놓자

웬만해서는 불변으로 만들자

  • A객체가 List를 만들고, B객체에게 넘겨줌
  • B는 데이터를 일부 삭제함
  • A는 삭제된 데이터 때문에 버그 발생
  • 왠만해서는 변경 불가능하게 막자.
  • kotlin으로??
  • 알 수 없는 버그는 원천 차단해 버리자.

final은 할당 후 변경이 불가능한 것이다. 안에 있는 객체까지 변경되는 것을 막지는 못한다.

예를 들면 private final List<String> products;

해결 : Collections.unmodifiableList(products);를 사용하자

조금 더 고급지게 사용하기(컬렉션을 더 쉽게 다룰 수 있는 도구들)

  • 하위 호환 등의 이유로 확장이 쉽지 않음
    • defaultMethod로 조금씩 확장 중
  • java8 stream API
    • 8주 차를 기대해주세요
  • guava collection
    • BiMap(키밸류 가 아닌, 양쪽 다 키로 사용하는 겨우)
    • MultiSet, MultiMap
  • eclipse collection
    • 이클립스 재단에서 만든 컬렉션
    • 자바에서 있는 컬렉션보다 빠르다.