💡 순서가 있는 데이터의 집합으로 같은 데이터의 중복 저장을 허용. ArrayList, LinkedList, Vector, Stack이 있다.
1-2. List 특징
List 인터페이스를 구현한 모든 클래스는 저장 순서가 유지된다.
List 계열의 클래스는 중복 저장을 허용한다.
2. ArrayList 개요
2-1. ArrayList 란?
💡 자바에서 제공하는 기본 배열보다 느릴 수 있지만 동적 배열이 구현되어 있다.
ArrayList 클래스는 가장 많이 사용되는 컬렉션 클래스
2-2. ArrayList 특징
ArrayList는 인스턴스를 생성하게 되면 내부적으로 10칸짜리 배열을 생성해서 관리한다.
배열의 단점을 보완하기 위해 만들어졌기 때문에 크기 변경, 요소 추가/삭제/정렬 기능들을 메소드로 제공하고 있다.
자동적으로 수행되는 것이지 속도가 빨라지는 것은 아니다.
ArrayList는 스레드간 동기화가 지원되지 않는다. 따라서 다수의 스레드가 동시에 접근하여 데이터를 조작하게 될 경우 데이터 훼손이 일어날 수 있다.
ArrayList는 인덱스로 데이터에 접근할 수 있기 때문에 조회 기능적으로 뛰어나다.
section01.list.run.Application1
package com.ohgiraffers.section01.list.run; // list -> 배열의 업그레이드 (1. 1개의 타입만 X / 2. 크기 정하지 않아도 된다.)
import java.util.*;
public class Application1 {
public static void main(String[] args) {
/* 수업목표. 컬렉션 프레임워크에 대해 이해할 수 있다.(list부터) */
// ArrayList list = new ArrayList(); // 제네릭 안쓰면 ArrayList<Object>와 같다.
List list = new ArrayList(); // List 계열의 다른 인스턴스로 바껴도 나머지 코드에 영향을 주지
// 않기 위해서 다형성을 적용한 형태로 많이 쓴다.
/* 설명. 자료형에 구애받지 않고 데이터를 추가할 수 있다. */
list.add("apple");
list.add(123);
list.add(45.67);
list.add(new java.util.Date());
/* 컬렉션들은 toString() 오버라이딩이 잘 되어 있어 출력해 보기 편함 */
System.out.println("list 한번에 출력: " + list);
System.out.println("list.get(0): " + list.get(0)); // 처음 add한 값
System.out.println("list.get(2): " + list.get(2)); // 세 번째 add한 값
System.out.println("list에 담긴 데이터의 크기: " + list.size()); // length 아니다!!!
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
/* 설명.
* 배열보다 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> intArrList = new ArrayList<>();
for (int i = 0; i < 5; i++) {
intArrList.add(i + 1);
}
System.out.println(intArrList);
intArrList.add(2, 7); // 매개변수 2개짜리 add 메소드를 활용
System.out.println(intArrList);
/* 설명. ArrayList에는 중복(동등(e, h)) 값이 허용된다. */
System.out.println(list);
list.add("apple");
list.add(123);
System.out.println(list);
/* 설명. ArrayList가 관리하는 값을 수정하려면 set() 메소드 활용한다. */
list.set(0, 777);
System.out.println(list);
/* 설명. ArrayList가 관리하는 것을 삭제하려면 remove() 메소드 활용한다. */
list.remove(0);
System.out.println(list);
list.add(null);
System.out.println(list);
/* 설명. ArrayList를 활용한 정렬 */
/* 설명. 1. 문자열 데이터 정렬 */
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);
/* 설명. ArrayList 안에 들어있는 데이터 타입(String)에 정의된 정렬 기준대로 정렬 */
Collections.sort(stringList); // String에는 문자열 오름차순(Ascending)에 대한 정의가 되어 있음
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(반복자)는 hasNext()와 next()를 활용해 들어있는 data를 반복시킬 수 있는 타입이다. */
/* 설명. 다루는 Iterator와 관련된 컬렉션의 제네릭 타입과 일치하는 제네릭을 꼭 써주자!(다운캐스팅 고민 없도록) */
Iterator<String> iter = ((LinkedList<String>)stringList).descendingIterator(); // 이해 안댐?
// descendingIterator는 linkedList의 고유 메소드여서 다운캐스팅하고, 반환형이 Iterator여서 Iterator 변수로 받는다.
while (iter.hasNext()) {
System.out.print(iter.next() + " ");
}
}
}
// 실행 결과
list 한번에 출력: [apple, 123, 45.67, Tue Jul 23 20:10:36 KST 2024]
list.get(0): apple
list.get(2): 45.67
list에 담긴 데이터의 크기: 4
apple
123
45.67
Tue Jul 23 20:10:36 KST 2024
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 0]
[1, 2, 3, 3, 4, 5]
[1, 2, 7, 3, 4, 5]
[1, 2, 3, 4, 5]
[1, 2, 7, 3, 4, 5]
[apple, 123, 45.67, Tue Jul 23 20:10:36 KST 2024]
[apple, 123, 45.67, Tue Jul 23 20:10:36 KST 2024, apple, 123]
[777, 123, 45.67, Tue Jul 23 20:10:36 KST 2024, apple, 123]
[123, 45.67, Tue Jul 23 20:10:36 KST 2024, apple, 123]
[123, 45.67, Tue Jul 23 20:10:36 KST 2024, apple, 123, null]
stringList = [apple, orange, banana, mango, grape]
stringList = [apple, banana, grape, mango, orange]
integerList = [4, 20, 5, 8, 14]
integerList = [4, 5, 8, 14, 20]
orange mango grape banana apple
section01.list.run.Application2
package com.ohgiraffers.section01.list.run;
import com.ohgiraffers.section01.list.comparator.AscendingPrice;
import com.ohgiraffers.section01.list.dto.BookDTO;
import java.util.ArrayList;
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));
/* 설명. Collectionas.sort를 활용한 정렬(Comparable 방식 또는 Comparator 방식) */
// Collections.sort(bookList); // Comparable 방식
Collections.sort(bookList, new AscendingPrice()); // Comparator 방식 (Comparable 보다 강하다)
/* 설명. ArrayList가 제공하는 sort를 사용할 수도 있다.(다만 Comparator 기준만 가능) */
bookList.sort(new AscendingPrice()); // ArrayList 일때만 사용 가능, Comparator 방식과 같다.
System.out.println(bookList);
for (int i = 0; i < bookList.size(); i++) {
System.out.println(bookList.get(i));
}
}
}
// 실행 결과
[BookDTO{number=2, title='목민심서', author='정약용', price=30000}, BookDTO{number=3, title='동의보감', author='허준', price=40000}, BookDTO{number=4, title='삼국사기', author='김부식', price=46000}, BookDTO{number=1, title='홍길동전', author='허균', price=50000}, BookDTO{number=5, title='삼국유사', author='일연', price=58000}]
BookDTO{number=2, title='목민심서', author='정약용', price=30000}
BookDTO{number=3, title='동의보감', author='허준', price=40000}
BookDTO{number=4, title='삼국사기', author='김부식', price=46000}
BookDTO{number=1, title='홍길동전', author='허균', price=50000}
BookDTO{number=5, title='삼국유사', author='일연', price=58000}
section01.list.dto.BookDTO
package com.ohgiraffers.section01.list.dto;
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 +
'}';
}
/* 필기.
* 우리가 원하는 필드의 오름차순 또는 내림차순을 할 수 있다.
* 필드가 n개면 총 (n * 2)가지의 정렬 기준을 가질 수 있다.(각각 ASC 또는 DESC)
* 정렬은 CompareTo()메소드가 반환하는 int형의 부호에 따라 정해지게 되므로 오름차순과 내림차순은
* 부호만 달리 되도록 하면 된다.
* (해당 필드가 String형일 경우는 String의 compareTo() 메소드를 활용하자)
* */
@Override // 정렬을 하나밖에 하지 못한다.
public int compareTo(BookDTO o) { // 메소드를 호출하는 것이 두번째, 매개변수로 들어오는 것이 첫번째 -> 내부적으로...
/* 설명. 가격에 대한 오름차순 */
// return this.price - o.getPrice(); // 어떤 필드를 이용할지가 중요.
/* 설명. 가격에 대한 내림차순 */
// return o.getPrice() - this.price;
// return -(this.price - o.getPrice());
/* 설명. 책 제목에 대한 오름차순 */
// return this.title.compareTo(o.getTitle());
/* 설명. 책 제목에 대한 내림차순 */
// return -this.title.compareTo(o.getTitle());
return o.getTitle().compareTo(this.title);
}
}
section01.list.comparator.AscendingPrice
package com.ohgiraffers.section01.list.comparator;
import com.ohgiraffers.section01.list.dto.BookDTO;
import java.util.Comparator;
public class AscendingPrice implements Comparator<BookDTO> {
/* 설명. 가격 오름차순이 가능하도록 compare() 메소드 오버라이딩 */
@Override
public int compare(BookDTO o1, BookDTO o2) {
return o1.getPrice() - o2.getPrice();
}
}
3. LinkedList 개요
3-1. LinkedList란?
💡 ArrayList 클래스가 배열과 유사하게 데이터를 저장함으로써 발생하는 단점을 보완하기 위해 고안. 내부적으로 연결 리스트(Linked List)를 이용해 데이터를 저장.
인덱스 같은 위치는 신경 쓰지 않는다.
3-2. LinkedList 특징
LinkedList는 저장되는 데이터들이 연속된 공간에 저장되는 것이 아니다. 따라서 각 데이터를 링크(Link)를 연결하여 구성한다.
데이터의 삽입, 삭제가 빈번할 경우 연결되는 링크 정보만 수정하면 되기 때문에 ArrayList보다 더 적합하다.
스택, 큐, 양방향 큐 등을 구성하기 용이하다.
LinkedList 에는 단일 연결 리스트와 이중 연결 리스트가 있다.
단일 연결 리스트
저장한 요소가 순서를 유지하지 않고 저장되지만 이러한 요소들 사이를 링크로 연결하여 구성하며 마치 연결된 리스트 형태인 것 처럼 만든 자료구조이다.
요소의 저장과 삭제 시 다음 요소를 가리키는 참조 링크만 변경하면 되기 때문에 요소의 저장과 삭제가 빈번히 일어나는 경우 ArrayList보다 성능 면에서 우수하다.
이중 연결 리스트
단일 연결 리스트는 다음 요소만 링크하는 반면 이중 연결 리스트는 이전 요소도 링크하여 이전 요소로 접근하기 쉽게 고안된 자료구조 이다.
section01.list.run.Application3
package com.ohgiraffers.section01.list.run;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
public class Application3 {
public static void main(String[] args) {
/* 수업목표. List 계열을 출력하는 4가지 방법 */
/* 수업목표. LinkedList에 대해 이해할 수 있다. */
// List<String> arrList = new ArrayList<>();
List<String> arrList = new LinkedList<>();
arrList.add("apple");
arrList.add("banana");
arrList.add("orange");
arrList.add("mango");
arrList.add("grape");
/* 설명. 1. toString() 활용하기 */
System.out.println("arrList = " + arrList);
/* 설명. 2. for문 활용하기 */
for (int i = 0; i < arrList.size(); i++) {
System.out.println(arrList.get(i));
}
/* 설명. 3. for-each문 활용하기 */
for(String str: arrList) {
System.out.println(str);
}
/* 설명. 4. iterator 활용하기 */
Iterator<String> iter = arrList.iterator();
while(iter.hasNext()) {
System.out.println(iter.next());
}
/* 설명. 1번 인덱스(2번째)의 과일 수정 */
arrList.set(1, "pineapple");
System.out.println("arrList = " + arrList);
/* 설명. list가 관리하는 요소들 제거 */
arrList.remove(2); // LinkedList는 ArrayList와 달리 부분적인 요소 제거에서 성능이 우수하다.
System.out.println("arrList = " + arrList);
arrList.clear(); // 전체 요소들 제거 / 리스트 객체는 그대로 있다. 보관한 값만 제거
System.out.println("arrList = " + arrList);
/* 설명. 요소가 없는 list 계열인지 확인 */
System.out.println("isEmpty = " + arrList.isEmpty());
}
}
// 실행 결과
arrList = [apple, banana, orange, mango, grape]
apple
banana
orange
mango
grape
apple
banana
orange
mango
grape
apple
banana
orange
mango
grape
arrList = [apple, pineapple, orange, mango, grape]
arrList = [apple, pineapple, mango, grape]
arrList = []
isEmpty = true
4. Stack 개요
4-1. Stack 이란?
💡 '쌓다', '더미' 라는 뜻을 가진 자료 구조로 리스트 계열 클래스인 Vector 클래스를 상속받아 구현된 자료구조
LIFO(Last In First Out)
4-2. Stack 특징
제일 나중에 들어간 데이터가 가장 먼저 나오는 LIFO(Last In First Out) 구조로 되어 있다.
메소드가 호출될 때 해당 구조로 메모리에 올라가게 된다.
section01.list.run.Application4
package com.ohgiraffers.section01.list.run;
import java.util.Stack;
public class Application4 {
public static void main(String[] args) {
/* 수업목표. Stack에 대해 이해하고 사용할 수 있다. */
/* 필기.
* Stack이란?
* 후입선출(LIFO) 또는 선입후출(FiLO)의 자료 구조로 push(), pop(), peek() 등의 메소드를
* 활용하여 자료를 처리할 수 있다.
* */
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);
System.out.println("peak(): " + 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);
}
}
// 실행 결과
[1, 2, 3, 4, 5]
peak(): 5
[1, 2, 3, 4, 5]
search(): 4
pop(): 5
pop(): 4
pop(): 3
pop(): 2
pop(): 1
[]
5. Queue 개요
5-1. Queue 란?
💡 사전적 의미로 '줄'을 의미. 스택과는 다르게 FIFO(선입선출)로 구현되어 있다.
시간보다 순서의 정확성을 더 중요하게 생각할 때 큐 사용!
5-2. Queue 특징
먼저 들어간 데이터가 먼저 나오는 FIFO(First In First Out) 구조.
큐는 Front와 Rear를 정하고 한 곳에는 삭제만, 다른 한 곳에서는 삽입 연산만 처리한다.
Queue 인터페이스를 상속받는 하위 인터페이스 들은 Deque, BlockingQueue, BlockingDeque, TreansferQueue 등 다양하지만 대부분의 큐는 LinkedList를 이용한다.
package com.ohgiraffers.section01.list.run;
import java.util.Collections;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Queue;
public class Application5 {
public static void main(String[] args) {
/* 수업목표. Queue에 대해 이해하고 활용할 수 있다. */
/* 필기.
* Queue란?
* 선형 메모리 공간에 데이터를 저장하여 순서를 유지하기 위한 선입선출(FIFO) 방식의 자료구조이다.
* 대부분 LinkedList를 많이 사용한다.
* */
// Queue que = new Queue();
// Queue<String> que = new LinkedList<>(); // Queue 타입은 단방향만 가능...
// LinkedList<String> que = new LinkedList<>(); // LinkedList는 queue 중에서도 deque에 해당되어
// // offerFirst()나 offerLast() 같은 메소드도 제공된다.
/* 설명. PriorityQueue를 활용하면 선입선출 + 정렬의 개념을 가져갈 수 있다. */
// PriorityQueue<String> que = new PriorityQueue<>(); // String의 기준대로 문자열 오름차순 / 결과 이상하던데?????????기준이 뭐지???????????????
PriorityQueue<String> que = new PriorityQueue<>(Collections.reverseOrder()); // 반대인 내림차순
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("poll(): " + que.poll());
System.out.println("que = " + que);
System.out.println("poll(): " + que.poll());
System.out.println("que = " + que);
System.out.println("poll(): " + que.poll());
System.out.println("que = " + que);
System.out.println("poll(): " + que.poll());
System.out.println("que = " + que);
System.out.println("poll(): " + que.poll());
System.out.println("que = " + que);
}
}
// 실행 결과
que = [third, fourth, second, first, fifth]
peek(): third
poll(): third
que = [second, fourth, fifth, first]
poll(): second
que = [fourth, first, fifth]
poll(): fourth
que = [first, fifth]
poll(): first
que = [fifth]
poll(): fifth
que = []