
출처 : https://techvidvan.com/tutorials/java-collection-framework/
일관된 API
Collection 에서 제공하는 규격화된 메소드를 사용함으로 일관된 사용과 유지보수가 가능하다.
프로그래밍 비용 감소
이미 제공된 자료구조를 활용하는 것으로 low-level의 알고리즘을 고민할 시간과 노력을 아낄 수 있다.
프로그래밍 속도 및 품질 향상
필요한 자료구조를 사용함으로써 프로그래밍의 속도 뿐만 아니라 기동 속도, 품질 향상을 기대할 수 있다.
| 인터페이스 | 설명 | 구현 클래스 |
|---|---|---|
| List | 순서가 있는 데이터의 집합으로, 데이터의 중복을 허용한다. | ArrayList, LinkedList, Stack, Queue, Vector |
| Set | 순서가 없는 데이터의 집합으로, 데이터 중복 허용하지 않는다. | HashSet, TreeSet |
| Map<K, V> | 키와 값이 쌍을 이루어 구성되는 데이터 집합으로 순서가 없다. | |
| 키의 중복은 허용되지 않지만, 값의 중복은 허용된다. | HashMap, TreeMap, Properties |
1-2. List 특징
2-1. ArrayList 란?
💡 ArrayList는 자바에서 제공하는 기본 배열보다 느릴 수 있지만 동적 배열이 구현되어 있다. ArrayList 클래스는 가장 많이 사용되는 컬렉션 클래스이다. JDK 1.2 부터 제공된다.
2-2. ArrayList 특징
ArrayList는 인스턴스를 생성하게 되면 내부적으로 10칸짜리 배열을 생성해서 관리한다.
배열의 단점을 보완하기 위해 만들어졌기 때문에 크기 변경, 요소 추가/삭제/정렬 기능들을 메소드로 제공하고 있다.
자동적으로 수행되는 것이지 속도가 빨라지는 것은 아니다.
ArrayList는 스레드간 동기화가 지원되지 않는다. 따라서 다수의 스레드가 동시에 접근하여 데이터를 조작하게 될 경우 데이터 훼손이 일어날 수 있다.
ArrayList는 인덱스로 데이터에 접근할 수 있기 때문에 조회 기능적으로 뛰어나다.
ArrayList 이용한 코드
import java.util.*;
public class Application1 {
public static void main(String[] args) {
/* 수업목표. 컬렉션 프레임워크에 대해 이해할 수 있다. */
ArrayList aList = new ArrayList();
// ArrayList<Object> aList = new ArrayList<>(); // 다이아몬드 연산자 없는 제네릭 클래스는 <Object>와 같다.
List list = new ArrayList<>(); // 컬렉션은 다형성을 적용한 형태로 많이 사용한다.
// Collection col = new ArrayList<>();
aList.add("apple"); // String -> Object
aList.add(123); // 123 -> (Integer)123 -> Object
aList.add(45.56);
aList.add(new java.util.Date());
System.out.println("aList = " + aList);
System.out.println("첫 번째 저장된 값: " + aList.get(0));
System.out.println("첫 번째 저장된 값: " + aList.get(1));
System.out.println("aList에 담긴 데이터에 담긴 크기: " + aList.size()); // 우리가 ArrayList에 추가한 값의 크기
for (int i = 0; i < aList.size(); i++) {
System.out.println(aList.get(i).toString());
}
/* 필기.
* 배열보다 ArrayList가 나은 점
* 1. 처음부터 크기 할당 불필요
* 2. 중간에 값 추가 및 삭제가 용이
* */
/* 설명. 배열과 ArrayList를 활용해 각각 원하는 인덱스에 값 추가해보기 */
int[] intArr = new int[5];
int num = 0;
for (int i = 0; i < intArr.length; i++) {
intArr[i] = ++num;
}
System.out.println(Arrays.toString(intArr));
int[] newArr = new int[intArr.length + 1];
System.arraycopy(intArr, 0, newArr, 0, intArr.length);
System.out.println(Arrays.toString(newArr));
/* 설명. 2번 인덱스 자리에 7를 끼워넣기 */
for (int i = newArr.length -2; i > 1; i--) { // 기존의 값을 뒤로 옮길 때 앞의 인덱스부터 옮기면 안됨
newArr[ i + 1] = newArr[i];
}
System.out.println(Arrays.toString(newArr));
newArr[2] = 7;
System.out.println(Arrays.toString(newArr));
/* 설명. ArrayList에서 제공하는 add메소드로 쉽게 해 보자. */
ArrayList<Integer> intArrayList = new ArrayList<>();
for (int i = 0; i < 5; i++) {
intArrayList.add(i + 1);
}
System.out.println(intArrayList);
intArrayList.add(2, 7);
System.out.println(intArrayList);
/* 설명. ArrayList는 중복 저장을 허용한다. */
System.out.println(aList);
aList.add("apple");
System.out.println(aList);
/* 설명. set(0 메소드로 원하는 인덱스의 값을 원하는 값으로 수정할 수 있다. */
aList.set(1, 777);
System.out.println(aList);
/* 설명. remove() 메소드로 원하는 인덱스의 값을 삭제할 수 있다. */
aList.remove(1);
System.out.println(aList);
aList.add(null);
System.out.println(aList);
/* 설명. ArrayList를 활용해 정렬을 진행해 보자. */
List<String> stringList = new ArrayList<>();
stringList.add("apple");
stringList.add("orange");
stringList.add("banana");
stringList.add("mango");
stringList.add("grape");
System.out.println("stringList = " + stringList);
/* 설명. Collections의 sort() 메소드를 활용한 정렬 */
Collections.sort(stringList);
System.out.println("stringList = " + stringList);
List<Integer> integerList = new ArrayList<>();
integerList.add(4);
integerList.add(20);
integerList.add(5);
integerList.add(8);
integerList.add(14);
System.out.println("integerList = " + integerList);
Collections.sort(integerList);
System.out.println("integerList = " + integerList);
/* 설명. 내림차순으로 바꾸기 위해 ArrayList에서 LinkedList로 교체 */
stringList = new LinkedList<>(stringList);
Iterator<String> iter = ((LinkedList<String>)stringList).descendingIterator();
while (iter.hasNext()) {
System.out.println(iter.next());
}
}
}
import com.ohgiraffers.section01.list.comparator.AscendingPrice;
import com.ohgiraffers.section01.list.comparator.DescendingAuthor;
import com.ohgiraffers.section01.list.dto.BookDTO;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class Application2 {
public static void main(String[] args) {
/* 수업목표. ArrayList에서 관리되는 자료의 정렬 기준을 이용할 수 있다. */
/* 목차. 1. Comparable 인터페이스 구현 방법 활용 */
List<BookDTO> bookList = new ArrayList<>();
bookList.add(new BookDTO(1, "홍길동전", "허균", 50000));
bookList.add(new BookDTO(2, "목민심서", "정약용", 30000));
bookList.add(new BookDTO(3, "동의보감", "허준", 40000));
bookList.add(new BookDTO(4, "삼국사기", "김부식", 46000));
bookList.add(new BookDTO(5, "삼국유사", "이연", 58000));
// for (int i = 0; i < bookList.size(); i++) {
// System.out.println(bookList.get(1));
// }
// Collections.sort(bookList);
// Collections.sort(bookList, new AscendingPrice());
Collections.sort(bookList, new DescendingAuthor());
/* 설명. ArrayList가 제공하는 sort 사용 시(Comparator 기준만 가능) */
bookList.sort(new DescendingAuthor());
for(BookDTO book: bookList) {
System.out.println(book);
}
}
}
public class BookDTO implements Comparable<BookDTO> {
private int number;
private String title;
private String author;
private int price;
public BookDTO() {
}
public BookDTO(int number, String title, String author, int price) {
this.number = number;
this.title = title;
this.author = author;
this.price = price;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public String toString() {
return "BookDTO{" +
"number=" + number +
", title='" + title + '\'' +
", author='" + author + '\'' +
", price=" + price +
'}';
}
/* 설명. 가격에 대해 오름차순 기준을 설정(feat.Comparable 인터페이스 + compareTo 메소드 오버라이딩) */
/* 필기.
* 우리가 원하는 필드의 오름차순 또는 내림차순을 할 수 있다.
* 필드가 4개면 총 8가지의 정렬 기준을 가질 수 있다. (각각 오름차순 또는 내림차순이 가능하므로)
* 정렬은 compareTo 메소드가 반환하는 int형의 부호에 따라 정해지게 되며 부호가 반대로 되면 오름차순과 내림차순의 개념이 반대로 된다.
* (오름차순은 매개변수로 넘어온 것을 빼주는 형태로 작성하면 된다.(String형 제외)
* */
// @Override
// public int compareTo(Object o) {
/* 설명. 연산이 가능한 자료형(숫자형)에 대해 정렬 기준 정하기 */
// return this.price - ((BookDTO)o).price; // 가격기준 오름차순
// return ((BookDTO)o).price - this.price; // 가격기준 내림차순
// return -(this.price - ((BookDTO)o).price); // 가격기준 내림차순
/* 설명. 연산이 불가능한 자료형에 대해 정렬 기준 정하기 */
// return this.author.compareTo(((BookDTO) o).author); // 작가의 오름차순
// return -this.author.compareTo(((BookDTO) o).author); // 작가의 내림차순
//
// }
/* 설명. Comparable 인터페이스에 제네릭을 걸면 다운캐스팅이 불필요하다. */
@Override
public int compareTo(BookDTO o) {
return this.title.compareTo(o.title);
}
}
import com.ohgiraffers.section01.list.dto.BookDTO;
import java.util.Comparator;
/* 설명. 우리가 원하는 필드의 원하는 정렬 기준을 정의하는 클래스(feat. Comparator 인터페이스) */
public class AscendingPrice implements Comparator<BookDTO> {
/* 설명. 가격 오름차순을 위한 compare 메소드 오버라이딩 작성 */
// @Override
// public int compare(Object o1, Object o2) {
// return ((BookDTO)o1).getPrice() - ((BookDTO)o2).getPrice();
// }
/* 설명. Comparator 인터페이스에 제네릭 추가 */
@Override
public int compare(BookDTO o1, BookDTO o2) {
return o1.getPrice() - o2.getPrice();
}
}
import com.ohgiraffers.section01.list.dto.BookDTO;
import java.util.Comparator;
public class DescendingAuthor implements Comparator<BookDTO> {
@Override
public int compare(BookDTO o1, BookDTO o2) {
return -o1.getAuthor().compareTo(o2.getAuthor());
}
}

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
public class Application3 {
public static void main(String[] args) {
/* 수업목표. LinkedList에 대해 이해하고 활용할 수 있다. */
List<String> linkedList = new LinkedList<>();
linkedList.add("apple");
linkedList.add("banana");
linkedList.add("mango");
linkedList.add("orange");
linkedList.add("grape");
System.out.println(linkedList.size() + "개의 자료가 담김");
/* 설명. List 계열 출력하는 4가지 방법 */
/* 설명. 1. toString() 활용하기 */
System.out.println("linkedList = " + linkedList);
System.out.println();
/* 설명. 2. for문 활용하기 */
for (int i = 0; i < linkedList.size(); i++) {
System.out.println(linkedList.get(1));
}
System.out.println();
/* 설명. 3. for-each문(향상된 for문) 활용하기 */
for (String str: linkedList) {
System.out.println(str);
}
System.out.println();
/* 설명. 4. iterator 활용하기 */
/* 설명. 반복문 안에서 next()를 두 번 이상 사용하게 될 경우 의도한 것이 아니라면 유의한다.(feat. 변수활동) */
Iterator<String> iter = linkedList.iterator();
while (iter.hasNext()) {
String fruit = iter.next();
System.out.println(fruit);
}
System.out.println();
/* 설명. 1번 인덱스(2번째)의 과일 수정 */
linkedList.set(1, "pineapple");
System.out.println(linkedList);
/* 설명. list가 관리하는 요소를 제거 */
linkedList.clear();
System.out.println(linkedList);
/* 설명. 요소가 없는 list 객체에 대해 boolean값으로 확인 */
System.out.println(linkedList.isEmpty()); // isEmpty = 속이 비어있으면 true
}
}

제일 나중에 들어간 데이터가 가장 먼저 나오는 LIFO(Last In First Out) 구조로 되어 있다.
메소드가 호출될 때 해당 구조로 메모리에 올라가게 된다.
Stack 사용하는 코드
import java.util.Stack;
public class Application4 {
public static void main(String[] args) {
/* 수업목표. Stack에 대해 이해하고 사용할 수 있다. */
/* 필기.
* Stack이란?
* 후입선출(LIFO-Last Input First Out) 또는 선입후출(FILO-First Input Last Out) 구조의 자료구조이다.
* push(), pop(), peek() 등의 메소드를 활용하여 자료를 처리할 수 있다.
* Vector를 상속한 클래스이다.
* */
Stack<Integer> integerStack = new Stack<>();
/* 설명. Stack 인스턴스 생성 */
integerStack.push(1);
integerStack.push(2);
integerStack.push(3);
integerStack.push(4);
integerStack.push(5);
System.out.println(integerStack);
/* 필기.
* peek(): 해당 스택의 최상단(top)에 있는 요소 반환
* search(): 해당 요소와 일치하는 요소의 위치를 최상단에서부처 몇 번째인지 반환
* pop(): 해당 스택의 최상단에 있는 요소 반환 후 제거
* */
System.out.println("peek(): " + integerStack.peek());
System.out.println(integerStack); // 탑에 쌓여있는 숫자를 잠깐 엿본거라서 제거되지않음
System.out.println("search(): " + integerStack.search(2));
System.out.println("pop(): " + integerStack.pop());
System.out.println("pop(): " + integerStack.pop());
System.out.println("pop(): " + integerStack.pop());
System.out.println("pop(): " + integerStack.pop());
System.out.println("pop(): " + integerStack.pop());
System.out.println(integerStack);
}
}


import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Queue;
public class Application5 {
public static void main(String[] args) {
/* 수업목표. Queue에 대해 이해하고 사용할 수 있다. */
/* 필기.
* Queue란?
* 선형 메모리 공간에 데이터를 저장하여 순서를 유지하기 위한 선입선출 방식의 자료구조이다.
* 대부분 LinkedList를 많이 사용한다.
* */
// Queue<String> que = new Queue(); // error 발생
// Queue<String> que = new LinkedList<>();
PriorityQueue<String> que = new PriorityQueue<>(); // que이면서 정렬을 위한 최우선 가치에 있는 요소의 순서를 최앞단으로 바꿔주는 기능이 있는 자료구조
que.offer("first");
que.offer("second");
que.offer("third");
que.offer("fourth");
que.offer("fifth");
System.out.println("que = " + que);
System.out.println("peek(): " + que.peek());
System.out.println("peek(): " + que.peek());
System.out.println("poll(): " + que.poll());
System.out.println("poll(): " + que.poll());
System.out.println("poll(): " + que.poll());
System.out.println("poll(): " + que.poll());
System.out.println("poll(): " + que.poll());
System.out.println("que = " + que);
}
}
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class Application1 {
public static void main(String[] args) {
/* 수업목표. Set 자료구조의 특성을 이해하고 hashSet을 이용할 수 있다. */
// HashSet<String> hset = new HashSet<>();
Set<String> hset = new HashSet<>();
hset.add(new String("java"));
hset.add(new String("mariaDB"));
hset.add(new String("servlet"));
hset.add(new String("spring"));
hset.add(new String("html"));
/* 설명. Set에 저장된 자료는 넣은 순서를 보장하지 않는다. */
System.out.println("hset = " + hset);
hset.add(new String("mariaDB")); // 같은 해쉬코드 값은 안들어가고 튕겨냄
hset.add(new String("mariaDB1"));
/* 설명. 중복값은 Set에 추가되지 않는다.(동등 객체 저장 안함)(feat. equals와 hashCode 오버라이딩 필요) */
/* 설명. 중복 되는 값이 있으면 새로 추가하려는 객체의 주소가 튕겨 나간다.(덮어쓰지 않음) */
System.out.println("hset = " + hset);
/* 설명. 인덱스 개면이 없는 Set은 iterator(반복자)를 돌리거나 배열로 바꿔야 확이나 가능하다. */
Iterator<String> iter = hset.iterator();
while (iter.hasNext()) {
System.out.println(iter.next());
}
System.out.println();
Object[] objArr = hset.toArray();
for (Object obj: objArr) {
System.out.println(obj);
}
System.out.println("size(): " + hset.size());
hset.clear();
System.out.println("size(): " + hset.size());
System.out.println("isEmpty(): " + hset.isEmpty());
}
}2-3. LinkedHashSet 이란?
💡 LinkedHashSet 이란 HashSet과 동일한 구조를 가지지만 HashSet과는 다르게 데이터를 삽입한 순서대로 출력된다. (HashSet은 순서를 관리하지 않는다)import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;
public class Application2 {
public static void main(String[] args) {
/* 수업목표. LinkedHashSet에 대해 이해하고 활용할 수 있다. */
/* 필기.
* LinkedHashSet은 저장 당시의 순서를 유지하는 특징을 가지고 있다.
* */
Set<String> lSet = new LinkedHashSet<>();
lSet.add("ramen");
lSet.add("pork");
lSet.add("kimchi");
lSet.add("friedEgg");
lSet.add("soup");
System.out.println("lSet = " + lSet);
}
}import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;
public class Application3 {
public static void main(String[] args) {
/* 수업목표. TreeSet에 대해 이해하고 활용할 수 있다. */
Set<String> tSet = new TreeSet<>();
tSet.add("ramen");
tSet.add("pork");
tSet.add("kimchi");
tSet.add("friedEgg");
tSet.add("soup");
System.out.println("tSet = " + tSet);
/* 설명. 과제. 중복되지 않는 번호를 발생시켜 로또 번호 발생기 만들기(TreeSet 사용하지 않고) */
Set<Integer> lotto = new TreeSet<>();
/* 설명. 1부터 45까지의 6자리 중복되지 않는 난수를 발생하여 값 저장 */
while (lotto.size() < 6) {
lotto.add((int)(Math.random() * 45) + 1);
}
/* 설명. 출력 시에는 오름차순으로 출력 되도록 작성 */
System.out.println("lotto: " + lotto);
}
}
저장은 느리지만 Hashing 이라는 해시 함수를 이용해서 데이터를 해시 테이블에 저장하기 때문에 검색 측면에서 뛰어나다.
Key는 중복이 불가능하지만, value는 중복이 가능하다.
중복된 키 값에 값을 저장하면 기존의 값에 새로운 값이 덮어 씌어진다.
HashMap 사용하는 코드
import java.util.*;
public class Application1 {
public static void main(String[] args) {
/* 수업목표. Map의 자료구조에 대해 이해하고 HashMap을 이용할 수 있다. */
Map<Object, Object> hmap = new HashMap<>();
hmap.put("one", new Date());
hmap.put(12, "red apple");
hmap.put(33, 123);
System.out.println("키가 \"one\"인 value 값: " + hmap.get("one"));
System.out.println("Map의 toString(): " + hmap);
/* 설명. 키 값은 중복되면 이후 put 되는 키와 밸류가 덮어씌운다. (주의할 것) */
hmap.put(12, "purple banana");
System.out.println(hmap.get(12));
/* 설명. 밸류는 중복되어도 상관이 없다. */
hmap.put(77, "purple banana");
System.out.println(hmap);
System.out.println("Map이 지닌 entry의 수: " + hmap.size());
System.out.println("77번 키와 관련된 entry 삭제: " + hmap.remove(77));
System.out.println("삭제 후 entry의 수: " + hmap.size());
/* 필기. Map을 반복하여 각 entry들(키와 밸류)을 활용해 보자. */
HashMap<String, String> hmap2 = new HashMap<>();
hmap2.put("one", "java17");
hmap2.put("two", "mariaDB 10");
hmap2.put("three", "servlet/jsp");
hmap2.put("four", "springboot 3.0");
hmap2.put("five", "vue.js");
/* 목차. 1. keySet()을 활용한 iterator 활용하기 */
Set<String> keys = hmap2.keySet();
Iterator<String> iter = keys.iterator();
while (iter.hasNext()) {
String key = iter.next();
System.out.println("key값:" + key + ", value값: " + hmap2.get(key));
}
/* 목차. 2. entrySet()을 활용한 iterator 활용하기 */
Set<Map.Entry<String, String>> set = hmap2.entrySet();
Iterator<Map.Entry<String, String>> interEntry = set.iterator();
while (interEntry.hasNext()) {
Map.Entry<String, String> entry = interEntry.next();
System.out.println("key값: " + entry.getKey() + ", value값: " + entry.getValue());
}
}
}