자바 "Map Of 컬렉션"
Map 는 Key와 Value로 이루어져 있다
Key와 Value가 1:1로 저장된다
Key는 해당 Map에서 중복되지 않는다
만약 Key가 다르고, Value가 동일하다면 맵에서 다른 것으로 간주한다
Map은
- 모든 데이터는 Key와 Value이 존재한다
- Key가 없이 Value만 저장될 수는 없다
- Value없이 Key만 저장될 수도 없다
- Key는 해당 Map에서 고유해야만 한다
- Value는 Map에서 중복되어도 전혀 상관없다
Map은 java.util 패키지의 Map 이라는 인터페이스로 선언
Map 인터페이스에 선언되어 있는 메소드들의 주요 기능들(아래 상세 설명 있음)
리턴 타입 | 메소드 이름 및 매개 변수 | 설명 |
V | put(K Key, V Value) | 첫 번쨰 매개 변수인 키를 갖는, 두 번째 매개 변수인 값을 갖는 데이터를 저장한다 |
void | putAll(Map<? extends K, ? extends V> m) | 매개 변수로 넘어온 Map의 모든 데이터를 저장한다 |
V | get(Object Key) | 매개 변수로 넘어온 키에 해당하는 값을 넘겨준다 |
V | remove(Object Key) | 매개 변수로 넘어온 키에 해당하는 값을 넘겨주며, 해당 키와 같은 Map에서 삭제한다 |
Set<K> | KeySet() | 키의 목록을 Set타입으로 리턴한다 |
Collection | values() | 값의 목록을 Collection 타입으로 리턴한다 |
Set<Map, Entry<K,V>> | entrySet() | Map 안에 Entry라는 타입의 Set을 리턴한다 |
int | size() | Map의 크기를 리턴한다 |
void | clear() | Map의 내용을 지운다 |
이 중에서도 꼭 기억해야 하는 것은
- Map에 데이터를 넣는 put()
- 데이터를 꺼내는 get()
- 데이털르 삭제하는 remove()
Map을 구현한 주요 클래스들
Map 인터페이스를 구현한 클래스들은 매우 다양하고 많다
그 중에서 HashMap, TreeMap, LinkedHashMap 등이 가장 유명하고, 개발자들이 애용하는 클래스다
Hashtable이라는 클래스도 있다
Hashtable은 일반적인 Map 인터페이스를 구현한 클래스들과는 다른점이 있다
- Map은 컬렉션 뷰를 사용하지만, Hashtable은 Enummeration 객체를 통해서 데이터를 처리한다
- Map은 키, 값, 키-값 쌍으로 데이터를 순환하여 처리할 수 있지만, Hashtable은 이 중에서 키-값 쌍으로 데이터를 순환하여 처리할 수 없다
- Map은 이터레이션을 처리하는 도중에 데이터를 삭제하는 안전한 방법을 제공하지만, Hashtable은 그러한 기능들을 제공하지 않는다
HashMap 클래스와 Hashtable 클래스 간의 차이
기능 | HashMap | Hashtable |
키나 값에 null 저장 가능 여부 | 가능 | 불가능 |
여려 쓰레드 안전 여부 | 불가능 | 가능 |
Hashtable을 제외한 Map으로 끝나는 클래스들을 여러 쓰레드에서 동시에 접근하여 처리할 필요가 있을 때에는 다음과 같이 선언하여 사용해야만 한다
Map m = Collections.synchronizedMap(new HashMap(...));
Hashtable, Vector는 클래스가 쓰레드에 안전하게 개발되어 있다
JDK 1.2부터 제공되는 대부부의 Collection 관련 클래스들은 이와 같은 처리를 해야하거나
이름에 Concurrent가 포함되어 있어야만 쓰레드에 안전하게 사용할 수 있다
HashMap
HashMap 클래스의 상속 관계
java.lang.Object
ㄴjava.util.AbstractMap<K, V>
ㄴjava.util.HashMap<K, V>
HashMap이 구현한 인터페이스들
인터페이스 | 용도 |
Serializable | 원격으로 객체를 전송하거나, 파일에 저장할 수 있음을 지정 |
Cloneable | Object 클래스의 clone() 메소드가 제대로 수행될 수 있음을 지정 |
Map<E> | 앱의 기본 메소드 지정 |
HashMap 클래스의 생성자들
생성자 | 설명 |
HashMap() | 16개의 저장 공간을 갖는 HashMap 객체를 생성한다 |
HashMap(int initialCapapcity) | 매개 변수만큼의 저장 공간을 갖는 HashMap 객체를 생성한다 |
HashMap(int initialCapapcity, float loadFactor) | 첫 매개 변수의 저장 공간을 갖고, 두 번째 매개 변수의 로드팩터를 갖는 HashMap 객체를 생성한다 |
HashMap(Map<? extends K, ? extends V> m) | 매개 변수로 넘어온 Map을 구현한 객체에 있는 데이터를 갖는 HashMap 객체를 생성한다 |
대부분 HashMap 객체를 생성할 때에는 매개 변수가 없는 생성자를 사용한다
HashMap에 담읠 데이터의 개수가 많은 경우에는 초기 크기를 지정해 주는 것을 권장한다
Map의 get() 메소드가 호출되는 원리
HashMap에 객체가 들어가면 hashCode() 메소드의 결과 값에 따른 bucket이라는 list형태의 바구니가 만들어진다
서로 다른 키가 저장되어있는데
hashCode() 메소드의 결과가 동일하다면, 이 버켓에 여러 개의 값이 들어갈 수 있다
따라서, get() 메소드가 호출되면 hashCode()의 결과를 확인하고, 버켓에 들어간 목록에 데이터가 여러 개일 경우 equals() 메소드를 호출하여 동일한 값을 찾게 된다
따라서, 여러분들이 키가 되는 객체를 직접 작성할 때에는 개발툴에서 제공하는 hashCode()와 equals()를 자동 생성해주는 기능을 사용하여 해당 메소드를 꼭 구현해 놓기 바란다
Map에 데이터를 넣을 때 사용하는 메소드
Map에 데이터를 넣을 때는 put()이라는 메소드를 사용한다
//Java_2_3_4_src1
public void checkHashMap(){
HashMap<String, String> map = new HashMap<String, String>();
map.put("A", "a");
}
어느것이 Key이고 어느것이 Value일까?
Map을 생각할 때에는 항상 Key - Value 순서대로 생각하기 바란다
Map에 값을 꺼낼 때 사용하는 메소드
//Java_2_3_4_src2
public void checkHashMap(){
HashMap<String, String> map = new HashMap<String, String>();
map.put("A", "a");
System.out.println(map.get("A")); //1
System.out.println(map.get("B")); //2
}
//1의 결과는 당연히 a가 나올것이다
//2의 결과는 어떨까? map이라는 이름의 Map에 B라는 Key를 가진 어떠한 Value도 넣어주지 않았다
없는 값을 호출했으니 분명 ArrayIntexOutOfBoundsException이라는 예외가 발생할 것이다
하지만, Map에서는 존재하지 않는 키로 get() 메소드를 호출할 경우에는 null을 리턴한다
이미 존재하는 값을 새로운 값으로 대치할때 사용하는 메소드
put() 메소드를 사용해서 이미 존재하는 키로 값을 넣어주면 기존의 값을 새로운 값으로 대치한다
새로운 값을 추가하거나, 기존의 값을 수정할 때 모두 put() 메소드를 사용한다
//Java_2_3_4_src3
public void checkHashMap(){
HashMap<String, String> map = new HashMap<String, String>();
map.put("A", "a");
map.put("A", "aa");
}
Map에 어떤 Key가 있는지 확인하는 메소드
HashMap에 어떤 키가 있는지를 확인하려면 KeySet() 메소드를 사용하면 된다
KeySet() 메소드의 리턴 타입은 Set이다, Set의 제네릭 타입은 키의 제네릭 타입과 동일하게 지정해주면 된다
//Java_2_3_4_src4
public void checkKeySet(){
HashMap<String, String> map = new HashMap<String,String>();
map.put("A", "a");
map.put("B", "b");
map.put("C", "c");
Set KeySet = map.keySet();
for(String temp : KeySet){
System.out.println(temp+"="+map.get(temp));
}
}
Map에 어떤 Value가 있는지만 확인하는 메소드
HashMap 객체에 담겨있는 값만 필요할 경우에는 values()라는 메소드가 있다
//Java_2_3_4_src5
public void checkValues(){
HashMap<String, String> map = new HashMap<String, String>();
map.put("A", "a");
map.put("B", "b");
map.put("C", "c");
Collection values = map.values();
for(String tempValue : values){
System.out.println(tempValue);
}
}
values() 라는 메소드를 사용하면 HashMap에 담겨있는 값의 목록을 Collection 타입의 목록으로 리턴해 준다
클래스 선언문 앞에 Collection 인터페이스를 import 해야만 한다
Map에 어떤 Key가 있는지 어떤 Value가 있는지 둘다 확인하는 메소드
entrySet()이라는 메소드를 사용할 수 있다
이 메소드를 사용하면 Map에 선언된 Entry라는 타입의 객체를 리턴한다
Entry에는 단 하나의 키와 값만이 저장된다
따라서, getKey()와 getValue()라는 메소드를 사용하면 키와 값을 간단하게 가져올 수 있다
//Java_2_3_4_src6
public void checkHashMapEntry(){
HashMap<String, String> map = new HashMap<String, String>();
map.put("A", "a");
map.put("B", "b");
map.put("C", "c");
map.put("D", "d");
Set<Map.Entry<String, String>> entries = map.entrySet();
for(Map.Entry<String, String> tempEntry : entries){
System.out.println(tempEntry.getKey()+"="+tempEntry.getValue());
}
}
Set과 Entry타입을 import해야 사용할 수 있다
import java.util.Map.Entry;
import java.util.Set;
Map에 어떤 키나 값이 존재하는지를 확인하는 메소드
어떤 키가 존재하는지를 확인하려면 containsKey() 메소드
어떤 값이 존재하는지를 확인하려면 containsValue() 메소드를 사용하면 된다
//Java_2_3_4_src7
public void checkContains(){
HashMap<String, String> map = new HashMap<String, String>();
map.put("A", "a");
map.put("B", "b");
map.put("C", "c");
map.put("D", "d");
System.out.println(map.containsKey("A"));
System.out.println(map.containsKey("Z"));
System.out.println(map.containsValue("a"));
System.out.println(map.containsValue("z"));
}
결과는 다음과 같다
true
false
true
false
무작정 get() 메소드로 해당 키나 값이 존재하는지 확인하는 것보다는 이렇게 contaionsKey()나 containsValue() 메소드를 사용하는 것이 효과적이다
Map의 데이터를 삭제하는 메소드
remove() 메소드를 사용하면 된다
//Java_2_3_4_src8
public void checkRemove(){
HashMap<String, String> map = new HashMap<String, String>();
map.put("A", "a");
map.remove("A");
System.out.println(map.size());
}
"A"를 키로 갖는 데이터를 삭제하려면, remove() 메소드의 매개 변수로 전달해주면 된다
수행한 결과는 다음과 같다
0
TreeMap
HashMap 객체의 키를 정렬하려면 여러 가지 방법을 사용해야만 한다
가장 간단한? 방법중 하나가
Arrays 라는 클래스를 사용하는 것이다
하지만, 불필요한 객체가 생긴다는 단점이 있다
이러한 단점을 보완하는 TreeMap이라는 클래스가 있다
이 클래스는 저장하면서, 키를 정렬한다
String과 같은 문자열이 정렬되는 기본적인 순서는 "숫자 > 알파벳 대문자 > 알파벳 소문자 > 한글" 순이다
//Java_2_3_4_src9
public void checkTreeMap(){
TreeMap<String, String> map = new TreeMap<String, String>();
map.put("A", "a");
map.put("가", "e");
map.put("1", "f");
map.put("a", "g");
Set<Map.Entry<String, String>> entries = map.entrySet();
for(Map.Entry<String, String> tempEntry : entries){
System.out.println(tempEntry.getKey()+"="+tempEntry.getValue());
}
}
이 메소드의 수행 결과는 다음과 같다
1 = f
A = a
a = g
가 = e
100건, 1000건 정도의 데이터를 처리하고, 정렬을 해야 할 필요가 있다면
HashMap 보다는 TreeMap을 사용하는 것이 더 유리하다
TreeMap이 키를 정렬하는 것은 SortedMap이라는 인터페이스를 구현했기 떄문이다
SortedMap을 구현한 클래스들은 모두 키가 정렬되어 있어야만 한다
키가 정렬이 되었을 떄의 장점은
가장 앞에 있는 키(firstKey())
가장 뒤에 있는 키(lastKey())
특정 키 뒤에 있는 키(higherKey())
특정 키 앞에 있는 키(lowerKey())
등을 알 수 있는 메소드를 제공해 준다
키를 검색하는 프로그램을 작성할 때 매우 도움이 된다
Properties
Java_2_1에서 System 클래스에 대해서 살펴보면서, Properties라는 클래스가 있다고 간단하게 소개했었다
기본적으로, 자바에서는 시스템의 속성을 이 클래스를 사용하여 제공한다
Properties 이 클래스는 Hashtable을 extends하였다
따라서, Map 인터페이스에서 제공하는 모든 메소드를 사용할 수 있다
Properties 클래스에 어떤 메소드들이 있는지 살펴보기 전에 다음 소스를 통해
시스템의 속성값들을 확인하는 방법을 살펴보자
import java.util.Properties;
import java.util.Set;
public class PropertiesSample{
public static void main(String[]args){
PropertiesSample sample = new PropertiesSample();
sample.checkProperties();
}
public void checkProperties(){
Properties prop = System.getProperties();
Set
System 클래스에 static으로 선언되어 있는 getProperties() 메소드를 호출하면, Properties 타입의 객체를 리턴한다
이 객체에 있는 내용을 확인하는 방법은 Map에 있는 데이터를 확인하는 방법과 동일하다
결과는 많은 시스템의 속성들이 출력된다
이러한 시스템에서 제공하는 여러 속성 중에서 앞으로 개발하면서 많이 사용할 값들은 다음과 같다
속성 | 설명 |
user.language | 사용자의 사용 언어 |
user.dir |
현재 사용중인 기본 디렉터리 |
user.home | 사용자 계정의 홈 디렉터리 |
java.io.tmpdir | 자바에서 사용하는 임시 디렉터리 |
file.encoding | 파일의 기본 인코딩 |
sun.io.unicode.encoding | 유니코드 인코딩 |
path.separator | 경로 구분자 |
file.separator | 파일 구분자 |
line.separator | 줄(line) 구분자 |
왜 Properties 클래스를 사용할까?
그 이유는 Properties 클래스에서 추가로 제공하는 메소드들을 보면 알 수 있다
리턴 타입 | 메소드 이름 및 매개 변수 | 설명 |
void | load(InputStream inStream) | 파일에서 속성을 읽는다 |
void | load(Reader reader) | 파일에서 속성을 읽는다 |
void | loadFromXML(InputStream in) | XML로 되어있는 속성을 읽는다 |
void | store(OutputStream out, String comments) | 파일에 속성을 저장한다 |
void | store(Writer writer, String comments) | 파일에 속성을 저장한다 |
void | storeToXML(OutputStream os, String comment) | XML로 구성되는 속성 파일을 생성한다 |
void | storeToXML(OutputStream os, String comment, Stirng encoding) | XML로 구성되는 속성 파일을 생성한다 |
Properties 클래스들의 객체에 시스템 속성만 저장할 수 있는 것은 아니다
여러분들이 사용하는 어플리케이션에서 사용할 여러 속성값들을
Properties 클래스를 사용하여 데이터를 넣고, 뺴고, 저장하고, 읽어들일 수 있다
여러분들이 IO를 배운뒤에 이 Properties를 활용하는 방법의 예를 보는 것이 좋겠지만 일단 어떻게 사용하는지 확인만 해보자
//Java_2_3_4_src11
public void saveAndLoadProperties(){
try{
String fileName = "test.properties";
File propertiesFile = new File(fileName);
FileOutputStream fos = new FileOutputStream(propertiesFile);
Properties prop = new Properties();
prop.setProperty("Writer", "Junn, Jang");
prop.setProperty("WriterHome", "http://www.godOfjava.com");
prop.store(fos, "Basic Properties file.");
fos.close();
FileInputStream fis = new FileInputStream(propertiesFile);
Properties propLoaded = new Properties();
propLoaded.load(fis);
fis.close();
System.out.println(propLoaded);
}catch(Exception e){
e.printStackTrace();
}
}
그냥 일단 "이렇게 Stream이라는 객체를 생성할 수 있구나,,,," 정도로 생각하고 있자
추가로
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
를 하고 실행해보자
Hashtable에 toString() 메소드가 구현되어 있기 때문에 간단하게 Properties 객체를 출력해도 중괄호가 묶인 결과가 출력된다
이번에는 XML로 파일을 저장하고 읽어보자
//Java_2_3_4_src12
public void saveAndLoadPropertiesXML(){
try{
String fileName = "test.xml";
File propertiesFile = new File(fileName);
FileOutputStream fos = new FileOutputStream(propertiesFile);
Properties prop = new Properties();
prop.setProperty("Writer", "Junn, Jang");
prop.setProperty("WriterHome", "http://www.godOfjava.com");
prop.storeToXML(fos, "Basic XML Property file.");
fos.close();
FileInputStream fis = new FileInputStream(propertiesFile);
Properties propLoaded = new Properties();
propLoaded.loadFromXML(fis);
System.out.println(propLoaded);
fis.close();
}catch(Exception e){
e.printStackTrace();
}
}
Properties 클래스를 사용하면 여러 속성을 저장하고, 읽어 들이는 작업을 보다 쉽게 할 수 있다
이렇게 자바의 자료구조들의 정리가 끝이났다
다시 한번 살펴보자
Collection을 구현한 것은 List, Set, Queue며, Map은 별도의 인터페이스로 되어 있다
배열처럼 목록을 처리하기 위한 List의 대표적인 클래스로는
ArrayList와 LinkedList가 있으며 보통은 ArrayList를 많이 사용한다
List처럼 목록을 처리하기는 하지만, 데이터의 중복이 없고, 순서가 필요 없는 Set의 대표적인 클래스는
HashSet, TreeSet, LinkedHashSet이 있다
데이터가 들어온 순서대로 처리하기 위해서 사용하는 Queue의 대표적인 클래스는
LinkedLIst와 PriorityQueue등이 있으며, LinkedLIst는 List에도 속하고 Queue에도 속하는 특이한 클래스이다
Map의 대표적인 클래스에는 HashMap, TreeMap, LinkedHashMap이 있으며
사용 용도에 따라 다르겠지만, 대부분 HashMap을 많이 사용한다
Map의 "Key" 목록은 KeySet() 메소드를 사용하면 Set 타입의 데이터를 얻을 수 있다
Collection의 데이터를 처리하기 위해서는 for 루프를 사용할 수 있지만
iterator() 메소드를 통하여 Iterator 객체를 얻어 각 데이터를 처리할 수도 있다
'Java_2' 카테고리의 다른 글
Java_2_5 (0) | 2020.05.26 |
---|---|
Java_2_4 (0) | 2020.05.20 |
Java_2_3_3 (0) | 2020.03.17 |
Java_2_3_2 (0) | 2020.03.14 |
Java_2_3_1 (0) | 2020.03.01 |
댓글