DevLogs/Article

[번역] Java Collections 튜토리얼

밥먹고해요 2019. 3. 10. 16:35

원문 출처

https://www.vogella.com/tutorials/JavaCollections/article.html

Lars Vogel (c) 2008, 2016 vogella GmbH Version 2.8, 29.09.2016


본 튜토리얼은 자바8에서의 List, ArrayList, Map과 같은 자바 컬렉션 사용법에 대해 설명합니다.


1. 자바 컬렉션

1.1. 컬렉션(Collection)이란?

자바는 객체를 배열로 저장할 수 있는데, 배열은 초기화 시점에 미리 정의된 크기로 초기화됩니다. 자바의 코어 라이브러리는 컬렉션 프레임워크(Collection Framework)라는 보다 융통성 있게 데이터를 담아서 처리하기 위한 자료구조를 제공합니다. 컬렉션에 저장된 데이터는 캡슐화되며, 미리 정의된 메서드로만 데이터에 접근이 가능토록 합니다. 예를 들면, 개발자는 메서드를 통해서만 요소를 추가할 수 있습니다.

컬렉션은 내부 저장소로 배열을 사용하지만, 동적으로 배열 크기를 관리해야 하는 복잡한 부분은 개발자가 신경쓸 필요가 없습니다. 예를 들어, 애플리케이션에 여러 개의 People 타입의 객체를 저장하려면, 그냥 컬렉션에 저장하면 됩니다.

1.2. 주요 기본 구현체들

대표적인 컬렉션에는 다음과 같은 것들이 있습니다.

  • Stack
  • Queue
  • Deque
  • List
  • Tree
자바는 기본적으로 List와 같은 인터페이스를 구현한 하나 이상의 구현체를 제공합니다. 예를들어, ArrayList와 LinkedList 클래스는 List 인터페이스의 구현체입니다.

1.3. 제네릭(generics)을 이용한 타입 설정

다양한 매개변수 타입의 정의를 가진 클래스나 인터페이스를, '제네릭 클래스' 또는 '제네릭 인터페이스'라고 합니다. 예를 들어, List를 E타입의 매개변수를 받게 하려면 List<E>로 정의합니다.
자바 컬렉션은 타입 정의를 매개변수로 받아야 합니다. 이는 컬렉션을 올바른 타입의 객체로 사용하는지를 자바 컴파일러가 검증할 수 있게 해주며, 다양한 종류의 객체를 조작하는 타입 혹은 메소드에게 컴파일 시점(complie-time)의 타입 안전성을 제공해 줍니다.
제네릭이 도입되기 이전에는, 컬렉션에서 읽어들인 모든 객체를 직접 캐스팅해야 했으며, 컬렉션에 잘못된 타입의 객체가 삽입되기라도 하면 런타임 예외(exception)와 마주해야만 했습니다.

1.4. 컬렉션과 람다(lambda)

컬렉션 라이브러리는 람다 표현식을 지원하는데, 덕분에 컬렉션 사용법이 어마어마하게 간단해졌습니다.
다음의 코드는 <String>으로 정의된 List 컬렉션에 오로지 String 타입만 허용하도록 Java 컴파일러에게 알려주는 방법을 보여주는 예시이며, 메서드 참조(method reference)와 foreach loop는 자바8에서 사용 가능합니다.

2. List 구현체

2.1. List 인터페이스

List 인터페이스는 크기 조정이 가능한 컨테이너에 객체를 저장하는 컬렉션의 기본 인터페이스입니다.

2.2. ArrayList와 LinkedList

ArrayList는 요소들을 동적으로 추가 및 삭제가 가능한 List 인터페이스의 구현체입니다. 만약, ArrayList에 초기 크기보다 더 많은 요소가 추가되는 경우에는 크기가 동적으로 증가됩니다. ArrayList는 배열을 기반으로 구현되어 있기 때문에 내부에 담긴 요소들을 get()과 set() 메서드를 사용하여 빠르고 효율적으로 접근할 수 있습니다. 일ㄴㅇ일일반적으로 ArrayList는 List 인터페이스의 구체 클래스로 많이 사용됩니다.

LinkedList는 이중 연결 리스트(double linked list)로 구현되어 있는데, 이는 add()와 remove() 메서드의 경우 ArrayList 보다 더 나은 성능을 발휘합니다. 반면에, LinkedList는 멤버에 대한 직접 접근을 제공하지 않기 때문에, get()과 set() 메서드는 ArrayList 보다 성능이 떨어집니다. 다음의 코드는 List와 ArrayList의 사용법을 설명합니다.


2.3. 정렬

람다(lambda)를 이용한 Comparator.compare()를 정의하여 자연스러운 순서(natural order)로 리스트를 정렬할 수 있습니다. 일반적으로 자바8에서는 compare() 메서드를 정의할 때 람다 표현식(lambda expression) 혹은 메서드 참조(method reference)를 사용합니다.


2.4 조건부 삭제

removeIf() 메서드를 사용하면 조건에 따라 list의 항목을 제거할 수 있습니다.


3. Map 구현체

3.1. Map과 HashMap


Map 인터페이스는 키에 값을 매핑하는 객체를 정의합니다. map은 중복된 키를 가질 수 없고, 각각의 키는 최대 하나의 값에만 매핑할 수 있습니다. HashMap 클래스는 Map 인터페이스의 효율적인 구현체 클래스입니다.


3.2. Map의 키를 배열 혹은 리스트로 전환

map의 키 또는 값은 배열 혹은 리스트로 전환할 수 있습니다.


3.3. map의 모든 요소를 처리하기

map의 모든 요소를 처리하기 위해서 매개변수로 람다를 이용하는 forEach() 메서드를 사용할 수도 있습니다.

3.4. map의 현재 값 또는 기본값 얻어오기

자바8에서 소개된 getOrDefault() 메서드를 이용하면, map에 키에 해당하는 값이 없으면 기본값을 반환합니다.


map에 존재하지 않을 때만 새로운 항목으로 추가해야 하는 경우라면, computeIfAbsent() 메소드를 사용하면 자동으로 값을 계산하여 map에 추가할 수 있습니다. (역자 주 : 덮어쓰기 방지)


4. 유용한 컬렉션 메서드

java.util.Collections의 클래스는 컬렉션을 다루는 작업에 유용한 기능들을 제공합니다.

  • Collections.copy(list, list) : 복사본 만들기
  • Collections.reverse(list) : 역순으로 정렬하기
  • Collections.shuffle(list) : 무작위로 섞기
  • Collections.sort(list) : 정렬하기

5. Collections.sort와 자바에서의 Comparator 사용

자바에서 컬렉션 정렬은 매우 쉽습니다. 그저 Collections.sort(Collection) 메서드만 사용하시면 됩니다.


위는 Integer가 Comparable 인터페이스를 구현했기 때문에 가능합니다. Comparable 인터페이스는,

비교 대상보다 작으면 음수를, 같으면 0을, 크면 양수를 반환하는, 요소들의 쌍(pairwise) 비교를 수행하는 메서드를 정의합니다.

이와는 다르게 정렬하고 싶다면, 람다 표현식을 사용해서 Comparator 인터페이스에 기반한 별도의 구현을 정의할 수도 있습니다


어떠한 속성이라도 혹은 심지어 속성 조합 기준으로도 정렬할 수 있습니다. 예를 들어, income과 dataOfBirth라는 속성을 가진 Person 타입의 객체가 있다면, 필요에 따라 Comparator의 다른 구현을 정의하는 것으로 객체를 정렬할 수 있습니다.