Spring Boot๋ก ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ง๋ค๋ ๋๊ท๋ชจ ๋ฐฐ์น ์ฒ๋ฆฌ๋ฅผ ํ๋ ๊ฐ์ List, Map, Set๊ณผ ๊ฐ์ ์๋ฐ ์ปฌ๋ ์ ์ ๊ฑฐ์ ๋ชจ๋ ๋ถ๋ถ์์ ์ฌ์ฉ๋๋ค๊ณ ํด๋ ๊ณผ์ธ์ด ์๋๋ค. ์ด๋ฒ ํฌ์คํ ์์๋ ์๋ฐ Collection์ ๋ํด์ ์ ๋ฆฌํด๋ณด๊ณ ์ ํ๋ค.
Java Collection Framework(์ดํ JCF)๋ JDK 1.2๋ถํฐ ๋์
๋์ด ๋ฐ์ดํฐ๋ฅผ ๊ทธ๋ฃน์ผ๋ก ๋ค๋ฃจ๊ธฐ ์ํด ํ์คํ๋ ์ํคํ
์ฒ๋ฅผ ์ ๊ณตํ๋ค. ํ๋ ์์ํฌ๋ผ๊ณ ๋ถ๋ฆฌ๋ ์ด์ ๋ ๋ฐ๋ก ์ด ๋๋ฌธ์ด๋ค.
JCF๋ ํฌ๊ฒ ๋๊ฐ์ ๋ฟ๋ฆฌ๋ก ๋๋๋ค. ํ๋๋ ๊ฐ์ฒด์ ๊ทธ๋ฃน์ ๋ค๋ฃจ๋ java.util.Collection ์ธํฐํ์ด์ค์ด๊ณ ๋ค๋ฅธ ํ๋๋ ํค(Key)์ ๊ฐ(Value)๋ฅผ ๋ค๋ฃจ๋ java.util.Map ์ธํฐํ์ด์ค๋ค. Map์ ์๋ฐํ ๋งํด Collection์ ์์๋ฐ์ง ์์ง๋ง JCF์ ์ค์ํ ํ ์ถ์ ๋ด๋นํ๋ค.
Collection ์ธํฐํ์ด์ค
Collection ์ธํฐํ์ด์ค๋ ๋ชจ๋ ์ปฌ๋ ์ ์ด ๊ฐ์ถ์ด์ผ ํ ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ๋์์ ์ ์ํ๋ค. ์ด๋ฅผ ์์๋ฐ๋ ์ฃผ์ ์ธํฐํ์ด์ค๋ ๋ค์๊ณผ ๊ฐ๋ค.
- List(๋ฆฌ์คํธ): ์์๊ฐ ์๋ ๋ฐ์ดํฐ์ ์งํฉ์ด๋ค. ๋ฐ์ดํฐ์ ์ค๋ณต์ ํ์ฉํ๋ฉฐ, ์ธ๋ฑ์ค(Index)๋ฅผ ํตํด ๋ฐ์ดํฐ์ ์ ๊ทผํ ์ ์์ด ์ํ์ค๋ผ๊ณ ๋ ๋ถ๋ฆฐ๋ค. ๋ํ์ ์ธ ๊ตฌํ์ฒด๋ก ArrayList, LinkedList, Vector(๋ ๊ฑฐ์)๊ฐ ์๋ค.
- Set(์ ): ์ํ์ ๊ฐ๋ ์ ๋ชจ๋ธ๋งํ๋ค. ๋ฐ์ดํฐ์ ์ค๋ณต์ ํ์ฉํ์ง ์๋๋ค. ์์๋ฅผ ๋ณด์ฅํ์ง ์๋ ๊ฒ์ด ์ผ๋ฐ์ ์ด๋ ๊ตฌํ์ฒด์ ๋ฐ๋ผ ์์๋ฅผ ๋ณด์ฅํ๊ธฐ๋ ํ๋ค. ๋ํ์ ์ผ๋ก HashSet, TreeSet์ด ์๋ค.
- Queue(ํ): ์ฒ๋ฆฌ๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ ์์๋ฅผ ๋ณด๊ดํ๋ ์ฉ๋๋ก ์ค๊ณ๋์๋ค. ์ผ๋ฐ์ ์ผ๋ก FIFO(First-In-First-Out) ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง์ง๋ง, ์ฐ์ ์์ ํ(PriorityQueue)์ฒ๋ผ ์ฐ์ ์์์ ๋ฐ๋ผ ์์๊ฐ ๊ฒฐ์ ๋๊ธฐ๋ ํ๋ค.
- Deque(๋ฑ): ‘Double Ended Queue’์ ์ฝ์๋ก ์์ชฝ ๋์์ ์์๋ฅผ ์ถ๊ฐํ๊ฑฐ๋ ์ ๊ฑฐํ ์ ์๋ ์๋ฃ๊ตฌ์กฐ๋ค. Queue๋ฅผ ์์๋ฐ์์ง๋ง Queue์๋ ์ฐ์์๊ฐ ๊ฝค ๋ฌ๋ผ์ ๋ณ๋์ ๋ฟ๋ฆฌ๋ก ์ดํดํ๋ฉด ์ข๋ค. ์คํ(Stack)๊ณผ ํ(Queue)์ ๊ธฐ๋ฅ์ ๋ชจ๋ ์ํํ ์ ์์ด ํ์ฉ๋๊ฐ ๋๋ค.
| ์ธํฐํ์ด์ค | ์ญํ ๋ฐ ํน์ง | ์์(Order) | ์ค๋ณต |
| List | ์์๊ฐ ์๋ ์ ์ฅ์(๋ฐฐ์ด๊ณผ ์ ์ฌ) | ์ ๋ ฅ ์์ ๋ณด์ฅ | ํ์ฉ |
| Set | ์งํฉ (์์๋ณด๋ค ์ ๋ํฌํ ๊ฐ์ด ์ค์) | ์์ ์์ (LinkedHashSet, TreeSet์ ์์ธ) | ํ์ฉ์ํจ |
| Queue | ๋๊ธฐ์ด (FIFO) | ์ ๋ ฅ ์์ ๋ณด์ฅ | ํ์ฉ |
| Deque | ์์ชฝ ๋์ด ๋ซ๋ฆฐ ํ (Stack + Queue) | ์ ๋ ฅ ์์ ๋ณด์ฅ (์์ชฝ ๋์์ ์กฐ์ ๊ฐ๋ฅ) | ํ์ฉ |
Map ์ธํฐํ์ด์ค
Map์ ํค(Key)๋ฅผ ๊ฐ(Value)์ ๋งคํํ๋ ๊ฐ์ฒด๋ค. ํค๋ ์ค๋ณต๋ ์ ์์ผ๋ฉฐ ํ๋์ ํค๋ ํ๋์ ๊ฐ์๋ง ๋งคํ๋๋ค. Collection ์ธํฐํ์ด์ค๋ฅผ ์์๋ฐ์ง๋ ์์ง๋ง keySet(), values(), entrySet() ๋ฉ์๋๋ฅผ ํตํด ์ปฌ๋ ์ ๋ทฐ๋ฅผ ์ ๊ณตํจ์ผ๋ก์จ ์ปฌ๋ ์ ๊ณผ ์ ๊ธฐ์ ์ผ๋ก ์ฐ๊ฒฐ๋๋ค.
๋ ๊ฑฐ์ ์ปฌ๋ ์
์ด๊ธฐ๋ฒ์ (JDK 1.0)์์ ์ฌ์ฉ๋๋ Vector, Stack, Hashtable๊ณผ ๊ฐ์ ํด๋์ค๋ ์ฌ์ ํ ์กด์ฌํ๋ค. ํ์ง๋ง ์ด๋ค์ ๋ชจ๋ ๋ฉ์๋์ ๋๊ธฐํ(Synchronized)๊ฐ ๊ฑธ๋ ค ์์ด ๋ฉํฐ์ค๋ ๋ ํ๊ฒฝ์ด ์๋ ๊ณณ์์๋ ๋ถํ์ํ ์ฑ๋ฅ ์ ํ๋ฅผ ์ ๋ฐํ๋ค. ๋ฐ๋ผ์ ํ์ฌ๋ ๋ค์๊ณผ ๊ฐ์ด ๋์ฒดํ์ฌ ์ฌ์ฉํ๋ ๊ฒ์ด ๊ถ์ฅ๋๋ค.
| ๋ ๊ฑฐ์ | ๋์ฒดํด๋์ค | ์ค๋ช |
| Vector | ArrayList | ๋๊ธฐํ ์ ๊ฑฐ, ์ฑ๋ฅ ์ด์ |
| HashTable | HashMap | ๋๊ธฐํ ์ ๊ฑฐ, null ํค/๊ฐ ํ์ฉ |
| Stack | ArrayDeque | ์ผ๊ด๋ API, ์ฑ๋ฅ ์ฐ์ |
Vector -> ArrayList, HashTable -> HashMap, Stack -> ArrayDeque ๋์ฒด๋ ๋ถํ์ํ ๋๊ธฐํ ๋ก์ง์ ์ ๊ฑฐํจ์ผ๋ก์จ ์ฑ๋ฅ์์ ์ด์ ์ ๊ฐ์ ธ์ค์ง๋ง ์ค๋ ๋ ์์ ํ์ง ์๊ธฐ ๋๋ฌธ์ ๋ฉํฐ์ค๋ ๋ ํ๊ฒฝ์์ ๊ณต์ ํ๋ ๊ฒ์ ์ํํ๋ค.
์ด ๊ฒฝ์ฐ์๋ java.util.concurrent ํจํค์ง์ ํด๋์ค๋ค์ ์ฌ์ฉํด์ผ ํ๋ค.
List ์ธํฐํ์ด์ค
๊ฐ๋ฐ์๊ฐ ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉํ๋ List ์ธํฐํ์ด์ค์ ๊ตฌํ์ฒด์ธ ArrayList์ LinkedList๋ฅผ ๋น๊ตํ๋ ๊ฒ์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฑ๋ฅ์ ์ข์ฐํ๋ ์ค์ํ ๋ถ๋ถ์ด๋ค.
ArrayList
ArrayList๋ ์ด๋ฆ ๊ทธ๋๋ก ๋ฐฐ์ด(Array)์ ๊ธฐ๋ฐ์ผ๋ก ํ ๋ฆฌ์คํธ๋ค. ๋ด๋ถ์ ์ผ๋ก Object elementData๋ผ๋ ๋ฐฐ์ด์ ๊ฐ์ง๊ณ ๋ฐ์ดํฐ๋ฅผ ๊ด๋ฆฌํ๋ค.
์๋ฐ์ ๋ฐฐ์ด์ ์์ฑ์ ํฌ๊ธฐ๊ฐ ๊ณ ์ ๋์ง๋ง ArrayList๋ ๋์ ์ผ๋ก ํฌ๊ธฐ๋ฅผ ๋ณ๊ฒฝํ ์ ์๋ค. ์ฒ์ ์ค์ ํ capacity๊ฐ ์ฐจ๋ฉด ๋ ํฐ ๋ฐฐ์ด์ ์๋ก ๋ง๋ค๊ณ ๊ธฐ์กด ๋ฐ์ดํฐ๋ฅผ ๋ณต์ฌํ๋ ์๋ฆฌ๋ค. ์ด ๋ ์๋ก์ด ๋ฐฐ์ด์ ์ผ๋ง๋ ๋ ํฌ๊ฒ ๋ง๋ค๊ฒ์ธ๊ฐ๊ฐ ์ค์ํ๋ฐ JDK์ ArrayList๋ ๊ธฐ๋ณธ์ ์ผ๋ก 1.5๋ฐฐ(50%)์ฉ ํฌ๊ธฐ๋ฅผ ๋๋ฆฐ๋ค.
old Capacity + (oldCapacity >> 1)JavaArrayList์ ๊ฐ์ฅ ํฐ ์ฅ์ ์ ์ธ๋ฑ์ค๋ฅผ ํตํ ์กฐํ ์๋๊ฐ O(1)์ด๋ผ๋ ์ ์ด๋ค. ๋ฐฐ์ด์ ๋ฉ๋ชจ๋ฆฌ ์์์ ์ฐ์์ ์ผ๋ก ์์นํ๊ธฐ ๋๋ฌธ์ ์ธ๋ฑ์ค์ ์ฃผ์๋ฅผ ์์ ์ฃผ์ + (์ธ๋ฑ์ค * ๋ฐ์ดํฐ ํฌ๊ธฐ)๋ผ๋ ๋จ์ํ ์์์ผ๋ก ์์น๋ฅผ ๋ฐ๋ก ์ฐพ์ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค. ์ด๋ ๋ฐ์ดํฐ๊ฐ 100๊ฐ๋ 100๋ง๊ฐ๋ ์กฐํ ์๋๊ฐ ๋์ผํจ์ ์๋ฏธํ๋ค.
LinkedList
LinkedList๋ ๋ชจ๋ ๋
ธ๋๋ค์ด ์๋ก ์ฐ๊ฒฐ๋ ๊ตฌ์กฐ๋ค. ๊ฐ ๋
ธ๋๋ ๋ฐ์ดํฐ์ ๋ค์ ๋
ธ๋์ ์ฃผ์(next), ์ด์ ๋
ธ๋์ ์ฃผ์(prev)๋ฅผ ๊ฐ์ง๊ณ ์๋ค. (Doublely Linked List)
LinkedList๋ ์ค๊ฐ์ ์ฝ์
/์ญ์ ๊ฐ ๋น ๋ฅด๋ค๋ ์ฅ์ ์ด ์๋ค. ArrayList๋ ์ค๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ๋ฃ์ผ๋ ค๋ฉด ๋ค์ ๋ฐ์ดํฐ๋ค์ ํ์นธ์ฉ ๋ฐ์ด์ผ ํด์ O(n)์ด ๊ฑธ๋ฆฌ์ง๋ง LinkedList๋ ์๋ค ๋
ธ๋์ ์ฐ๊ฒฐ ๊ณ ๋ฆฌ๋ง ๋ฐ๊ฟ์ฃผ๋ฉด ๋๋ฏ๋ก O(1)์ด ๊ฑธ๋ฆฐ๋ค.
ํ์ง๋ง ์ฌ๊ธฐ์๋ ํจ์ ์ด ์๋ค.
ํ๋์ ์ปดํจํฐ ์ํคํ
์ฒ์์ CPU๋ ๋ฉ๋ชจ๋ฆฌ(RAM)์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ๋ ํ์ํ ๋ฐ์ดํฐ ํ๋๋ง ๊ฐ์ ธ์ค์ง ์๊ณ ์บ์ ๋ผ์ธ(Cache Line) ์ด๋ผ๋ ๋จ์(๋ณดํต 64๋ฐ์ดํธ)๋ก ์ฃผ๋ณ ๋ฐ์ดํฐ๊น์ง ๋ฉ์ด๋ฆฌ๋ก ๊ฐ์ ธ์ CPU ๋ด๋ถ์ ์บ์์ ์ ์ฅํ๋ค. (Cache Locality)
- ArrayList์ ๊ฒฝ์ฐ: ๋ฐ์ดํฐ๊ฐ ๋ฉ๋ชจ๋ฆฌ์ ์ฐ์์ ์ผ๋ก ์ ์ฅ๋์ด ์๊ธฐ ๋๋ฌธ์ ๊ทธ ๋ค์ ์์๋ค๋ ํจ๊ป ์บ์๋ก ๋ธ๋ ค์จ๋ค. ์์ฐจ์ ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ฝ์ ๋ ์ด๋ฏธ ๋ฐ์ดํฐ๊ฐ ์บ์์ ์์ ํ๋ฅ ์ด ๋๊ธฐ ๋๋ฌธ์ ๋น ๋ฅด๋ค.
- LinkedList์ ๊ฒฝ์ฐ: ๋ฐ์ดํฐ(๋ ธ๋)๊ฐ ํ ๋ฉ๋ชจ๋ฆฌ์ ์ด๊ณณ์ ๊ณณ์ ํฉ์ด์ ธ ์๊ธฐ ๋๋ฌธ์ ๋ ผ๋ฆฌ์ ์ผ๋ก๋ ์ฐ๊ฒฐ๋์ด ์์ง๋ง ๋ฌผ๋ฆฌ์ ์ผ๋ก๋ ๋ฉ๋ฆฌ ๋จ์ด์ ธ ์์ ์ ์๋ค. ๋ฐ๋ผ์ ๋ค์ ๋ ธ๋๋ก ์ด๋ํ ๋๋ง๋ค CPU๋ ์บ์์ ์๋ ๋ฐ์ดํฐ๋ฅผ ๋ฉ๋ชจ๋ฆฌ์์ ์๋ก ๊ฐ์ ธ์์ผ ํ๊ธฐ ๋๋ฌธ์ ์ด๋ก ์ธํด CPU ํ์ดํ๋ผ์ธ์ด ๋ฉ์ถ๊ณ ์ฑ๋ฅ์ด ์ ํ๋ ์ ์๋ค.
์ค์ ๋ฒค์น๋งํฌ๋ฅผ ๋๋ ค๋ณด๋ฉด ์์์ ์ฝ์ /์ญ์ ์กฐ์ฐจ๋ ๋ฆฌ์คํธ์ ํฌ๊ธฐ๊ฐ ์์ฒญ๋๊ฒ ํฌ์ง ์์ ์ด์ ArrayList๊ฐ LinkedList๋ณด๋ค ๋น ๋ฅธ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค. LinkedList์์ ์ฝ์ ์์น๋ฅผ ์ฐพ๊ธฐ ์ํด ํ์ํ๋ ๊ณผ์ ์์ฒด๊ฐ O(n)์ด๋ฉฐ ์ด ํ์ ๊ณผ์ ์์ ์บ์ ํจ์จ์ด ๋์๊ธฐ ๋๋ฌธ์ด๋ค. ๋ฐ๋ผ์ ํน๋ณํ ์ด์ ๊ฐ ์๋ค๋ฉด ํญ์ ArrayList๋ฅผ ๊ธฐ๋ณธ์ ์ผ๋ก ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค.
์ ๋ด์ฉ์ ์๋ ๋งํฌ๋ค์ ์ฐธ๊ณ ํ๊ธฐ ๋ฐ๋๋ค.
https://stackoverflow.com/questions/79594341/arraylist-vs-linkedlist-in-terms-of-cache-locality
https://www.geeksforgeeks.org/dsa/why-arrays-have-better-cache-locality-than-linked-list/
https://can-ozkan.medium.com/arraylist-vs-linkedlist-88022088fdef
Map๊ณผ Set์ ๋ด๋ถ
๋ฐ์ดํฐ์ ์ ์ผ์ฑ์ ๋ณด์ฅํ๋ Set๊ณผ ํค-๊ฐ์์ ๊ด๋ฆฌํ๋ Map์ ๋ด๋ถ์ ์ผ๋ก ์ ์ฌํ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๋ค. ์ฌ์ค HashSet์ ๋ด๋ถ์ ์ผ๋ก HashMap์ ์ฌ์ฉํ์ฌ ๊ตฌํ๋์ด ์๊ณ Set์ ๊ฐ์ HashMap์ ํค๋ก ์ ์ฅ๋๊ณ HashMap์ ๊ฐ์ PRESENT๋ผ๋ ๋๋น ๊ฐ์ฒด๊ฐ ๋ค์ด๊ฐ๋ค. ๋ค์์ HashSet ํด๋์ค์ ๋ฉค๋ฒ๋ณ์ ์ ์ธ๋ถ๋ถ์ด๋ค.
private transient HashMap<E,Object> map;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();JavaHashMap์ ์งํ: LinkedList์์ Red-Black ํธ๋ฆฌ๋ก
HashMap์ ํด์ ๋ฒํท(Hash Bucket)์ด๋ผ๋ ๋ฐฐ์ด์ ์ฌ์ฉํ๋ค. ํด์ ๋ฒํท์ ํด์ ํ ์ด๋ธ(HashMap)์ ๊ฐ์ฅ ๊ธฐ๋ณธ์ด ๋๋ ์ ์ฅ์ ๋จ์๋ฅผ ๋งํ๋ค. ์ฝ๊ฒ ๋งํด ๋ฐ์ดํฐ๊ฐ ์ค์ ๋ก ๋ด๊ธฐ๋ ์๋์ด๋ผ๊ณ ์๊ฐํ๋ฉด ๋๋ค. HashMap์ ๋ด๋ถ์ ์ผ๋ก ๊ฑฐ๋ํ ๋ฐฐ์ด์ ํ๋ ๊ฐ์ง๊ณ ์๋๋ฐ ์ด ๋ฐฐ์ด์ ๊ฐ ์นธ(์ธ๋ฑ์ค) ํ๋ํ๋๊ฐ ๋ฐ๋ก ๋ฒํท์ด๋ค. ์ด ๋ฒํท ์์์ ์ด๋ค ์ผ์ด ๋ฒ์ด์ง๋์ง ํนํ ํด์ ์ถฉ๋์ด ๋ฐ์ํ์ ๋ ์๋ฐ๊ฐ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ๋์ง ์๋ ๊ฒ์ด ์ค์ํ๋ค.
ํด์ ๋ฒํท(Hash Bucket)์ ๊ธฐ๋ณธ๊ตฌ์กฐ
map.put(“key”, “value”)๋ฅผ ํธ์ถํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ์ผ์ด ์ผ์ด๋๋ค.
- ํด์ ๊ณ์ฐ: ํค(“key”)์ ํด์์ฝ๋๋ฅผ ๊ตฌํ๋ค.
- ์ฃผ์ ๋ฐฐ์ : ๊ทธ ์ซ์๋ฅผ ๋ฐฐ์ด์ ํฌ๊ธฐ๋ก ๋๋ ๋๋จธ์ง๋ฅผ ๊ตฌํด ๋ช ๋ฒ์งธ ๋ฒํท์ ๋ฃ์์ง ์ ํ๋ค. (์: 3๋ฒ ๋ฒํท)
- ์ ์ฅ: 3๋ฒ์งธ ๋ฒํท์ ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ๋ฃ๋๋ค. ์ด๋ 3๋ฒ ๋ฒํท์ด ๋น์ด์๋ค๋ฉด ๋ฌธ์ ์์ง๋ง ์ด๋ฏธ ๋ฐ์ดํฐ๊ฐ ์กด์ฌํ๋ ๊ฒฝ์ฐ ์ถฉ๋์ด ๋ฐ์ํ๋ค.
ํด์ ์ถฉ๋
์๋ก ๋ค๋ฅธ ํค์ธ๋ฐ ๊ณ์ฐ๋ ํด์ ๊ฐ์ด ๊ฐ์ ์ฃผ์๊ฐ ๊ฐ์ ์ ์๋ค. ์ด๋ฅผ ํด์ ์ถฉ๋์ด๋ผ๊ณ ํ๋ค. ์ด ๋ ๋ฒํท์ ๋จ์ํ ๋ณ์์์ ์๋ฃ๊ตฌ์กฐ๋ก ๋ณ์ ํ๋ค.
์๋ฐ(HashMap)์์๋ Seperate Chaining ๋ฐฉ์์ ์ฌ์ฉํ๋๋ฐ ๋ฒํท ๋ด๋ถ ๊ตฌ์กฐ๋ ๋ฐ์ดํฐ ์์ ๋ฐ๋ผ ๋ ๊ฐ์ง๋ก ๋๋๋ค.
LinkedList (๋ฐ์ดํฐ๊ฐ ์ ์ ๋)
ํ ๋ฒํท์ ๋ฐ์ดํฐ๊ฐ ๊ฒน์น๋ฉด LinkedList๋ก ์ฎ์ด์ ์ ์ฅํ๋ค.
- ๊ตฌ์กฐ: A -> B -> C
- ์กฐํ ์๋: O(n) (์ต์ ์ ๊ฒฝ์ฐ ๋ฆฌ์คํธ ๋๊น์ง ๋ค ๋ค์ ธ์ผํจ)
- ์ฌ์ฉ ์์ : ๋ฒํท ํ๋์ ๋ฐ์ดํฐ๊ฐ 8๊ฐ ๋ฏธ๋ง์ผ ๋
Red-Black ํธ๋ฆฌ (๋ฐ์ดํฐ๊ฐ ๋ง์ ๋)
์๋ฐ 8์์ ๋์ ๋ ์ฑ๋ฅ ์ต์ ํ๋ก ๊ฐ์ ๋ ๋ฐฉ์์ด๋ค. ํ ๋ฒํท์ ๋ฐ์ดํฐ๊ฐ ๋๋ฌด ๋ง์ด ์์ด๋ฉด(8๊ฐ ์ด์) ์กฐํ ์๋๊ฐ ๋๋ ค์ง๋ LinkedList๋ฅผ ๋ฒ๋ฆฌ๊ณ ํธ๋ฆฌ(Tree)๊ตฌ์กฐ๋ก ๋ฆฌ๋ชจ๋ธ๋ง ํ๋ค.
- ๊ตฌ์กฐ: ๊ท ํ์กํ ์ด์งํธ๋ฆฌ (์ ๋ ฌ๋ ์ํ ์ ์ง)
- ์กฐํ ์๋: O(log N) (๋ฐ์ดํฐ๊ฐ ์๋ฌด๋ฆฌ ๋ง์๋ ํจ์ฌ ๋น ๋ฅด๋ค)
- ์ฌ์ฉ ์์ : ๋ฒํท ํ๋์ ๋ฐ์ดํฐ๊ฐ 8๊ฐ ์ด์ ์์ผ ๋ (๋ค์ 6๊ฐ ์ดํ๋ก ์ค์ด๋ค๋ฉด ๋ฆฌ์คํธ๋ก ๋์๊ฐ๋ค.)
HashMap ํด๋์ค์ Threshold ์์๊ฐ์ ๋ค์๊ณผ ๊ฐ์ด ์ ์ ๋์ด ์๋ค.
/**
* The bin count threshold for using a tree rather than list for a
* bin. Bins are converted to trees when adding an element to a
* bin with at least this many nodes. The value must be greater
* than 2 and should be at least 8 to mesh with assumptions in
* tree removal about conversion back to plain bins upon
* shrinkage.
*/
static final int TREEIFY_THRESHOLD = 8;
/**
* The bin count threshold for untreeifying a (split) bin during a
* resize operation. Should be less than TREEIFY_THRESHOLD, and at
* most 6 to mesh with shrinkage detection under removal.
*/
static final int UNTREEIFY_THRESHOLD = 6;Java์ด๋ฌํ ๊ตฌ์กฐ ๋๋ถ์ HashMap์ ๋ฐ์ดํฐ๊ฐ ์๋ฌด๋ฆฌ ๋ง์ด ๋ค์ด์๋ ๊ฝค ๊ด์ฐฎ์ ๊ฒ์ ์๋๋ฅผ ์ ์งํ ์ ์๋ค.

LinkedHashMap / LinedkHashSet
LinkedHashMap๊ณผ LinkedHashSet์ ๊ธฐ์กด HashMap ๊ธฐ๋ฅ์ ์ด์ค ์ฐ๊ฒฐ ๋ฆฌ์คํธ๋ฅผ ์ฌ์ฉํ๋ค๋ ๊ฒ์ด๋ค.
๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ ๋ฐฉ์์ ํด์ ํ
์ด๋ธ(Hash Table)์ด์ง๋ง ๋ฐ์ดํฐ ๋ผ๋ฆฌ์ ์์๋ link๋ก ๋ฌถ์ด์ ์์๋ฅผ ๊ธฐ์ตํ๋๋ก ํ๋ ๊ตฌ์กฐ๋ค.
LinkedHashMap์ ๋ ธ๋ ๊ตฌ์กฐ
์ผ๋ฐ์ ์ธ HashMap์ ๋ ธ๋๋ 4๊ฐ์ ํ๋๋ฅผ ๊ฐ์ง๋ค. ํ์ง๋ง LinkedHashMap์ ์์๋ฅผ ๊ธฐ์ตํ๊ธฐ ์ํด์ 2๊ฐ์ ํ๋๋ฅผ ๋ ๊ฐ์ง๋ค.
- HashMap Node: Hash, Key, Value, Next
- LinkedHashMap Entry: HashMap Node + Before, After
Before์ After๊ฐ ๋ฐ๋ก ‘๋๋ณด๋ค ๋จผ์ ๋ค์ด์จ ๋’, ‘๋ ๋ค์์ ๋ค์ด์จ ๋’์ ๊ฐ๋ฆฌํค๋ ๋งํฌ ์ญํ ์ ํ๋ค.
LinkedHashMap์ ๋ด๋ถ ๊ตฌํ์ ๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ ์๋์ด ์๋ค.
Node์ Next๋ ๋์ผ ๋ฒํท์์ ์ฌ๋ฌ ๋
ธ๋๊ฐ ์์ ๋(LinkedList ํน์ RB Tree) ํด๋น ๋ฒํท ๋ด์์์ ๋งํฌ๋ฅผ ์๋ฏธํ๊ณ Before, After๋ ๋ฒํท๊ณผ ์๊ด์์ด ์ด์ ๊ณผ ์ดํ์ ์
๋ ฅ๋ ๋
ธ๋๋ฅผ ๊ฐ๋ฅดํจ๋ค.
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}JavaLinkedHashMap์ ๋ค์ ๋ ๊ฐ์ง ๋ฐฉ์์ ์ฌ์ฉํ๋ค.
- ๊ฒ์ (Hash Table) – HashMap๊ณผ ๋์ผ
- Key์ ํด์ ๊ฐ์ ๊ณ์ฐํด์ ๋ฒํท(๋ฐฐ์ด)์ ์์น๋ฅผ ์ฐพ๋๋ค.
- ๋ฒํท์ ์์น๋ฅผ ๋ฐ๋ก ์ฐพ์ผ๋ฏ๋ก O(1)์ ์ฑ๋ฅ์ ๊ฐ๋๋ค. (๋ฒํท ์ถฉ๋์ด ๋ฐ์ํ ๊ฒฝ์ฐ๋ O(log n))
- HashMap๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก ์ถฉ๋์ด ๋๋ฉด Next๋ก ์ฐ๊ฒฐ๋๊ฑฐ๋ Red-Black Tree๊ฐ ๋๋ค.
- ์์ ๋ณด์ฅ (Doubly Linked List)
- ๋ชจ๋ ๋ฐ์ดํฐ(๋ ธ๋)๋ค์ด ์ ๋ ฅ๋ ์์๋๋ก Head ๋ถํฐ Tail๊น์ง ํ๋์ ๊ธด ์ค๋ก ์ฐ๊ฒฐ๋์ด ์๋ค.
- ์ ์ฒด๋ฅผ ์ํ(Iteration)ํ ๋๋ ์ด ๊ธธ์ ๋ฐ๋ผ๊ฐ์ผ๋ก์จ ์ ๋ ฅ ์์๋ฅผ ๋ณด์ฅํ๋ค.

LinkedHashSet
HashSet์ด HashMap์ ๊ป๋ฐ๊ธฐ์๋ ๊ฒ์ฒ๋ผ LinkedHashSet์ LinkedHashMap์ ๊ป๋ฐ๊ธฐ ์ญํ ์ ํ๋ค.
- ๋ด๋ถ์ ์ผ๋ก LinkedHashMap์ ์์ฑํด์ ์ฌ์ฉํ๋ค.
- ๋ฐ์ดํฐ๋ Key์ ์ ์ฅํ๊ณ Value๋ ๋๋ฏธ ๊ฐ์ฒด(PRESENT)๋ฅผ ๋ฃ๋ ๋ฐฉ์์ด ์์ ํ ๋์ผํ๋ค.
- ๊ตฌ์กฐ์ ์ผ๋ก LinkedHashMap๊ณผ ๋์ผํ๋ค.
- ์ ๋ ฅ ์์๋ฅผ ์ ์งํ๊ณ ์ค๋ณต์ ๋ฐฉ์งํ๊ณ ์ ํ๋ ๊ฒฝ์ฐ ์ฌ์ฉํ๋ค.
TreeMap / TreeSet
TreeMap๊ณผ TreeSet์ ์ด๋ฆ์์ ์ ์ ์๋ฏ์ด ๋ด๋ถ์ ์ผ๋ก ์ฒ์๋ถํฐ ๋๊น์ง Red-Black Tree๋ก๋ง ์ด๋ฃจ์ด์ ธ ์๋ค. TreeMap์ ๋ฒํท์ด๋ ํด์ ํจ์ ๊ฐ์๊ฑด ์์ ์๊ณ ๋ชจ๋ ๋ฐ์ดํฐ๊ฐ ํ๋์ ๊ฑฐ๋ํ Red-Black Tree๋ก ์ ์ฅ๋๋ค.
TreeMap์ ํค๋ฅผ ๊ธฐ์ค์ผ๋ก ๋น๊ต ์ฐ์ฐ์๋ฅผ ํตํด์ ์ ๋ ฌ๋์ด ์ ์ฅ๋๋ค.
๋ด๋ถ๊ตฌ์กฐ
TreeMap์ ๋ด๋ถ๋ ๋ฐฐ์ด ์์ด ๋ฃจํธ ๋ ธ๋ ํ๋์์ ์์ํด์ ๋ชจ๋ ๋ฐ์ดํฐ๊ฐ ๊ฐ์ง์น๊ธฐํ๋ฏ ์ฐ๊ฒฐ๋ ๊ตฌ์กฐ๋ค.
- ์ ์ฅ ๋ฐฉ์: ๋ฐ์ดํฐ๋ฅผ ๋ฃ์ ๋๋ง๋ค ์ ๋ ฌ ๊ธฐ์ค(ํฌ๊ธฐ)์ ๋ง์ถฐ์ ์ผ์ชฝ(์์๊ฐ)์ด๋ ์ค๋ฅธ์ชฝ(ํฐ๊ฐ) ์์์ผ๋ก ์ฐพ์ ๊ฐ๋ค.
- ๊ท ํ ์ ์ง: ๋ฐ์ดํฐ๊ฐ ๋ค์ด์ค๋ฉด ์ฆ์ ์๊น์ ์น ํ๊ณ ํ์ (Rotation)ํด์ ํธ๋ฆฌ์ ๋์ด๋ฅผ logN์ผ๋ก ๋ง์ถ๋ค.
- TreeSet: ์ญ์ HashSet๊ณผ ๊ฐ์ด ๊ป๋ฐ๊ธฐ์ผ ๋ฟ์ด๊ณ ๋ด๋ถ๋ TreeMap์ ์ฌ์ฉํ๋ค.
ํต์ฌ ์ฐจ์ด: ํด์ vs ๋น๊ต
TreeMap์ hashCode()์ equals()๋ฅผ ์ ํ ์ฌ์ฉํ์ง ์๋๋ค.
| ๊ตฌ๋ถ | HashMap / HashSet | TreeMap / TreeSet |
| ์๋ณ ๋๊ตฌ | hashCode(), equals() | compareTo() (๋๋ Comparator) |
| ์ ์ฅ ์์น | ํด์ ๊ฐ์ผ๋ก ๊ณ์ฐ๋ ๋ฒํท ์ฃผ์ | ํฌ๊ธฐ ๋น๊ต๋ฅผ ํตํด ๊ฒฐ์ ๋ ํธ๋ฆฌ ์์น |
| ์ ๋ ฌ | ๋ถ๊ฐ๋ฅ (๋ค์ฃฝ๋ฐ์ฃฝ) | ์๋ ์ ๋ ฌ(์ค๋ฆ์ฐจ์ ๊ธฐ๋ณธ) |
| Null Key | 1๊ฐ ํ์ฉ | ๋ถ๊ฐ (๋น๊ตํด์ผ ํ๋๋ฐ null์ด๋ฉด ์๋ฌ) |
TreeMap์ ๊ฐ์ฒด๋ฅผ ๋ฃ์ผ๋ ค๋ฉด ๊ทธ ๊ฐ์ฒด๋ ๋ฐ๋์ Comparable ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๊ณ ์์ด์ผ ํ๋ค.
(์๋๋ฉด ์์ฑ์์ ๋ณ๋์ Comparator๋ฅผ ๋ฃ์ด์ค์ผ ํ๋ค)
๊ทธ๋ ์ง ์์ผ๋ฉด ์ ๋ ฌ ๋ฐฉ๋ฒ์ ์ ์ ์์ด ์๋ฌ(ClassCastException)๊ฐ ๋ฐ์ํ๋ค.
TreeMap์ ์ธ์ ์ฌ์ฉํ๋?
๋จ์ํ ๋ฐ์ดํฐ ์ ์ฅ์ด ๋ชฉ์ ์ด๋ผ๋ฉด ๋ฌด์กฐ๊ฑด HashMap์ด ๋ซ๋ค. ํ์ง๋ง ์์๋ ๋ฒ์๊ฐ ํ์ํ ๋๋ TreeMap์ด ์๋์ ์ด๋ค.
- ์ ๋ ฌ๋ ๋ฐ์ดํฐ๊ฐ ํ์ํ ๋
- ๋ฐ์ดํฐ๋ฅผ ๋ฃ๊ธฐ๋ง ํ๋ฉด ์์์ ๊ฐ๋๋ค์(ํน์ ์ง์ ํ ์์)์ผ๋ก ์ ๋ ฌ๋๋ค. Collections.sort()๋ฅผ ํธ์ถํ ํ์๊ฐ ์๋ค.
- ๋ฒ์ ๊ฒ์(Range Search)
- “20๋ ํ์๋ง ๊ฐ์ ธ์”, “์ด๋ฆ์ด ‘๊น’์ผ๋ก ์์ํ๋ ์ฌ๋๋ง ์ฐพ์์ค” ๊ฐ์ ์ฟผ๋ฆฌ์ ์ต์ ํ ๋์ด ์๋ค.
- ๋ฉ์๋: subMap(), headMap(), tailMap()
- ๊ฐ๊น์ด ๊ฐ ์ฐพ๊ธฐ
- ์ ํํ 100์ ์๋๋ฐ 100์ด๋ ๊ฐ์ฅ ๊ฐ๊น์ด ํฐ ์๋ ๋ญ๊ฐ?
- ๋ฉ์๋: ceilingEntry() (ํฌ๊ฑฐ๋ ๊ฐ์), floorEntry() (์๊ฑฐ๋ ๊ฐ์), higherEntry(), lowerEntry()
์ํ ์ฝ๋
๋ค์์ TreeMap์ ์ฌ์ฉํ๋ ์ํ ์ฝ๋๋ค. ์ ๋ ฌ, ๋ฒ์ ์ถ์ถ๋ฑ๊ณผ ๊ฐ์ ์์ ์ ์์ฝ๊ฒ ํ ์ ์๋ค.
@Test
void treeMapTest() {
// 1. TreeMap ์์ฑ (๊ธฐ๋ณธ์ Key ๊ธฐ์ค ์ค๋ฆ์ฐจ์ ์ ๋ ฌ)
// ์ญ์(ํฐ ์ ์๊ฐ ๋จผ์ )์ผ๋ก ํ๋ ค๋ฉด new TreeMap<>(Collections.reverseOrder()) ์ฌ์ฉ
TreeMap<Integer, String> ranking = new TreeMap<>();
// 2. ๋ฐ์ดํฐ ๋ฌด์์ ์
๋ ฅ (๋ฃ๋ ์์๋ ์๊ด์์, ์์์ ์ ๋ ฌ๋จ)
ranking.put(50, "ํ์ด");
ranking.put(95, "์ฒ ์");
ranking.put(10, "๋งน๊ตฌ");
ranking.put(88, "์ํฌ");
ranking.put(72, "์งฑ๊ตฌ");
System.out.println("=== 1. ์ ์ฒด ๋ญํน (์๋ ์ ๋ ฌ๋จ) ===");
// HashMap์ด์๋ค๋ฉด ๋ค์ฃฝ๋ฐ์ฃฝ ๋์๊ฒ ์ง๋ง, ์ฌ๊ธด ์ ์์์ผ๋ก ๋์ด
ranking.forEach((score, name) ->
System.out.println(score + "์ : " + name)
);
System.out.println("\n=== 2. ํน์ ์ ์ ๊ตฌ๊ฐ ๊ฒ์ (subMap) ===");
// subMap(์์์ , ํฌํจ์ฌ๋ถ, ๋์ , ํฌํจ์ฌ๋ถ)
// 70์ ์ด์ ~ 90์ ๋ฏธ๋ง์ธ ์ ์ ๋ง ์ ๋ฝ์๋
NavigableMap<Integer, String> silverTier = ranking.subMap(70, true, 90, false);
System.out.println("์ค๋ฒ ํฐ์ด(70~89์ ): " + silverTier);
System.out.println("\n=== 3. ๊ทผ์ ๊ฐ ์ฐพ๊ธฐ (๊ฐ์ฅ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ!) ===");
int myScore = 80;
// floorEntry: 80์ ๊ณผ ๊ฐ๊ฑฐ๋, ์์ผ๋ฉด ๋ฐ๋ก ์๋ ์ ์ (๋ด๋ฆผ)
Map.Entry<Integer, String> lower = ranking.floorEntry(myScore);
// ceilingEntry: 80์ ๊ณผ ๊ฐ๊ฑฐ๋, ์์ผ๋ฉด ๋ฐ๋ก ์ ์ ์ (์ฌ๋ฆผ)
Map.Entry<Integer, String> higher = ranking.ceilingEntry(myScore);
System.out.println("๋ด ์ ์(" + myScore + ") ๋ฐ๋ก ์๋ ๋ผ์ด๋ฒ: " + lower.getKey() + "์ (" + lower.getValue() + ")");
System.out.println("๋ด ์ ์(" + myScore + ") ๋ฐ๋ก ์ ๋ผ์ด๋ฒ: " + higher.getKey() + "์ (" + higher.getValue() + ")");
System.out.println("\n=== 4. 1๋ฑ๊ณผ ๊ผด์ฐ ๊ตฌํ๊ธฐ ===");
System.out.println("1๋ฑ(์ต๊ณ ์ ): " + ranking.lastEntry()); // Key๊ฐ ๊ฐ์ฅ ํฐ ๋
System.out.println("๊ผด์ฐ(์ต์ ์ ): " + ranking.firstEntry()); // Key๊ฐ ๊ฐ์ฅ ์์ ๋
}Java์คํ๊ฒฐ๊ณผ
=== 1. ์ ์ฒด ๋ญํน (์๋ ์ ๋ ฌ๋จ) ===
10์ : ๋งน๊ตฌ
50์ : ํ์ด
72์ : ์งฑ๊ตฌ
88์ : ์ํฌ
95์ : ์ฒ ์
=== 2. ํน์ ์ ์ ๊ตฌ๊ฐ ๊ฒ์ (subMap) ===
์ค๋ฒ ํฐ์ด(70~89์ ): {72=์งฑ๊ตฌ, 88=์ํฌ}
=== 3. ๊ทผ์ ๊ฐ ์ฐพ๊ธฐ (๊ฐ์ฅ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ!) ===
๋ด ์ ์(80) ๋ฐ๋ก ์๋ ๋ผ์ด๋ฒ: 72์ (์งฑ๊ตฌ)
๋ด ์ ์(80) ๋ฐ๋ก ์ ๋ผ์ด๋ฒ: 88์ (์ํฌ)
=== 4. 1๋ฑ๊ณผ ๊ผด์ฐ ๊ตฌํ๊ธฐ ===
1๋ฑ(์ต๊ณ ์ ): 95=์ฒ ์
๊ผด์ฐ(์ต์ ์ ): 10=๋งน๊ตฌPlaintextKey ํ์ ์ด Integer ํ์ ์ธ๋ฐ Integer๋ Comparable<Integer>๋ฅผ ๊ตฌํํ๊ณ ์๋ค.
Set/Map ๊ตฌํ์ฒด ๋น๊ต ๋ฐ ์ฑ๋ฅ (Big-O) – Java8 ์ด์ ๊ธฐ์ค
| ๊ตฌํ์ฒด | ๊ธฐ๋ฐ์๋ฃ๊ตฌ์กฐ | ์กฐํ | ์ถ๊ฐ | ์ญ์ | ํน์ง |
| HashSet/ HashMap | Hash Table (+RB Tree) | O(1) ~ O(log n) | O(1) ~ O(log n) | O(1) ~ O(log n) | ์์๋ณด์ฅ X ๊ฐ์ฅ ๋น ๋ฆ |
| LinkedHashSet/ LinkedHashMap | HashTable + LinkedList | O(1) ~ O(log n) | O(1) ~ O(log n) | O(1) ~ O(log n) | ์ ๋ ฅ์์(Insert Order) ๋ณด์ฅ |
| TreeSet/ TreeMap | Red-Black Tree | O(log n) | O(log n) | O(log n) | ํค ๊ธฐ์ค ์ ๋ ฌ ์ํ ์ ์ง |
Java 21 Sequenced Collections (์ํ์ค ์ปฌ๋ ์ )
Java 21 (JEP 431)์์๋ ์ปฌ๋ ์ ํ๋ ์์ํฌ์ ๊ฐ์ฅ ํฐ ๊ตฌ์กฐ์ ๋ณํ๊ฐ ์ผ์ด๋ฌ๋ค. ๋ฐ๋ก Sequenced Collections์ ๋์ ์ด๋ค.
์์๋ ์๋๋ฐ ๊ณตํต ์ธํฐํ์ด์ค๊ฐ ์๋ค
์ด์ ๊น์ง ์๋ฐ์๋ ์์๊ฐ ์๋ ์ปฌ๋ ์ ์ ์์ฐ๋ฅด๋ ์์ ํ์ ์ด ์์๋ค.
- List: ์์ ์์ (์ธ๋ฑ์ค ์ ๊ทผ ๊ฐ๋ฅ)
- Deque: ์์ ์์ (์๋ ์ ๊ทผ ๊ฐ๋ฅ)
- LinkedHashSet: ์์ ์์ (์ ๋ ฅ ์์)
ํ์ง๋ง ์ด๋ค์ ๊ณตํต ์กฐ์์ธ Collection์ด๋ Set ์ธํฐํ์ด์ค๋ ์์์ ๋ํ ๊ฐ๋ ์ด ์๋ค. ๊ทธ๋์ ‘์ฒซ ๋ฒ์งธ ์์’๋ฅผ ๊ฐ์ ธ์ค๋ ๋จ์ํ ์์ ์กฐ์ฐจ ๊ตฌํ์ฒด๋ง๋ค ์ ๊ฐ๊ฐ์ด์๋ค.
- List: list.get(0)
- Deque: deque.getFirst()
- LinkedHashSet: iterator().next()
์๋ก์ด ์ธํฐํ์ด์ค ์ฃผ์
Java 21์ ๊ณ์ธต ๊ตฌ์กฐ ์ฌ์ด์ 3๊ฐ์ ์๋ก์ด ์ธํฐํ์ด์ค๋ฅผ ์ถ๊ฐํ๋ค.
- SequencedCollection: Collection์ ์์ ๋ฐ์ผ๋ฉฐ List์ Deque์ ์์๊ฐ ๋๋ค. addFirst, addLast, getFirst, getLast, removeFirst, removeLast, reversed() ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ค.
- SequencedSet: Set ์ด๋ฉด์ ์์๊ฐ ์๋ LinkedHashSet, TreeSet ๋ฑ์ ์์ ์ธํฐํ์ด์ค๋ค.
- SequencedMap: LinkedHashMap, TreeMap ๋ฑ์ ์์ ์ธํฐํ์ด์ค๋ก firstEntry, lastEntry, pollFirstEntry, pollLastEntry ๋ฑ์ ์ ๊ณตํ๋ค.
์ฝ๋ ๋ฆฌํฉํ ๋ง
๊ฐ์ฅ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ์ reversed() ๋ทฐ์ด๋ค. ์ด์ List๋ Set์ ์ญ์์ผ๋ก ์ํํ๋ ๊ฒ์ด ๋๋ฌด๋ ์ง๊ด์ ์ผ๋ก ๋ณํ๋ค.
๋ค์์ LinkedHashSet์ ๋ง์ง๋ง ์์ ๊ฐ์ ธ์ค๊ธฐ ๋ฐ ์ญ์ ์กฐํ ์ฝ๋๊ฐ ์ด๋ป๊ฒ ๊ฐ์ ๋์๋์ง๋ฅผ ๋ํ๋ธ๋ค.
Java 21 ์ด์
LinkedHashSet<String> set = new LinkedHashSet<>();
set.add("Apple");
set.add("Banana");
set.add("Cherry");
// ๋ง์ง๋ง ์์๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํด ์ ์ฒด๋ฅผ ์ํํ๊ฑฐ๋ ๋ฐฐ์ด๋ก ๋ณํํด์ผ ํ์
String lastElement = null;
for(String s : set) lastElement = s; // O(n)
// ์ญ์ ์ถ๋ ฅ์ ์ํด ๋ณ๋ ๋ฆฌ์คํธ ์์ฑ ํ์ (๋ฉ๋ชจ๋ฆฌ ๋ญ๋น)
List<String> list = new ArrayList<>(set);
Collections.reverse(list);
for(String s : list) {
System.out.println(s);
}JavaJava 21 ์ดํ
// ๋ง์ง๋ง ์์ ์ฆ์ ์ ๊ทผ O(1)
String lastElement = set.getLast();
// ์ญ์ ๋ทฐ๋ฅผ ํตํ ์ํ (๋ฉ๋ชจ๋ฆฌ ๋ณต์ฌ ์์)
for(String s : set.reversed()) {
System.out.println(s);
}Java์ฌ๊ธฐ์ reversed()๋ ์๋ก์ด ์ปฌ๋ ์ ์ ๋ณต์ฌํด์ ๋ง๋๋ ๊ฒ์ด ์๋๋ผ ์๋ณธ ์ปฌ๋ ์ ์ ๊ฑฐ๊พธ๋ก ๋ณด์ฌ์ฃผ๋ ๋ทฐ(view)๋ฅผ ๋ฐํํ๋ค. ๋ฐ๋ผ์ ๋ฉ๋ชจ๋ฆฌ ํจ์จ์ด ๋ฐ์ด๋๋ฉฐ reversed() ๋ทฐ์์ ์์๋ฅผ ์ญ์ ํ๋ฉด ์๋ณธ์์๋ ์ญ์ ๋๋ค.
https://stackoverflow.com/questions/79603303/how-can-i-process-elements-in-reverse-order-across-different-collection-types
https://nipafx.dev/inside-java-newscast-45/
๋ถ๋ณ์ฑ๊ณผ ์คํธ๋ฆผ
Java 9์ดํ ๊ทธ๋ฆฌ๊ณ Java 16์ ๊ฑฐ์น๋ฉด์ ์ปฌ๋ ์ ์ ์์ฑํ๊ณ ๋ณํํ๋ ๋ฐฉ์์ด ๊ฐ๊ฒฐํ๊ณ ์์ ํด์ก๋ค.
List.of vs Arrays.asList: ๋ถ๋ณ์ฑ์ ์ฐจ์ด
๋ฆฌ์คํธ๋ฅผ ์ด๊ธฐํํ ๋ ์ด ๋๊ฐ์ง๋ฅผ ์ฃผ๋ก ํผ์ฉํ๋ ๊ฒฝ์ฐ๋ค์ด ๋ง์ด ์๋๋ฐ ์ด ๋์ ๊ฒฐ์ ์ ์ธ ์ฐจ์ด๊ฐ ์๋ค.
| ํน์ฑ | Arrays.asList(1, 2, 3) | List.of(1, 2, 3) |
| ๋ณ๊ฒฝ ๊ฐ๋ฅ์ฑ | ๊ณ ์ ํฌ๊ธฐ(Fixed-size) set()์ผ๋ก ๊ฐ ๋ณ๊ฒฝ์ ๊ฐ๋ฅํ์ง๋ง add/remove๋ ๋ถ๊ฐ | ์์ ๋ถ๋ณ(Immutable). ๊ฐ ๋ณ๊ฒฝ, ์ถ๊ฐ, ์ญ์ ๋ชจ๋ ์๋์ UnsupportedOperationException ๋ฐ์ |
| null ํ์ฉ | ํ์ฉํจ. | ํ์ฉ ์ ํจ. ํฌํจ์ NullPointerException ๋ฐ์ |
| ์ฐธ์กฐ ๊ด๊ณ | ์๋ณธ ๋ฐฐ์ด์ ๋ทฐ(view)์ญํ . ์๋ณธ ๋ฐฐ์ด์ด ๋ฐ๋๋ฉด ๋ฆฌ์คํธ๋ ๋ฐ๋ | ๊ฐ ์์ฒด๋ฅผ ๋ณต์ฌํ์ฌ ๋ ๋ฆฝ์ ์ธ ๋ฆฌ์คํธ ์์ฑ |
๊ถ์ฅ ์ฌํญ: ๋ฐ์ดํฐ ์ ์ก ๊ฐ์ฒด(DTO)๋ ์ค์ ๊ฐ์ฒ๋ผ ๋ณ๊ฒฝ๋์ง ์์์ผ ํ ๋ชฉ๋ก์ ๋ง๋ค ๋๋ ํญ์ List.of๋ฅผ ์ฌ์ฉํ์ฌ ์๋์น ์์ ๋ณ๊ฒฝ์ฌํญ์ ๋ฐฉ์งํ๋ ๊ฒ์ด ์ข๋ค.
Stream API: Stream.toList() vs Collectors.toList()
Java 8์์ ์คํธ๋ฆผ ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌ์คํธ๋ก ๋ชจ์ ๋ collect(Collector.toList())๋ฅผ ์ฌ์ฉํ๋ค. Java 16๋ถํฐ๋ Stream.toList()๊ฐ ๋์ ๋์๋ค.
// Java 8 ์คํ์ผ
List<String> list1 = stream.collect(Collectors.toList());
// Java 16 ์คํ์ผ
List<String> list2 = stream.toList();Java์ฐจ์ด์ ์ ๋ค์๊ณผ ๊ฐ๋ค.
- Collectors.toList(): ๋ฐํ๋๋ ๋ฆฌ์คํธ๊ฐ ArrayList๋ฑ ๋ณ๊ฒฝ ๊ฐ๋ฅํ ๋ฆฌ์คํธ์ผ ๊ฐ๋ฅ์ฑ์ด ๋๋ค. (๋ช ์ธ์ ๋ณด์ฅ๋์ง ์์)
- Stream.toList(): ๋ณ๊ฒฝ ๋ถ๊ฐ๋ฅํ(Unmodifiable) ๋ฆฌ์คํธ๋ฅผ ๋ฐํํ๋ค. ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ด ์ต์ ํ๋์ด ์๊ณ null ๊ฐ์ ํ์ฉํ์ง ์๋๋ค.
๊ฒฐ๊ณผ ๋ฆฌ์คํธ๋ฅผ ์์ ํ ์ผ์ด ์๋ค๋ฉด ๋ ๊ฐ๊ฒฐํ๊ณ ์์ ํ Stream.toList()๋ฅผ ์ฌ์ฉํ ๊ฒ์ ๊ถ์ฅํ๋ค.
์์ํ ํ
contains() ํจ์
ArrayList.contains()๋ฅผ ์ฌ์ฉํ์ฌ ์ค๋ณต ์ฒดํฌ๋ฅผ ํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค.
List<String> list = new ArrayList<>();
//... ๋ฐ์ดํฐ 10๋ง ๊ฐ ์ถ๊ฐ...
if (!list.contains("Target")) { // ์ฌ๊ธฐ์ O(n) ๋ฐ์!
list.add("Target");
}JavaArrayList์ contains๋ ๋ฆฌ์คํธ ์ ์ฒด๋ฅผ ์ํํ๋ค. ๋ฐ์ดํฐ๊ฐ ๋ง์์ง์๋ก ์ฑ๋ฅ์ ์ ํ์ ์ผ๋ก ๋๋น ์ง๋ค (O(n)). ์กด์ฌ ์ฌ๋ถ ํ์ธ์ด ๋น๋ฒํ๋ค๋ฉด HashSet์ ์ฌ์ฉํ๋ ๊ฒ์ ์ถ์ฒํ๋ค. HashSet์ contains ์ฑ๋ฅ์ O(1)์ด๋ค. ๊ฐ์ contains ๋ฉ์๋๋ผ๊ณ ํ๋๋ผ๋ ๋ด๋ถ ์๋ฃ ๊ตฌ์กฐ์ ๋ฐ๋ผ ์ฑ๋ฅ ์ฐจ์ด ๋ฐ์ํ๋ค๋ ๊ฒ์ ์ ์ ์๋ค.
์์ ํ ์ญ์
๋ฃจํ๋ฅผ ๋๋ฉด์ ๋ฆฌ์คํธ์ ์์๋ฅผ ์ญ์ ํ๋ ์ฝ๋๋ ๊ฐ๋ฐ์๊ฐ ๋ง์ด ์ค์ํ๋ ๋ถ๋ถ์ด๋ค. for-each ๋ฃจํ์์ list.remove()๋ฅผ ํธ์ถํ๋ฉด ConcurrentModificationException์ด ๋ฐ์ํ๋ค.
for (String s : list) {
if (s.startsWith("A")) {
list.remove(s); // ConcurrentModificationException ์์ธ ๋ฐ์!
}
}Javaiterator๊ฐ ๋ฆฌ์คํธ๋ฅผ ์ํํ๋ ๋์ค์ ๋ฆฌ์คํธ์ ๋ด์ฉ์ด(๊ตฌ์กฐ) ๋ณ๊ฒฝ๋์๊ธฐ ๋๋ฌธ์ ์์ธ๊ฐ ๋ฐ์ํ๋ค. (for-each๋ iterator๋์)
java 8์ด์ ์ ๊ฒฝ์ฐ์๋ iterator์ remove()๋ฅผ ํธ์ถํ์ฌ ์ญ์ ํด์ผ ํ๋ค.
Iterator<String> iter = list.iterator();
while (iter.hasNext()) {
String s = iter.next();
if (s.startsWith("A")) {
iter.remove(); // ์์ ํจ: Iterator๊ฐ ์ญ์ ํ๊ณ ์ค์ค๋ก ์ํ ์
๋ฐ์ดํธํจ
}
}Javajava 8๋ถํฐ๋ ๋ด๋ถ์ ์ผ๋ก ์ต์ ํ๋ removeIf ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ค.
list.removeIf(s -> s.startsWith("A"));JavaremoveIf๋ ๋ด๋ถ์ ์ผ๋ก iterator๋ฅผ ์ฌ์ฉํ์ฌ ์์ ํ๊ฒ ์ญ์ ํ๊ณ ArrayList์ ๊ฒฝ์ฐ ์ญ์ ํ ๋ฐฐ์ด ๋ณต์ฌ ํ์๋ฅผ ์ต์ํ ํ๋๋ก ์ต์ ํ ๋์ด ์์ด ์ฑ๋ฅ์ ์ผ๋ก๋ ์ ๋ฆฌํ๋ค.
์ง๊ธ๊น์ง Java Collection Frameworkd์์ ๋ฟ๋ฆฌ๋ผ๊ณ ํ ์ ์๋ List, Map, Set์ ๋ํด์ ์ ๋ฆฌํด๋ณด์๋ค. Collection์ ๋จ์ํ ๋ฐ์ดํฐ ์ ์ฅ์๋ฅผ ๋์ด ๊ทธ ํน์ฑ์ ๋ง๊ฒ ๋ด๋ถ ์๋ฃ ๊ตฌ์กฐ๊ฐ ์ด๋ป๊ฒ ๋์ด ์๊ณ ๋์ํ๋์ง ์ดํดํ๋ ๊ฒ์ด ์ค์ํ ๊ฒ ๊ฐ๋ค. ๊ทผ๋ฐ ๋งค๋ฒ ๋ณผ ๋๋ง๋ค ์๋ก์ด ๋๋์ ์์ผ๊น? ใ ใ ๋ณผ ๋๋ ์ดํด๊ฐ ๋๋๋ฐ ์ง๋๊ณ ๋๋ฉด ๊ธ๋ฐฉ ์์ด๋ฒ๋ฆฐ๋ค. ์์ฃผ ๋ค์ฌ๋ค๋ด์ผ๊ฒ ๋ค.
์ฐธ๊ณ ํ๋ฉด ์ข์ ์์ฝ
- ์ผ๋ฐ์ ์ธ ์ํฉ์์๋ ArrayList๊ฐ LinkedList ๋ณด๋ค ๋น ๋ฅด๋ค. (CPU Cache locality)
- ์ค๋ณต ์ ๊ฑฐ์ ๋น ๋ฅธ ๊ฒ์์ด ํ์ํ ๋ ์ฃผ์ ์์ด HashSet์ ์ฌ์ฉํ์.
- Java 21์ ์ฌ์ฉํ๋ค๋ฉด SequencedCollection์ reversed(), getLast()๋ฑ์ ์ ๊ทน ํ์ฉํ์.
- ๋ถ๋ณ ๋ฆฌ์คํธ๊ฐ ํ์ํ ๋ List.of()์ Stream.toList()๋ฅผ ์ฌ์ฉํ์.
