[Java] LinkedHashMap์ ํตํ ๊ธฐ์ด ์บ์ฑ
๐ง ์บ์ฑ ๋ฐฉ์ ์ฌ์ฉ ์์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ์ต์ํํ๋ ๋ฐฉ๋ฒ์ ๋ฌด์์ผ๊น?
@Test
@DisplayName("๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ์ต์ํํ๋ ๋ฐฉ๋ฒ์ ๋ฌด์์ผ๊น?")
void ๋ฉ๋ชจ๋ฆฌ_์ฌ์ฉ๋์_์ต์ํํ๋_๋ฐฉ๋ฒ์_๋ฌด์์ผ๊น() {
// TODO: ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ์ต์ํํ๋ ๋ฐฉ๋ฒ์ ๊ณ ๋ฏผ ํ ๊ฐ์ ํด๋ณด์ธ์.
record Position(int value) {
private static final Map<Integer, Position> CACHE = new ConcurrentHashMap<>();
public static Position startingPoint() {
return valueOf(0);
}
public static Position valueOf(final int value) {
return CACHE.computeIfAbsent(value, Position::new);
}
์ฐํ ์ฝ์ ์ข์ ์ฝ๋, ์ข์ ์์ธ ์ฒ๋ฆฌ ์์ ์์ ์์ ๊ฐ์ด Map์ ํตํด ๋ฐ์ดํฐ๋ฅผ ์บ์ฑํ๋ ์ํฉ์์ ๋ฉ๋ชจ๋ฆฌ์ ์ฌ์ฉ๋์ ์ต์ํํ๋ ๋ฐฉ๋ฒ์ด ๋ฌด์์ผ๊น์ ๋ํ ๋ฌธ์ ๋ฅผ ๋ฐ๊ฒ ๋์๋ค.
์บ์ฑ์ ํ๊ฒ ๋ ๋ฐฐ๊ฒฝ
record Position(int value) {
Position() {
this(0);
}
public Position increase() {
return new Position(value + 1);
}
}
์ฐ์ ์บ์ฑ์ ํ๋ ์ด์ ๋ Position์ ๋ถ๋ณ์ํ๋ก ๋ง๋ค์ด์ ์์ธกํ์ง ๋ชปํ ๊ฐ ๋ณ๋์ ๋ฐ๊พธ๊ธฐ ์ํด ๋ค์๊ณผ ๊ฐ์ ์ฝ๋๋ฅผ ์ฐจ์ฉํ๊ธฐ ๋๋ฌธ์ด๋ค.
๋งค๋ฒ Position์ด ์ฆ๊ฐ๋ ๋๋ง๋ค, ์ฌ์ด๋ ์ดํํธ๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด ์๋ก์ด Position ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด์ returnํ๊ณ ์๋ ํํ์ด๋ค.
๋ฌผ๋ก ๋ฌธ์ ํด๊ฒฐ์๋ ๋์์ด ๋๊ฒ ์ง๋ง ์ด๋ ๊ฒ ๊ด๋ฆฌํ๊ฒ ๋๋ฉด ๊ณ์ํด์ Position ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ฒ ๋๊ธฐ ๋๋ฌธ์ ์ด์ ๋ํ ํด๊ฒฐ์ฑ ์ผ๋ก ์บ์ฑ์ด๋ผ๋ ๋ฐฉ๋ฒ์ ๋์ ํ๊ฒ ๋ ์ํฉ์ด๋ค.
๐ก LinkedHashMap์ removeEldestEntry
๋๋ ์ด ๋ฌธ์ ์ ๋ํด ๊ณ ๋ฏผํ๋ ๊ณผ์ ์์ LinkedHashMap์ removeEldestEntry() ๋ผ๋ ๋ฉ์๋๋ฅผ ์๊ฒ ๋์๋ค.
LinkedHashMap์ ์ ์ฅ๋ ๋ฐ์ดํฐ์ ์์๋ฅผ ๊ธฐ์ตํ๋ Map์ธ๋ฐ,
์ด๋ฌํ ํน์ง์ ์ด์ฉํ์ฌ Map์ ์ค์ ํ ์ต๋ ์บ์ฑ ์๊ฐ N๊ฐ๋ณด๋ค ๋ง์์ก์ ์ค๋๋ ๋ฐ์ดํฐ๋ฅผ ์ญ์ ํ๋๋ก ๋ง๋ค ์ ์๋ ๋ฉ์๋์๋ค.
๋๋ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ์ต์ํ๋ผ๋ ์ฃผ์ ์ ์ ์ฉํด๋ณผ ์ ์๋ ๋ฐฉ์ ์ค ํ๋๊ฐ ๋ฐ๋ก ์ด LinkedHashMap์ด๋ผ๊ณ ์๊ฐ๋์ด ๋ฐ๋ก ์ ์ฉํด๋ณด์๋ค.
@Test
@DisplayName("๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ์ต์ํํ๋ ๋ฐฉ๋ฒ์ ๋ฌด์์ผ๊น?")
void ๋ฉ๋ชจ๋ฆฌ_์ฌ์ฉ๋์_์ต์ํํ๋_๋ฐฉ๋ฒ์_๋ฌด์์ผ๊น() {
// TODO: ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ์ต์ํํ๋ ๋ฐฉ๋ฒ์ ๊ณ ๋ฏผ ํ ๊ฐ์ ํด๋ณด์ธ์.
record Position(int value) {
private static final Map<Integer, Position> CACHE = new LinkedHashMap<>() {
static final int CACHE_MAX = 5;
@Override
protected boolean removeEldestEntry(Map.Entry<Integer, Position> eldest) {
return CACHE.size() >= CACHE_MAX;
}
};
public static Position valueOf(final int value) {
return CACHE.computeIfAbsent(value, Position::new);
}
- removeEldestEntry๋ฅผ Override ํ์ฌ CACHE_MAX๊ฐ ์ด์์ ๋ฐ์ดํฐ๊ฐ ์บ์ฑ๋๋ฉด, ์บ์ฑ๋์ง ์ค๋๋ ์์๋๋ก ๊ฐ์ ์ญ์ ํ๋๋ก ๋ง๋ค์ด์ CACHE_MAX ๋ณด๋ค ๋ง์ ๋ฐ์ดํฐ๊ฐ ์บ์ฑ๋์ง ์๋๋ก ์กฐ์ ํ์๋ค.
์ด ๋ฐฉ์์ ํ์ฉํ๋ฉด ํ์ฌ ๊ฐ์ง๊ณ ์๋ ๋ฆฌ์์ค์ ๋ฐ๋ผ CACHE_MAX๋ฅผ ์ ์ ํ๊ฒ ์กฐ์ ํ์ฌ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ์ต์ํํ ์ ์๋๋ก ๊ตฌํํ ์ ์์ ๊ฒ์ด๋ค.
๋ฌผ๋ก ๋จ์ ์ผ๋ก๋ ์์ฃผ ์ฌ์ฉ๋๋ ๊ฒ์ ๊ตฌ๋ถํ๋ ๋ฐฉ์์ด ์๋ ์ค๋๋ ์์๋๋ก ์ญ์ ํ๋ค๋ ์ ์ด ์๋ค.
๋๋ฌธ์ ์์ฃผ ์ฌ์ฉ๋๋ ๊ฒ์ ๊ตฌ๋ถํด์ ์บ์ฑํด์ผํ๋ ์ํฉ์ด๋ผ๋ฉด ์ด ๋ฐฉ๋ฒ์ ์ ํํ๋ ๊ฒ์ด ๊ทธ๋ ๊ฒ ํจ์จ์ ์ด์ง ์์ ์๋ ์๋ค.
์ ์ฉ ๊ฒฐ๊ณผ
์ ๋ฐฉ์์ ์ ์ฉํ๊ณ ๋๋, ์์ ๊ฐ์ด ์บ์ฑ ์ฌ์ด์ฆ๊ฐ CACHE_MAX ๋ฅผ ๋์ด๊ฐ๋ฉด ์ค๋๋ ๋ฐ์ดํฐ๋ฅผ ์ ๊ฑฐํ๋๋ก ๋์ํ๋ ๊ฒ์ ํ์ธํ ์ ์์๋ค.
๋ฌธ์ ์
์บ์์ ์ต๋ ํ๊ณ์น๋ฅผ ์ค์ ํ ์ ์๋ค๋ ์ ์ ์๊ฒ ๋๋ฐ, ์์ฃผ ์ฌ์ฉ๋๋ ๊ฒ์ ๊ตฌ๋ถํ๋ ๋ฐฉ์์ด ์๋๋ผ Insert๋ ์ง ์ค๋๋ ์์๋๋ก ์ญ์ ํ๋ค๋ ํฐ ๋ฌธ์ ์ ์ ์ด๋ป๊ฒ ํด๊ฒฐํ ์ ์์๊น?
์ด์ ๋ํ ํด๊ฒฐ์ฑ ์ผ๋ก LinkedHashMap์ด ์ ๊ณตํ๋ Ordering Mode ์ค Access-Order ๋ฐฉ์ ์ ํ์ฉํด ๋ณผ ์ ์๊ฒ ๋ค.
๐ก LinkedHashMap์ Ordering Mode
LinkedHashMap์ Ordering Mode๋ผ๋ ๊ฒ์ ํตํด ์ค๋๋ ๋ฐ์ดํฐ๋ฅผ ํ๋จํ๋ ๊ธฐ์ค์ 2๊ฐ์ง ๊ธฐ์ค์ผ๋ก ๊ตฌ๋ถํ ์ ์๋ค.
Insertion-Order (์ฝ์ ๊ธฐ์ค ์ ๋ ฌ)
- Insertion-Order๋ ๋ฐ์ดํฐ๊ฐ Insert ๋ ์์๋ฅผ ๊ธฐ์ค์ผ๋ก ์ ๋ ฌํ๋ค๋ ๊ฒ์ด๋ค.
- LHM์ ๊ธฐ๋ณธ ์์ฑ์๋ก ์์ฑํ๊ฒ ๋๋ฉด Ordering Mode ์ค์ ์ ๋ด๋นํ๋ accessOrder๊ฐ false๋ก ์ค์ ๋์ด ์๋์ผ๋ก Insertion-order ๋ชจ๋๋ก ์ค์ ๋๋ค.
Access-Order (์ ๊ทผ ๊ธฐ์ค ์ ๋ ฌ)
- Access-Order ๋ ๋ฐ์ดํฐ๊ฐ ์ต๊ทผ ์ ๊ทผ๋ ์์๋ฅผ ๊ธฐ์ค์ผ๋ก ์ ๋ ฌํ๋ค๋ ๊ฒ์ด๋ค.
- LHM์ด ๊ฐ์ฅ ์ค๋ซ๋์ ์ฌ์ฉํ์ง ์์ ํ์ด์ง๋ฅผ ๊ต์ฒดํ๋ ๊ธฐ๋ฒ์ธ LRU(Least Recently Used) ์๊ณ ๋ฆฌ์ฆ์ ๊ตฌํํ ์ ์๋๋ก ์ ๊ณตํ๋ ๊ธฐ๋ฅ์ด๋ค.
- initialCapacity, loadFactor, accessOrder๋ฅผ ์ ๋ ฅ๋ฐ๋ ์์ฑ์๋ฅผ ํตํด ์ฌ์ฉ ๊ฐ๋ฅํ๋ค.
๐ initialCapacity
- ์ด๊ธฐ Map์ ์ฉ๋์ ๋ํ ๊ฐ์ด๋ค.
- ๊ธฐ๋ณธ ์์ฑ์์ Default ๊ฐ์ 16์ด๋ค.
๐ loadFactor
- Map์ ์ฉ๋์ด ์ด๋์ ๋ ์ฐผ์ ๋ capacity๋ฅผ ํ์ฅํ ์ง์ ๋ํ ๊ฐ์ด๋ค.
- ๊ธฐ๋ณธ ์์ฑ์์ Default ๊ฐ์ 0.75f์ด๋ค.
๐ accessOrder
- Ordering Mode ์ค์ ์ ๋ด๋นํ๋ ๊ฐ์ผ๋ก true๋ก ์ ํ ์, Access-Order ๋ชจ๋๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
๐ก Access-Order ๋ชจ๋ ์ ์ฉ
record Position(int value) {
private static final Map<Integer, Position> CACHE = new LinkedHashMap<>(16, 0.75f, true) {
static final int CACHE_MAX = 5;
@Override
protected boolean removeEldestEntry(Map.Entry<Integer, Position> eldest) {
System.out.println(CACHE);
return CACHE.size() >= CACHE_MAX;
}
};
- LHM์ Access-Order ๋ชจ๋ ์ ์ฉ์ ํตํด ์ต๊ทผ์ ์ ๊ทผํ ์์๋๋ก Map์ ์์๋ค์ ์ ๋ ฌํ์ฌ,
๊ฐ์ฅ ์ค๋ซ๋์ ์กฐํ๋์ง ์์ ๊ฐ์ ์ค๋๋์๋ค๊ณ ํ๋จํ๋๋ก ์ค์ - ์ด๋ฅผ ํตํด ์ฌ์ฉ๋์ง ์๋ ๊ฐ๋ค์ ์์ฐ์ค๋ ์บ์ ๋ฉ๋ชจ๋ฆฌ์์ ์ ๊ฑฐํ๊ณ ์์ฃผ ์ฌ์ฉ๋๋ ๊ฐ๋ค์ ์ค๋ซ๋์ ์ ์ง
์ ์ฉ ๊ฒฐ๊ณผ
์ ์ฝ๋ ์ ์ฉ์ ํตํด ์ฌ์ง ๋ง์ง๋ง ์ค์์ ๋ณผ ์ ์๋ฏ์ด 1,2,3,4,5๋ฅผ ์์ฐจ์ ์ผ๋ก ์์ฑํ ํ, 1๋ฒ์ ์ ๊ทผ ํ์์ ๋
๋งจ ๋ค์ ์๋ 1๋ฒ์ด ๊ฐ์ฅ ๋งจ ์์ผ๋ก ์ค๋ ๊ฒ์ ํ์ธํ ์ ์์๋ค.
๋ง๋ฌด๋ฆฌ
2์ฃผ๊ฐ ์ฐํ ์ฝ์์ ํ์ตํ๋ฉฐ ๋๋ ์ฐํ ์ฝ์ ์ฅ์ ์
์ข์ ์ฝ๋๋ฅผ ๋ง๋ค๊ธฐ ์ํด ๋ ๊น์ด ์๊ฒ ํ์ตํ๊ณ , ๋ ๊น์ด์๊ฒ ๊ณ ๋ฏผํ ์ ์๋ ํ๊ฒฝ์ ์กฐ์ฑํด์ค๋ค๋ ์ ์ธ ๊ฒ ๊ฐ๋ค.
์ฐํ
์ฝ์์ ์ฒด๊ณ์ ์ธ ๋ฏธ์
๊ณผ ์์
๊ทธ๋ฆฌ๊ณ ์์ค ๋์ ๋ฆฌ๋ทฐ๋ฅผ ๋ฐ์๋ณด๋ฉฐ ๋ด๊ฐ ๋ถ์กฑํ ๊ฒ์ด ๋ฌด์์ธ์ง์ ๋ํด ์ง๊ด์ ์ผ๋ก ํ์
ํ ์ ์๊ฒ ๋์๊ณ ,
ํ์ด ํ๋ก๊ทธ๋๋ฐ์ ํตํด ํ์ด์ ์ฝ๋์ ๋ํ ๊ณ ๋ฏผ์ ์๊ฐ์ด ์๋ ๋ํ๋ก ์ฃผ๊ณ ๋ฐ์ผ๋ฉด์ ๋จธ๋ฆฟ์์ ์ด์ง๋ฌ์ ธ์๋ ์๊ฐ๊ณผ ๊ฐ๋
๋ค์ด ๊ฐ์ง๋ฐํ ์ ๋ฆฌ๋์ด ๊ฐ๋ค๋ ๊ฒ์ ๋๊ผ๋ค.
๋, ํ์ด ๋ฟ๋ง ์๋๋ผ ํฌ๋ฃจ์๋ค๊ณผ ๋ํํ๋ ๊ณผ์ ์์๋ ๋ง์ ์ธ์ฌ์ดํธ์ ๋๊ธฐ๋ถ์ฌ๋ฅผ ์ป๊ณ ์๋ ์์ฆ์ด๋ค.
ํฌ์คํ ์ ๋ด์ฉ ๋ํ ํฌ๋ฃจ์๋ค๊ณผ ๋ํ ์ค WeakHashMap ์ด๋ผ๋ ๊ฒ์ ์๊ฒ ๋์ด, ์ด์ ๋ํด ํ์ตํ๋ ๊ณผ์ ์์ ์๋กญ๊ฒ ๋ฐฐ์ด ๋ด์ฉ์ด๋ค.
์ญ์ ๊ฐ๋ฐ์ ํผ์๊ฐ ์๋ ํจ๊ปํ ๋ ๊ฐ์ฅ ๋น ๋ฅด๊ฒ ์ฑ์ฅํด๋๊ฐ๋ ๊ฒ ๊ฐ๋ค๐ฅ