Java의 자료구조는 Collection Framework과 Map으로 나눌 수 있습니다.
Collection Framework은
다수의 데이터를 쉽고 효과적으로 처리할 수 있도록 도와줄 수 있는 클래스의 집합이고,
Map은
Key와 Value로 이루어진 자료 구조입니다.
Collection Framework란?
자바에서 컬렉션 프레임워크(collection framework)란 다수의 데이터를 쉽고 효과적으로 처리할 수 있는 표준화된 방법을 제공하는 클래스의 집합을 의미합니다.
즉, 데이터를 저장하는 자료 구조와 데이터를 처리하는 알고리즘을 구조화하여 클래스로 구현해 놓은 것입니다.
Iterator 인터페이스를 상속하는 Collection은 List, Queue, Set 으로 나뉘며, 각각의 요소들은 아래와 같이 구성되어있습니다.
List
- ArrayList, LinkedList, Vector, Stack
Queue
- PriorityQueue, ArrayDeque
Set
- HashSet, LinkedHashSet, TreeSet
Collection Framework - List
ArrayList, LinkedList, Vector, Stack
1. ArrayList
ArrayList는 배열을 이용하여 만든 리스트 입니다. 기본 크기는 10이지만, 원소가 늘어나면 더 큰 배열에 옮겨 담습니다.
배열의 특징 때문에 조회가 빠르다는 장점이 있지만, 삽입/삭제가 느리다는 단점 또한 존재합니다.
import java.util.ArrayList;
class Test {
public static void main (String[] args) {
// creating an ArrayList named marks
ArrayList<Integer> marks = new ArrayList<>();
// adding elements in the ArrayList
marks.add(50);
marks.add(60);
marks.add(40);
marks.add(70);
// printing the ArrayList
System.out.println(marks);
}
}
2. LinkedList
LinkedList 클래스는 ArrayList 클래스가 배열을 이용하여 요소를 저장함으로써 발생하는 단점을 극복하기 위해 고안되었습니다. LinkedList는 데이터가 저장되어있는 노드(Node) 객체들을 참조 체인으로 엮어 놓은 자료 구조입니다.
next와 prev로 양방향 포인터를 가집니다. 포인터로 각각의 노드들을 연결하고 있어서, 단순히 기존 포인터를 끊고 새로운 노드에 연결하면 되기 때문에 삽입과 삭제가 빠르다는 장점이 있습니다.
하지만, 특정 원소를 조회하기 위해서는 첫 노드부터 순회해야 하기 때문에 ArrayList에 비해 느리다는 단점이 있습니다.
LinkedList는 조회보다 삽입/삭제가 많은 경우에 사용하는 것이 좋습니다.
import java.util.LinkedList;
class Main {
public static void main(String[] args){
// create linkedlist
LinkedList<String> animals = new LinkedList<>();
// Add elements to LinkedList
animals.add("Dog");
animals.add("Cat");
animals.add("Cow");
System.out.println("LinkedList: " + animals);
}
}
3. Vector
Vector는 ArrayList와 같이 배열로 만들어진 리스트입니다. ArrayList와 마찬가지로 Vector내부에 값이 추가되면 자동으로 크기가 조절되며 그다음 객체들은 한 자리씩 뒤로 이동됩니다.
하지만, ArrayList와 다른점이 있는데, Vector는 동기화된 메소드로 구성되어 있기 때문에 멀티 스레드가 동시에 이 메소드메서드들을 실행할 수 없고, 하나의 스레드가 실행을 완료해야만 다른 스레드들이 실행할 수 있습니다. 그래서 멀티 스레드 환경에서 안전하게 객체를 추가하고 삭제할 수 있습니다. 하지만 get, put 모두 synchronized가 있어서, 스레드마다 lock이 걸리게 되고 성능은 ArrayList에 비해 안좋다는 단점이 있습니다.
import java.util.Iterator;
import java.util.Vector;
class Main {
public static void main(String[] args) {
Vector<String> animals= new Vector<>();
animals.add("Dog");
animals.add("Horse");
animals.add("Cat");
// Using get()
String element = animals.get(2);
System.out.println("#### Element at index 2: " + element);
// Using iterator()
Iterator<String> iterate = animals.iterator();
System.out.print("#### Vector: ");
while(iterate.hasNext()) {
System.out.print(iterate.next());
System.out.print(", ");
}
System.out.println(">>>> Initial Vector: " + animals);
// Using remove()
String element = animals.remove(1);
System.out.println(">>>> Removed Element: " + element);
System.out.println(">>>> New Vector: " + animals);
// Using clear()
animals.clear();
System.out.println(">>>> Vector after clear(): " + animals);
}
}
// 출력
// #### Element at index 2: Cat
// #### Vector: Dog, Horse, Cat,
// >>>> Initial Vector: [Dog, Horse, Cat]
// >>>> Removed Element: Horse
// >>>> New Vector: [Dog, Cat]
// >>>> Vector after clear(): []
4. Stack
Stack은 LIFO(Last-In-First-Out) 특성을 가지는 자료구조입니다. 마지막에 들어온 원소가 처음으로 나가는 특징을 가지고, 들어올 때는 push, 나갈때는 pop이라는 용어를 사용합니다.
Stack은 Vector를 implements 받습니다.
하지만 push()와 pop()은 Stack 클래스 내부에 구현되어 있기 때문에, 이 메서드를 사용하려면 Stack으로 받아야 합니다.
void stackTest() {
Stack<Integer> stack = new Stack<>();
stack.push(30);
stack.push(50);
assertEquals(2, stack.size());
assertEquals(50, stack.pop());
assertEquals(30, stack.pop());
assertEquals(0, stack.size());
}
Collection Framework - Queue
PriorityQueue, ArrayDeque
Stack과는 다르게 Queue는 FIFO(First-In-First-Out) 구조를 가집니다.
처음 들어온 원소가 처음으로 나간다는 특징이 있고, 대기열에서 원소는 선입선출 방식으로 저장되고 액세스 됩니다.
즉, 원소는 뒤에서 추가되고 앞에서 제거됩니다.
1. PriorityQueue
우선순위를 가지는 큐(queue)로 일반적인 큐와는 조금 다르게, 원소에 우선순위를 부여하여 높은 순으로 먼저 반환합니다.
import java.util.PriorityQueue;
class Main {
public static void main(String[] args) {
// Creating a priority queue
PriorityQueue<Integer> numbers = new PriorityQueue<>();
// Using the add() method
numbers.add(4);
numbers.add(2);
System.out.println("PriorityQueue: " + numbers);
// Using the offer() method
numbers.offer(1);
System.out.println("Updated PriorityQueue: " + numbers);
}
}
// 출력
// PriorityQueue: [2, 4]
// Updated PriorityQueue: [1, 4, 2]
2. ArrayDeque
Deque이라는 자료구조는 Queue와 매우 매우 비슷한 성질을 갖고 있지만 약간 다릅니다. 간단하게 설명하면 Queue는 한 곳으로만 들어오고 한곳으로만 나가는 반면에 Deque은 rear와 front 모두 삽입, 삭제가 가능합니다. 위 그림에서 rear와 front라는 용어가 있는데 linear collection의 양 끝 부분을 의미합니다.
ArrayDeque는 Deque를 구현한 interface입니다. ArrayDeque는 양쪽으로 넣고 빼는 것이 가능한 큐 자료구조입니다.
import java.util.ArrayDeque;
class Main {
public static void main(String[] args) {
ArrayDeque<String> animals= new ArrayDeque<>();
// Using add()
animals.add("Dog");
// Using addFirst()
animals.addFirst("Cat");
// Using addLast()
animals.addLast("Horse");
System.out.println("ArrayDeque: " + animals);
}
}
// 출력
// ArrayDeque: [Cat, Dog, Horse]
Collection Framework - Set
HashSet, LinkedHashSet, TreeSet
Set은 집합이라고 부르며, 순서가 없고 원소의 중복을 허용하지 않는다는 특징이 있습니다.
1. HashSet
HashSet은 집합의 특징 때문에 중복을 허용하지 않고, 순서를 가지지 않으며, 일정하게 유지되지 않는 게 특징입니다.
HashSet은 null 요소 또한 허용합니다.
@Test
@DisplayName("Collection의 HashSet 테스트")
void hashSetTest() {
Set<Integer> hashSet = new HashSet<>();
hashSet.add(10);
hashSet.add(20);
hashSet.add(30);
hashSet.add(10); // 중복된 수 추가
assertEquals(3, hashSet.size());
assertEquals("[20, 10, 30]", hashSet.toString()); // 순서가 없음
}
// 출력
// 20
// 10
// 30
2. LinkedHashSet
집합의 특징은 중복을 허용하지 않고, 순서를 가지지 않는다는 특징이 있습니다.
그렇지만, LinkedHashSet은 중복은 허용하지 않지만 순서를 가진다는 특징이 있습니다.
void linkedHashSetTest() {
Set<Integer> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add(10);
linkedHashSet.add(20);
linkedHashSet.add(30);
linkedHashSet.add(10); // 중복된 수 추가
assertEquals(3, linkedHashSet.size()); // 들어온 순서대로, 순서를 가짐
Iterator<Integer> it = linkedHashSet.iterator();
while(it.hasNext())
System.out.println(it.next());
}
// 출력
// 10
// 20
// 30
3. TreeSet
TreeSet도 중복을 허용하지 않고, 순서를 가지지 않습니다. 하지만, 데이터를 정렬하여 저장하고 있다는 특징이 있습니다.
void treeSetTest() {
Set<Integer> treeSet = new TreeSet<>();
treeSet.add(20);
treeSet.add(1);
treeSet.add(100);
treeSet.add(33);
treeSet.add(1); // 중복 제거 됨
assertEquals(4, treeSet.size()); // 기본적으로는 오름차순으로 정렬 됨
Iterator<Integer> it = treeSet.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
}
// 출력
// 1
// 20
// 33
// 100
Map
HashMap, LinkedHashMap, HashTable, TreeMap
Map은 Key와 Value로 이루어진 자료구조입니다. Set과 같이 순서를 가지지 않습니다. Key 값은 중복될 수 없지만, Value는 중복될 수 있는 특징을 가지고 있습니다.
1. HashMap
HashMap은 일반적으로 많이 사용하는 Map 자료구조입니다.
HashMap은 Map 인터페이스를 구현한 대표적인 Map 컬렉션입니다. Map 인터페이스를 상속하고 있기에 Map의 성질을 그대로 가지고 있습니다.
Map은 키와 값으로 구성된 Entry객체를 저장하는 구조를 가지고 있는 자료구조입니다. 여기서 키와 값은 모두 객체입니다.
값은 중복 저장될 수 있지만 키는 중복 저장될 수 없습니다. 만약 기존에 저장된 키와 동일한 키로 값을 저장하면 기존의 값은 없어지고 새로운 값으로 대체됩니다.
HashMap은 이름 그대로 해싱(Hashing)을 사용하기 때문에 많은 양의 데이터를 검색하는 데 있어서 뛰어난 성능을 보입니다.
import java.util.HashMap;
class Main {
public static void main(String[] args) {
HashMap<Integer, String> languages = new HashMap<>();
languages.put(1, "Java");
languages.put(2, "Python");
languages.put(3, "JavaScript");
System.out.println("HashMap: " + languages);
// return set view of keys
// using keySet()
System.out.println("Keys: " + languages.keySet());
// return set view of values
// using values()
System.out.println("Values: " + languages.values());
// return set view of key/value pairs
// using entrySet()
System.out.println("Key/Value mappings: " + languages.entrySet());
}
}
// 출력
// HashMap: {1=Java, 2=Python, 3=JavaScript}
// Keys: [1, 2, 3]
// Values: [Java, Python, JavaScript]
// Key/Value mappings: [1=Java, 2=Python, 3=JavaScript]
2. LinkedHashMap
일반적으로 Map 자료구조는 순서를 가지지 않지만, LinkedHashMap은 들어온 순서대로 순서를 가집니다.
HashMap의 경우 단점이 하나 있습니다. 그 단점은 put을 통해 데이터나 객체를 넣을 때 key의 순서가 지켜지지 않는다는 것인데, 코드상으로 순차적으로 Key Value를 넣어도, 실제 HashMap에서는 해당 순서가 지켜지지 않는다는 점입니다.
만약 입력된 Key의 순서가 보장되어야 한다면 LinkedHashMap을 사용하면 됩니다.
LinkedHashMap은 put을 통해 입력된 순서대로 Key가 보장되므로 해당 문제를 해결할 수 있습니다. 사용법은 HashMap과 동일합니다.
import java.util.LinkedHashMap;
class Main {
public static void main(String[] args) {
// Creating LinkedHashMap of even numbers
LinkedHashMap<String, Integer> evenNumbers = new LinkedHashMap<>();
// Using put()
evenNumbers.put("Two", 2);
evenNumbers.put("Four", 4);
System.out.println("Original LinkedHashMap: " + evenNumbers);
// Using putIfAbsent()
evenNumbers.putIfAbsent("Six", 6);
System.out.println("Updated LinkedHashMap(): " + evenNumbers);
//Creating LinkedHashMap of numbers
LinkedHashMap<String, Integer> numbers = new LinkedHashMap<>();
numbers.put("One", 1);
// Using putAll()
numbers.putAll(evenNumbers);
System.out.println("New LinkedHashMap: " + numbers);
}
}
// 출력
// Original LinkedHashMap: {Two=2, Four=4}
// Updated LinkedHashMap: {Two=2, Four=4, Six=6}
// New LinkedHashMap: {One=1, Two=2, Four=4, Six=6}
3. HashTable
HashMap과 동일한 특징을 가지지만, 용도는 다릅니다.
HashTable은 키와 값을 1:1 형태로 가져가며 HashTable에 저장이 됩니다. 키는 값을 식별하기 위한 고유한 키, 값은 키가 가진 값을 의미합니다.
또한 HashMap과 반대로 동기화가 이루어집니다. HashMap에서는 값으로 null이 입력이 가능하지만, HashTable에서는 null 입력이 불가능합니다. 키는 중복이 안 되지만 값은 중복을 허용합니다.
import java.util.*;
class HashTable {
public static void main(String args[])
{
Hashtable<Integer, Integer>
ht = new Hashtable<Integer, Integer>();
ht.put(123, 432);
ht.put(12, 2345);
ht.put(15, 5643);
ht.put(3, 321);
ht.remove(12);
System.out.println(ht);
}
}
4. TreeMap
이진 검색 트리의 형태로 키와 값의 쌍으로 이루어진 데이터를 저장합니다.
Map의 장점인 빠른 검색과 Tree의 장점인 정렬과 범위 검색의 장점을 모두 갖고 있지만, 이진 검색 트리처럼 데이터를 저장할 때 정렬하기 때문에 저장시간이 길다는 단점이 있습니다.
정렬된 상태로 데이터를 조회하는 경우가 빈번하다면, 데이터를 조회할 때 정렬해야 하는 hashMap보다는 이미 정렬된 상태로 저장되어 있는 TreeMap이 빠른 조회 결과를 얻을 수 있습니다.
주로 HashMap을 사용하고, 정렬이나 범위 검색이 필요한 경우에만 TreeMap을 사용하는 것이 좋습니다.
import java.util.TreeMap;
class Main {
public static void main(String[] args) {
TreeMap<String, Integer> numbers = new TreeMap<>();
numbers.put("First", 1);
numbers.put("Second", 2);
numbers.put("Third", 3);
System.out.println("TreeMap: " + numbers);
// Using the firstKey() method
String firstKey = numbers.firstKey();
System.out.println("First Key: " + firstKey);
// Using the lastKey() method
String lastKey = numbers.lastKey();
System.out.println("Last Key: " + lastKey);
// Using firstEntry() method
System.out.println("First Entry: " + numbers.firstEntry());
// Using the lastEntry() method
System.out.println("Last Entry: " + numbers.lastEntry());
}
}
// 출력
// TreeMap: {First=1, Second=2, Third=3}
// First Key: First
// Last Key: Third
// First Entry: First=1
// Last Entry: Third=3
'JAVA' 카테고리의 다른 글
[JAVA] Get 방식과 Post 방식 (2) | 2022.06.03 |
---|---|
[JAVA] 현재 날짜, 시간 구하기 (2) | 2022.05.03 |
[JAVA] Garbage Collection의 개념과 동작 원리 (2) | 2022.03.28 |
[JAVA] Lambda Expression(람다식) (4) | 2022.03.15 |
[JAVA] JAVA 버전 별 특징(1 ~ 17 버전) (0) | 2022.03.15 |
댓글