사이먼's 코딩노트
[Java] 제네릭 / HashMap / 정리 본문
[제네릭]
- 문제를 풀어보면서 제네릭에 대해 조금 더 자세히 알아봅시다.
- 문제의 목표는 제네릭을 이용해서 클래스 3개를 1개로 줄여보는 것입니다.
public class Main {
public static void main(String[] args) {
Int저장소 a저장소1 = new Int저장소();
a저장소1.setData(30);
int a = a저장소1.getData();
System.out.println(a);
Double저장소 a저장소2 = new Double저장소();
a저장소2.setData(5.5);
double b = a저장소2.getData();
System.out.println(b);
사과저장소 a저장소3 = new 사과저장소();
a저장소3.setData(new 사과());
사과 c = a저장소3.getData();
System.out.println(c);
}
}
class Int저장소 {
Object data;
int getData() {
return (int)data;
}
void setData(Object inputedData) {
this.data = inputedData;
}
}
class Double저장소 {
Object data;
double getData() {
return (double)data;
}
void setData(Object inputedData) {
this.data = inputedData;
}
}
class 사과 {
}
class 사과저장소 {
Object data;
사과 getData() {
return (사과)data;
}
void setData(Object inputedData) {
this.data = inputedData;
}
}
- 아래 코드는 클래스 3개를 1개로 줄여서 재작성한 코드입니다.
public class Main {
public static void main(String[] args) {
저장소<Integer> a저장소1 = new 저장소();
a저장소1.setData(30);
int a = a저장소1.getData();
System.out.println(a);
저장소<Double> a저장소2 = new 저장소();
a저장소2.setData(5.5);
double b = a저장소2.getData();
System.out.println(b);
저장소<사과> a저장소3 = new 저장소();
a저장소3.setData(new 사과());
사과 c = a저장소3.getData();
System.out.println(c);
}
}
class 저장소<T> {
Object data;
T getData() {
return (T)data;
}
void setData(Object inputedData) {
this.data = inputedData;
}
}
class 사과 {
}
- 기존에 있던 Int저장소, Double저장소, 사과저장소 클래스는 제거하고, 모두를 담을 수 있는 저장소 클래스 하나를 생성한다.
- 저장소 클래스는 어떤 타입이든 객체든 모두 연결될 수 있기 때문에 클래스 뒤에 <> 제네릭을 써준다.
- 이 때 <T>에서 T는 Type의 약자로 사용자 임의로 정한 약어라고 생각하면 좋다.
- 제네릭을 썼기 때문에 getData() 메서드의 타입도 T로 바꿔주고, return 해주는 data 값도 앞에 각 타입에 맞는 강제 형변환이 아닌 (T)를 쓰면서 모든 타입의 데이터를 불러올 수 있게 해준다.
- Main 메서드에서도 당연히 각 저장소 별로 <Integer>, <Double>, <사과>를 붙혀 각 타입별로 저장소 객체와 연결될 수 있도록 한다.
[HashMap]
- HashMap은 데이터를 저장할 때 키(key)와 밸류(value)가 짝을 이루어 저장된다.
- 데이터를 저장할 때는 키값으로 해쉬함수를 실행한 결과를 통해 저장 위치를 결정한다.
- 따라서 HashMap은 특정 데이터의 저장위치를 해시함수를 통해 바로 알 수 있기 때문에 데이터의 추가, 삭제, 특히 검색이 빠르다는 장점이 있다.
- 이러한 이유로 HashMap은 키값을 통해서만 검색이 가능하며, HashMap의 키값은 중복될 수 없고, 밸류값은 키값이 다르다면 중복은 가능하다.
- 아래 코드는 저번 ArrayList 클래스를 직접 만들어 본 것처럼 HashMap 클래스를 간단하게 직접 구현해본 것이다. 이또한 import 해서 불러쓰면 되지만 간단히 이해를 돕기 위한 부분이라고 생각하면 좋을 것 같다.
public class Main {
public static void main(String[] args) {
HashMap<String, Integer> ages = new HashMap<>();
ages.put("영희", 22);
ages.put("철수", 23);
ages.put("민서", 25);
ages.put("철수", 27);
ages.remove("영희");
ages.put("광수", 27);
for ( String name : ages.keySet() ) {
System.out.println("이름 : " + name + ", 나이 : " + ages.get(name));
}
}
}
class HashMap<K, V> {
private Object[] keys;
private Object[] values;
private int lastIndex;
HashMap() {
lastIndex = -1;
keys = new Object[1];
values = new Object[1];
}
private boolean isArrayFull() {
return lastIndex >= keys.length - 1;
}
private void extendArraySizeIfFull() {
if ( isArrayFull() ) {
extendArraySize();
}
}
private void extendArraySize() {
Object[] newKeys = new Object[keys.length * 2];
Object[] newValues = new Object[values.length * 2];
for ( int i = 0; i < keys.length; i++ ) {
newKeys[i] = keys[i];
newValues[i] = values[i];
}
System.out.println("내부 배열의 사이즈가 증가합니다. " + keys.length + " => " + newKeys.length);
keys = newKeys;
values = newValues;
}
// 데이터 넣기 or 변경하기
void put(K key, V value) {
int keyIndex = getIndexOfKey(key);
if ( keyIndex >= 0 ) {
values[keyIndex] = value;
}
else {
extendArraySizeIfFull();
lastIndex++;
keys[lastIndex] = key;
values[lastIndex] = value;
}
}
void remove(K key) {
int keyIndex = getIndexOfKey(key);
if ( keyIndex >= 0 ) {
remove(keyIndex);
}
}
void remove(int index) {
for ( int i = index; i < lastIndex; i++ ) {
keys[i] = keys[i + 1];
values[i] = values[i + 1];
}
lastIndex--;
}
private int getIndexOfKey(K key) {
for ( int i = 0; i <= lastIndex; i++ ) {
if ( keys[i].equals(key) ) {
return i;
}
}
return -1;
}
Set<K> keySet() {
Set<K> keySet = new HashSet<>();
for ( int i = 0; i <= lastIndex; i++ ) {
keySet.add((K)keys[i]);
}
return keySet;
}
V get(K key) {
int keyIndex = getIndexOfKey(key);
if ( keyIndex >= 0 ) {
return (V)values[keyIndex];
}
return null;
}
}
- HashMap의 생성 방법은 [HashMap<String, Integer> ages = new HashMap<>()] 와 같다.
- 제네릭 안의 key값과 value값은 항상 String, Integer 타입만 받는 것은 아니고 모든 타입과 객체를 받을 수 있다.
- HashMap에서 데이터를 입력하고 싶다면 put(), 제거할 때는 remove(), 데이터를 얻을 때는 get() 메서드를 사용한다.
[정리]
- 앞에서 다룬 배열, ArrayList, HashMap를 아래 표를 통해 비교하면서 최종 정리해봅시다.
- 배열, ArrayList, HashMap의 클래스 생성 모습은 약간씩 다르다.
- 유연성이란, 데이터를 얼마나 자유롭게 늘릴 수 있는지를 나타낸다.
- 각 클래스별로 데이터를 넣고, 가져오고, 삭제하고, 수정할 때 쓰이는 문법과 메서드가 다르니 주의해야한다.
반응형
'Java > Java' 카테고리의 다른 글
[Java] 예외처리 / 접근제한자 (0) | 2024.03.19 |
---|---|
[Java] 인터페이스 (2) | 2024.03.19 |
[Java] ArrayList (2) | 2024.03.18 |
[Java] 배열 / toString / equals / StringBuilder (2) | 2024.03.14 |
[Java] 형변환 문제풀이 (0) | 2024.03.13 |