๐์๋ฐ์ ์ ์ ์ฐ์ต๋ฌธ์ ๋ฅผ ํ๋ค๊ฐ ์ฃผ์ ๊ฐ ์ฌ๋ฐ์ด์ ์์ฑํ๋ค
๋ค์ ์์ ์ ๋น๊ณ ํ์ 1~30์ฌ์ด์ ์ซ์๋ค๋ก ๋ง๋ ๊ฒ์ธ๋ฐ,
์ซ์๋ค์ ์์น๊ฐ ์ ์์ด์ง ์๋๋ค๋ ๋ฌธ์ ๊ฐ ์๋ค.
์ด๋ฌํ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ ์ด์ ์ ์ด ๋ฌธ์ ๋ฅผ ๊ฐ์ ํ๊ธฐ ์ํ ๋ฐฉ๋ฒ์ ์ค๋ช
ํ๊ณ ,
์ด๋ฅผ ๊ฐ์ ํ ์๋ก์ด ์ฝ๋๋ฅผ ์์ฑํ์์ค.
์์ :
(1) 1~30๋ฒ์์ ๋๋ค๊ฐ์ ๋ง๋ค๊ณ
(2) ๋๋ค๊ฐ 25๊ฐ๋ฅผ HashSet์ ์ ์ฅ
(3) HashSet์ ์ ์ฅ๋ ๋ฐ์ดํฐ๋ฅผ ์์๋๋ก ์ถ๋ ฅ
*๊ธฐ์กด ์์ฑ๋ ์ฝ๋๋ฅผ ์์ฝํ๊ฒ๋๋ค
๊ฒฐ๊ณผ:
์ซ์๋ค์ด ์ ๋๋ก ์์ด์ง ์์ ๊ฒ์ ํ์ธํ ์ ์๋ค
HashSet์ ์์์ ์ง๋ฅผ ํ์ง ์์ผ๋ฉฐ, ํด์ฑ๊ธฐ๋ฒ์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ๋น์ทํ ๊ฐ๋ค์ด ๋น์ทํ ์์น์ ์ ์ฅ๋๋ค๋ ๊ฒ์ ๋์ถฉ ์ ๊ฒ์ด๋ค. ๊ทธ๋ผ ์ค์ ๋ด๋ถ๋์์ ์ด๋จ๊น..?
์ด์ ์ฝ๋๋ฅผ ํ๊ณ ๋ค์ด๊ฐ๋ณด์,,,๐๐๐
hashSet ๋จผ์ ์์ฑโ๏ธ
/**
* Constructs a new, empty set; the backing {@code HashMap} instance has
* default initial capacity (16) and load factor (0.75).
*/
public HashSet() {
map = new HashMap<>();
}
: HashSet์ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ฐฐ์ด ํฌ๊ธฐ
๊ฐ 16
์ด๊ณ load factor
๋ 0.75
๋ก ์์ฑํ๋ค๊ณ ํ๋ค
load factor
16*0.75=12๊ฐ
๊ฐ ์ ์ฅ๋๋ฉด ํฌ๊ธฐ๊ฐ ์ฆ๊ฐ๋๋ค๋ ๊ฒ์ด๋ค์ด์ ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐํด๋ณด์!โ๏ธ
/**
* Adds the specified element to this set if it is not already present.
* More formally, adds the specified element {@code e} to this set if
* this set contains no element {@code e2} such that
* {@code Objects.equals(e, e2)}.
* If this set already contains the element, the call leaves the set
* unchanged and returns {@code false}.
*
* @param e element to be added to this set
* @return {@code true} if this set did not already contain the specified
* element
*/
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
// Dummy value to associate with an Object in the backing Map
static final Object PRESENT = new Object();
: ์ ์ฅํ๋ ค๋ ์์๊ฐ ํฌํจ๋์ด์๋ค๋ฉด ์ถ๊ฐํ ๋ค์ true๋ฅผ ๋ฐํํ๊ณ ์ด๋ฏธ ์กด์ฌํด์ ์ถ๊ฐ๋ฅผ ํ์ง ์์ ๊ฒฝ์ฐ false๋ฅผ ๋ฐํํ๋ค๊ณ ํ๋ค
HashSet์ ๋ด๋ถ์ ์ผ๋ก HashMap์ ๊ธฐ๋ฐ์ผ๋ก ๋ง๋ค์ด์ ธ์๋๋ฐ HashMap์ (key,value)๋ฅผ ์ ์ฅํ์ง๋ง HashSet์ key๋ง ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ value์๋ ๋๋ฏธ๋ฐ์ดํฐ๋ฅผ ๋ฃ๋ ๊ฒ!
๋ ๋ค์ด๊ฐ๋ณด์~โ๏ธ
/**
* Associates the specified value with the specified key in this map.
* If the map previously contained a mapping for the key, the old
* value is replaced.
*
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return the previous value associated with {@code key}, or
* {@code null} if there was no mapping for {@code key}.
* (A {@code null} return can also indicate that the map
* previously associated {@code null} with {@code key}.)
*/
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
: ๊ฐ๋จํ๊ฒ ์ค๋ช ํ์๋ฉด key์ ๋งคํ๋ value๊ฐ ์๋ ๊ฒฝ์ฐ์๋ null์ ๋ฐํํ๊ฑฐ๋ ๊ธฐ์กด์ ์ ์ฅ๋์ด์๋ value๋ฅผ ๋ฐํํ๋ค๋ ๊ฒ์ด๋ค.
๊ทธ๋ฆฌ๊ณ ์ฌ๊ธฐ์ hash(key)
๊ฐ ํธ์ถ๋๋ ๊ฒ์ ์ ์ ์๋ค.
hashCode()
๋ฉ์๋ ํธ์ถstatic final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
์ ์ด์ HashSet
์ HashSet<Object>
๋ก ๊ธฐ๋ณธํ์ ๋ฐ์ง ์์ ๋ํผ ํด๋์ค๋ฅผ ์ ๋ฌํด์ค์ผ ํ๋ค. ๊ทผ๋ฐ set.add(10)
๋ ์ด๋ป๊ฒ ๊ฐ๋ฅํ๊ฑฐ์ง..?
public class Main {
public static void main(String[] args) {
HashSet<int> set = new HashSet<>();
set.add(10);
}
}
์ ์ฝ๋๋ฅผ ์ปดํ์ผํ ํ์ผ์ ์ฝ์ผ๋ฉด ๋ค์๊ณผ ๊ฐ๋ค
$ javap -verbose Main
public class Main
minor version: 0
major version: 64
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #20 // Main
super_class: #2 // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 1
Constant pool:
#1 = Methodref #2.#3 // java/lang/Object."<init>":()V
#2 = Class #4 // java/lang/Object
#3 = NameAndType #5:#6 // "<init>":()V
#4 = Utf8 java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Class #8 // java/util/HashSet
#8 = Utf8 java/util/HashSet
#9 = Methodref #7.#3 // java/util/HashSet."<init>":()V
๐#10 = Methodref #11.#12 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
#11 = Class #13 // java/lang/Integer
#12 = NameAndType #14:#15 // valueOf:(I)Ljava/lang/Integer;
#13 = Utf8 java/lang/Integer
#14 = Utf8 valueOf
#15 = Utf8 (I)Ljava/lang/Integer;
#16 = Methodref #7.#17 // java/util/HashSet.add:(Ljava/lang/Object;)Z
#17 = NameAndType #18:#19 // add:(Ljava/lang/Object;)Z
#18 = Utf8 add
#19 = Utf8 (Ljava/lang/Object;)Z
#20 = Class #21 // Main
#21 = Utf8 Main
#22 = Utf8 Code
#23 = Utf8 LineNumberTable
#24 = Utf8 main
#25 = Utf8 ([Ljava/lang/String;)V
#26 = Utf8 SourceFile
#27 = Utf8 Main.java
{
public Main();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: new #7 // class java/util/HashSet
3: dup
4: invokespecial #9 // Method java/util/HashSet."<init>":()V
7: astore_1
8: aload_1
๐9: bipush 10
๐11: invokestatic #10 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
14: invokevirtual #16 // Method java/util/HashSet.add:(Ljava/lang/Object;)Z
17: pop
18: return
LineNumberTable:
line 5: 0
line 6: 8
line 7: 18
}
SourceFile: "Main.java"
๐ง???์ด๊ฒ ๋ญ์ง... ์ถ๊ฒ ์ง๋ง ์์์ ๊ด๋ จ๋ ๋ถ๋ถ๋ง ๊ฐ์ ธ์ค๋ฉด ์ดํด๊ฐ ์ฝ๋ค
(1) #10 = Methodref #11.#12 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
(2) bipush 10
(3) invokestatic #10 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
(1) #10์ Integer.valueOf(int)
๋ฉ์๋๋ฅผ ์ฐธ์กฐ
(2) int ํ์
์ 10์ ์คํ์ ํธ์
(3) invokestatic #10
์ #10 ๋ฉ์๋๋ฅผ ํธ์ถํ๋ผ๋ ๊ฒ์ผ๋ก ์คํ์์ 10์ ๊บผ๋ด์ #10์ ์ ์๋ Integer.valueOf(10)
์ ์คํํ์ฌ Integer ๊ฐ์ฒด
๋ก ๋ํ๋ 10์ ๋ฐํ
๋๋ ๋ค์ด๊ฐ๋ณด์โ๏ธ
/**
* Implements Map.put and related methods.
*
* @param hash hash for key
* @param key the key
* @param value the value to put
* @param onlyIfAbsent if true, don't change existing value
* @param evict if false, the table is in creation mode.
* @return previous value, or null if none
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
์ ์ฒด์ ์ธ ํ๋ฆ์ ๋ค์๊ณผ ๊ฐ๋ค
tab[i = (n - 1) & hash
ํ์ธ(key,value)
์ถ๊ฐvalue
๋ฎ์ด์ฐ๊ณ ์ด์ value
๋ฐํ16*0.75=12๊ฐ
๋ณด๋ค ํฌ๋ค๋ฉดnewThr = oldThr << 1
(ํ์ฅํ ํฌ๊ธฐ - 1) & hash
๋ก ๋ค์ ๊ณ์ฐํ๋คโญ์ฌ๊ธฐ์ ์ค์ํ ๊ฒ๋ง ๋ณด์๋ฉด ์ธ๋ฑ์ค๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ๊ตฌํ๋ค๋ ๊ฒ์ ๋ณผ ์ ์๋ค
tab[i = (n - 1) & hash
static final int TREEIFY_THRESHOLD = 8;
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
binCount >= TREEIFY_THRESHOLD - 1
๋ผ๋ฉด ์ฐ๊ฒฐ๋ฆฌ์คํธ
๋ฅผ Red-Black Tree
๋ก ๋ณํํ์ฌ O(n) -> O(log n)์ผ๋ก ํ์ ์ฑ๋ฅ์ ๋์ด๋ ๊ฒ
-> ์ด ๋ถ๋ถ์ ์ธ์ง๋ง ํ๊ณ ๋์ค์ ๋ ์์ธํ ์์๋ณด์
์์์ โญ๋ฅผ ๋ฐํ์ผ๋ก ๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ธ๋ฑ์ค๊ฐ ๊ตฌํด์ง๋๋ฐ ์ด๊ฑธ ์ด์ฉํด์ ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํด๋ณด์
import java.util.*;
public class test {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
set.add("1");
set.add("2");
set.add("3");
set.add("4");
set.add("5");
set.add("6");
set.add("7");
set.add("8");
set.add("9");
set.add("10");
set.add("11");
set.add("12");
int n = 16; // HashMap ๊ธฐ๋ณธ ํฌ๊ธฐ
int h, hash, index;
System.out.printf("%-6s | %-12s | %-20s | %-10s\n", "Key", "hashCode", "hash (h ^ (h >>> 16))", "Index");
System.out.println("---------------------------------------------------------------");
for (String str : set) {
h = str.hashCode();
hash = h ^ (h >>> 16);
index = (n - 1) & hash;
System.out.printf("%-6s | %-12d | %-20d | %-10d\n", str, h, hash, index);
}
}
}
๐ก๊ฒฐ๊ณผ
Key | hashCode | hash (h ^ (h >>> 16)) | Index
---------------------------------------------------------------
11 | 1568 | 1568 | 0
1 | 49 | 49 | 1
12 | 1569 | 1569 | 1
2 | 50 | 50 | 2
3 | 51 | 51 | 3
4 | 52 | 52 | 4
5 | 53 | 53 | 5
6 | 54 | 54 | 6
7 | 55 | 55 | 7
8 | 56 | 56 | 8
9 | 57 | 57 | 9
10 | 1567 | 1567 | 15
ํ์ฌ ํฌ๊ธฐ * 2
๊ฐ ๋๋ฏ๋ก ํฌ๊ธฐ๊ฐ 16
์ผ ๊ฒฝ์ฐ์ 32
์ผ ๊ฒฝ์ฐ์ ์ธ๋ฑ์ค๋ฅผ ๊ตฌํ๋ค
<import java.util.*;
public class test {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
set.add("1");
set.add("2");
set.add("3");
set.add("4");
set.add("5");
set.add("6");
set.add("7");
set.add("8");
set.add("9");
set.add("10");
set.add("11");
set.add("22");
set.add("21");
set.add("24");
set.add("50");
set.add("13");
set.add("12");
// ํ
์ด๋ธ ํฌ๊ธฐ (16 -> 32๋ก ์ฆ๊ฐ)
int oldCapacity = 16;
int newCapacity = oldCapacity + (oldCapacity >> 1);
System.out.printf("%-6s | %-12s | %-20s | %-10s\n", "Key", "hashCode", "hash (h ^ (h >>> 16))", "Index (Old -> New)");
System.out.println("---------------------------------------------------------------");
for (String str : set) {
int h = str.hashCode();
int hash = h ^ (h >>> 16);
// ๋ฆฌ์ฌ์ด์ฆ ์ ์ธ๋ฑ์ค (16 ํฌ๊ธฐ์ผ ๋)
int oldIndex = (oldCapacity - 1) & hash;
// ๋ฆฌ์ฌ์ด์ฆ ํ ์ธ๋ฑ์ค (32 ํฌ๊ธฐ์ผ ๋)
int newIndex = (newCapacity - 1) & hash;
System.out.printf("%-6s | %-12d | %-20d | %-10d -> %-10d\n", str, h, hash, oldIndex, newIndex);
}
}
}
๐ก๊ฒฐ๊ณผ
Key | hashCode | hash (h ^ (h >>> 16)) | Index (Old -> New)
---------------------------------------------------------------
11 | 1568 | 1568 | 0 -> 0
22 | 1600 | 1600 | 0 -> 0
12 | 1569 | 1569 | 1 -> 1
24 | 1602 | 1602 | 2 -> 2
13 | 1570 | 1570 | 2 -> 2
1 | 49 | 49 | 1 -> 17
2 | 50 | 50 | 2 -> 18
3 | 51 | 51 | 3 -> 19
4 | 52 | 52 | 4 -> 20
5 | 53 | 53 | 5 -> 21
6 | 54 | 54 | 6 -> 22
7 | 55 | 55 | 7 -> 23
8 | 56 | 56 | 8 -> 16
9 | 57 | 57 | 9 -> 17
50 | 1691 | 1691 | 11 -> 19
10 | 1567 | 1567 | 15 -> 23
21 | 1599 | 1599 | 15 -> 23
p.s ์์ฆ,, ์ด๋ก์ด๋กํ๊ฒ ์ข์์ ์ธ๋ค์ผ์ ๋ฐ๊ฟจ๋ค,,ใ ใ