[Java 25] 자바 - 컬렉션 프레임워크 (1)
학습목표
- 컬렉션 프레임워크의 기본적인 개념을 이해한다.
- List, Set, Map의 사용법을 알고있다.
컬렉션 프레임워크 소개
Collections Framework Class diagram
출처: 생활코딩
자료구조를 바탕으로 객체들을 효율적으로 추가, 삭제, 검색할 수 있도록 java.util 패키지에 컬렉션과 관련된 인터페이스와 클래스들을 포함시켜 놓았습니다. 이를 총칭해 컬렉션 프레임워크라고 합니다.
인터페이스 분류 | 특징 | 구현 클래스 | |
collection | List | - 순서를 유지하고 저장 - 중복 저장가능 | ArrayList, Vetor, LinkedList |
collection | Set | - 순서를 유지하지 앟고 저장 - 중복 저장 안됨 | HashSet, TreeSet |
Map | - 키와 값의 쌍으로 저장 - 키는 중복 저장 안됨 | HashMap, Hashtable, TreeMap, Properties |
List 컬렉션
- 객체를 일렬로 늘어놓은 구조
- 객체를 인덱스로 관리하기 때문에 객체를 저장하면 자동 인덱스가 부여되고 인덱스를 통해 검색, 삭제가 가능
- List 컬렉션은 객체 자체를 저장하는 것이 아니라 객체의 번지를 참조
- 동일한 객체를 중복 저장이 가능한데, 이 경우 동일한 번지가 참조( null도 저장 가능 )
ArrayList
- 생성할 때 크기가 고정되는 배열과 달리, ArrayList는 저장 용량을 초과한 객체들이 들어오면 자동적으로 저장용량 늘어남
- ArrayList에 객체를 추가하면 인덱스 0부터 차례대로 저장
- 특정 인덱스의 객체 제거하면 이후의 모든 인덱스가 1씩 당겨지고 특정 인덱스에 객체 삽입하면 이후의 모든 인덱스가 1씩 밀린다.
List<String> list = new ArrayList<String>(); //컬렉션 생성
list.add("홍길동"); //컬렉션에 객체를 추가
String name = list.get(0); //컬렉션에서 객체 검색, 홍길동 바로 얻음
예제 1
import java.util.ArrayList;
import java.util.List;
public class ArrayListExample {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
//String 객체를 저장
list.add("Java");
list.add("JVM");
list.add("abcd");
list.add("ABCD");
//저장된 총 객체수 반환
int size = list.size();
System.out.println("총 객체수 : " + size);
System.out.println();
//2번 인덱스의 객체 얻기
String skill = list.get(2);
System.out.println("2 : " + skill);
System.out.println();
// 저장된 객체 수만큼 루핑
for(int i =0; i <list.size(); i++) {
String str = list.get(i);
System.out.println(i + ":" + str);
}
System.out.println();
list.remove(2); // 2번 인덱스 객체 삭제
list.remove(2); // 2번 인덱스 객체 삭제
list.remove("abcd");
// 저장된 객체 수만큼 루핑
for(int i =0; i <list.size(); i++) {
String str = list.get(i);
System.out.println(i + ":" + str);
}
}
}
실행결과
총 객체수 : 4
2 : abcd
0:Java
1:JVM
2:abcd
3:ABCD
0:Java
1:JVM
예제 2
import java.util.Arrays;
import java.util.List;
public class ArraysAsListExample {
public static void main(String[] args) {
List<String> list1 = Arrays.asList("홍길동", "김길동", "자바"); //왜 new 안해줌?
for(String name: list1) {
System.out.println(name);
}
List<Integer> list2 = Arrays.asList(1,2,3);
for(int value : list2) {
System.out.println(value);
}
}
}
실행결과
홍길동
김길동
자바
1
2
3
Set 컬렉션
- List 컬렉션은 저장 순서를 유지하지만, Set 컬렉션은 저장 순서가 유지되지 않음
- 수학의 집합이라고 생각하면 된다.
Set<String> set = ...;
set.add("홍길동"); //객체 추가
set.add("김길동");
set.remove("홍길동"); //객체 삭제
- Set 컬렉션은 인덱스로 객체를 검색해서 가져오는 메소드는 없다. 대신 전체 객체를 대상으로 한번씩 반복해서 가죠오는 반복자(Iterator)를 제공한다.
Set<String> set = ...;
Iterator<String> iterator = set.iterator();
- 다음은 Iterator 인터페이스에 선언된 메소드들이다.
리턴 타입 | 메소드명 | 설명 |
---|---|---|
boolean | hasNext() | 가져올 객채가 있으면 true 리턴하고 없으면 false 리턴 |
E | next() | 컬렉션에서 하나의 객체를 가져온다 |
void | remove() | Set 컬렉션에서 객체를 가져온다 |
- Iterator에서 하나의 객체를 가져올 때는 next() 메소드를 사용한다. next() 메소드를 사용하기 전에 먼저 가져올 개체가 있는지 확인
- hasNext() 메소드는 가져올 객체가 있으면 true 리턴하고 더 이상 가져올 객체가 없으면 false를 리턴
- 따라서 true가 리턴될 때 next() 메소드를 사용해야 한다.
Set<String> set = ...;
Iterator<String> iterator = set.iterator();
// 저장된 객체 수만큼 루핑한다
while (iterator.hasNext()) {
// String 객체 하나를 가져옴
String str = iterator.next();
}
- Iterator 사용하지 않더라도 향상된 for문을 이용해서 전체 객체 대상으로 반복이 가능
Set<String> set = ...;
// 저장된 객체 수만큼 루핑한다
for(String str : set) {
}
- remove() 메소드를 통한 객체 제거 가능
while(iterator.hasNext()) {
String str = iterator.next();
if(str.equals("홍길동")) {
iterator.remove();
}
}
HashSet
- HashSet 생성을 위한 기본 생성자
Set<E> set = new HashSet<E>();
- HashSet은 객체들을 순서 없이 저장하고 동일한 객체는 중복 저장하지 않는다.
public class HashSetExample {
public static void main(String[] args) {
Set<String> set = new HashSet<String>();
set.add("Java");
set.add("JDBC");
set.add("Servlet/JSP");
set.add("Java"); // "Java"는 한번만 저장됨
set.add("abcde");
int size = set.size(); // 저장된 객체 수 얻기
System.out.println("총 객체수 : " + size);
Iterator<String> iterator = set.iterator(); // 반복자 얻기
// 객체 수만큼 루핑
while (iterator.hasNext()) {
String element = iterator.next(); // 한개의 객체를 가져온다
System.out.println("\t" + element);
}
// 객체 삭제
set.remove("JDBC");
set.remove("abcde");
System.out.println("총 객체수: " + set.size());
iterator = set.iterator(); // 반복자 얻기
// 객체 수만큼 루핑
while(iterator.hasNext()) {
String element = iterator.next();
System.out.println("\t" + element);
}
set.clear(); // 모든 객체 제거
if(set.isEmpty()) { System.out.println("비어 있음"); }
}
}
실행결과
총 객체수 : 4
Java
abcde
JDBC
Servlet/JSP
총 객체수: 2
Java
Servlet/JSP
비어 있음
예제 2 사용자 정의 클래스인 Member를 만들고 hashCode()와 equals() 메소드를 오버라이딩하였다. 이를 통해 인스턴스가 달라도 이름과 나이가 동일하면 동일한 객체로 간주하여 중복 저장을 방지한다.
public class Member {
public String name;
public int age;
public Member(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) { //name과 age 값이 같으면 true 리턴
if (obj instanceof Member) {
Member member = (Member) obj;
return member.name.equals(name) && (member.age == age);
} else {
return false;
}
}
@Override
public int hashCode() { //name과 age 값이 같으면 동일한 hashCode 리턴
return name.hashCode() + age;
}
}
import java.util.*;
public class HashSetExample2 {
public static void main(String[] args) {
Set<Member> set = new HashSet<Member>();
//인스턴스는 다르지만 내부 데이터가 동일하므로 객체 1개만 저장
set.add(new Member("홍길동", 20));
set.add(new Member("홍길동", 20));
System.out.println("총 객체수 : " + set.size());
}
}
실행결과
총 객체수 : 1
Map 컬렉션
- 키(key)와 값(value)으로 구성된 Entry 객체를 저장하는 구조
- 키와 값은 모두 객체이다.
- 키는 중복 저장될 수 없고, 값은 중복 저장이 가능하다.
- 만약 기존에 저장된 키와 동일한 키로 값을 저장하면 기존의 값은 없어지고 새로운 값으로 대치된다.
Map<String, Integer> map = ~;
map.put("홍길동", 30); //객체 추가
int score = map.get("김길동"); //객체 찾기
map.remove("홍길동"); //객체 삭제
- 키를 알고 있다면 get() 메소드로 간단하게 객체를 찾아오면 되지만, 저장되니 객체를 대상으로 하나씩 얻고 싶은 경우에는 두가지 방법을 사용할 수 있다.
방법 1
keySet() 메소드로 모든 키를 Set 컬렉션으로 얻은 다음, 반복자를 통해 키를 하나씩 얻고 get() 메소드를 통해 값을 얻는다.
Map<K, V> map = ~;
Set<K> keyset = map.keySet();
Iterator<K> keyIterator = keySet.iterator();
while (keyiterator.hasNext()) {
K key = keyIterator.next();
v value = map.get(key);
}
방법 2
entrySet() 메소드로 모든 Map.Entry를 Set 컬렉션으로 얻은 다음, 반복자를 통해 Map.Entry를 하나씩 얻고 getValue() 메소드를 이용해 키와 값을 얻으면 된다.
Set<Map.Entry<K, V>> entrySet = map.entrySet();
Iterator<Map.Entry<K, V>> entryIterator = entrySet.iterator();
while(entryIterator.hasNext()) {
Map.Entry<K, V> entry = entryIterator.next();
K key = entry.getKey();
V value = entry.getValue();
}
HashMap
- HashMap의 키로 사용할 객체는 hasCode()와 equals() 메소드를 재정의해서 동등 객체가 될 조건을 정해야 한다.
- 동등 개체, 즉 동일한 키가 될 조건은 hashCode()의 리턴값이 같아야 하고, equals() 메소드가 true를 리턴해야 한다.
- 주로 키 타입은 String을 많이 사용하는데, String은 문자열이 같을 경우 동등 객체가 될 수 있도록 hashCode()와 equals() 메소드가 재정의되어 있다
- HashMap을 생성하기 위해서는 키 타입과 값 타입을 파라미터로 주고 기본 생성자를 호출하면 된다.
Map<K, V> map = new HashMap<K, V>(); //K: 키 타입, V: 값 타입
- 키와 값의 타입은 기본타입을 사용할 수 없고 클래스 및 인터페이스 타입만 가능
- 키로 String 타입을 사용하고 값으로 Integer 타입을 사용하는 HashMap은 다음과 같이 생성된다.
Map<String, Integer> map = new HashMap<String, Integer>();
예제 1
public class HashMapExample {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("홍길동", 85);
map.put("박길동", 90);
map.put("김길동", 80);
map.put("홍길동", 95); // "홍길동" 키가 같기 떄문에 제일 마지막에 저장한 값으로 대치
System.out.println("총 Entry 수 : " + map.size());
// 객체 찾기
System.out.println("\t홍길동 : " + map.get("홍길동")); //이름(키)으로 점수(값)를 검색
System.out.println();
// 객체를 하나씩 처리
Set<String> keySet = map.keySet(); //Key Set 얻기
Iterator<String> keyIterator = keySet.iterator();
while (keyIterator.hasNext()) {
String key = keyIterator.next();
Integer value = map.get(key);
System.out.println("\t" + key + " : " + value);
}
System.out.println();
// 객체 삭제
map.remove("홍길동");
System.out.println("총 Entry 수 :" + map.size());
// 객체 하나씩 처리
Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
Iterator<Map.Entry<String, Integer>> entryIterator = entrySet.iterator();
while (entryIterator.hasNext()) {
Map.Entry<String, Integer> entry = entryIterator.next();
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println("\t" + key + " : " + value);
}
System.out.println();
map.clear();
System.out.println("총 entry 수: " + map.size());
}
}
실행결과
총 Entry 수 : 3
홍길동 : 95
박길동 : 90
김길동 : 80
홍길동 : 95
총 Entry 수 :2
박길동 : 90
김길동 : 80
총 entry 수: 0
예제 2
class Student {
public int sno;
public String name;
public Student(int sno, String name) {
this.sno = sno;
this.name = name;
}
public boolean equals(Object obj) { //학번과 이름이 동일할 경우 true 리턴
if (obj instanceof Student) {
Student student = (Student) obj;
return (sno == student.sno) && (name.contentEquals(student.name));
} else {
return false;
}
}
// 학번과 이름이 같다면 동일한 값을 리턴
public int hashCode() {
return sno + name.hashCode();
}
}
public class HashMapExample2 {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<String, Integer>();
//학번과 이름이 동일한 경우 같은 키로 인식
map.put("홍길동", 95);
map.put("홍길동", 95);
System.out.println("총 Entry 수 : " + map.size());
}
}
댓글남기기