Java - java.util 패키지와 Collection 프레임워크
studyjava.util 패키지
java.util 패키지
- Java 프로그래밍에 유용한 클래스들을 모아둔 패키지
- 날짜와 관련된 클래스인
Date
/Calendar
클래스, 자료구조와 관련된Collection 프레임워크
와 관련된 인터페이스와 클래스 등이 포함되어 있다.
이번 주는 먼저 Collection 프레임워크
에 대해 알아보았다!
Collection 프레임워크
- java.util 패키지의 자료구조 클래스들
- 다수의 데이터를 쉽고 효과적으로 처리할 수 있는 표준화된 방법을 제공하는 클래스의 집합
- 책을 보관하기 위해서 책장을 이용하는 것처럼 다양한 자료들을 다양한 방식으로 관리하기 위한 방법이 필요한데, 이러한 방법을 제공하는 것을 자료구조, 컬렉션 프레임워크이다.
컬렉션 프레임워크의 주요 인터페이스
주요 인터페이스 간의 상속관계
- Collection 인터페이스
- 컬렉션 프레임워크에서 가장 기본이 되는 interface로, 여기에 자료가 있다는 것을 표현한다.
- 중복 허용, 순서 기억 x
- 대표적인 메소드:
add()
,size()
,iterator()
- Collection은 저장된 자료를 하나씩 하나씩 꺼낼 수 있는 Iterator 인터페이스를 반환한다.
-
- Iterator 인터페이스 의 메소드
- 꺼낼것이 있는지 없는지 살펴보는
hasNext()
메소드와 하나씩 자료를 꺼낼때 사용하는next()
메소드
-
- Set 인터페이스
- 중복 허용 x
- Collection 인터페이스를 상속받는다.
add
메소드: 같은 자료가 있으면 false, 없으면 true를 반환한다.
- List 인터페이스
- 중복 허용, 순서 기억
- Set 인터페이스와 마찬가지로 Collection 인터페이스를 상속받는다.
get(int)
메소드: n번째 자료를 꺼낸다.
- Map 인터페이스
- Key와 Value를 가지는 자료구조
put()
메소드를 이용하여 key와 value를 함께 저장한다.- 원하는 값을 꺼낼때는 key를 매개변수로 받아들이는
get()
메소드를 이용하여 값을 꺼낸다. - Map에 저장되어 있는 모든 Key들은 중복된 값을 가지면 안된다.
- Key의 이런 특징 때문에 Map은 자신이 가지고 있는 모든 Key들에 대한 정보를 읽어들일 수 있는 Set을 반환하는
keySet()
메소드를 가지고 있다.
각 인터페이스를 자세히 살펴보기 전에 제네릭에 대해 알아보자!
제네릭(Generic)
- 클래스 내부에서 지정하는 것이 아닌 외부에서 사용자에 의해 지정되는 것
- 제네릭의 선언 및 생성
ArrayList<Integer> list1 = new ArrayList<Integer>();
ArrayList<String> list2 = new ArrayList<Integer>();
LinkedList<Double> list3 = new LinkedList<Double>():
LinkedList<Character> list4 = new LinkedList<Character>();
위 코드에서처럼 <>
안에 데이터타입을 지정해준다.
그리고 위와 같이 선언된 제네릭 클래스(generic class)를 생성할 때에는 타입 변수 자리에 사용할 실제 타입을 명시 한다.
generic을 사용하는 이유
- 제네릭을 사용하면 잘못된 타입이 들어올 수 있는 것을 컴파일 단계에서 방지할 수 있다.
- 클래스 외부에서 타입을 지정해주기 때문에 따로 타입을 체크하고 변환해줄 필요가 없다. 즉, 관리하기가 편하다.
- 비슷한 기능을 지원하는 경우 코드의 재사용성이 높아진다.
> 사용 예시
제네릭 사용 X 코드
Box 클래스
public class Box {
private Object obj;
public void setObj(Object obj){
this.obj = obj;
}
public Object getObj(){
return obj;
}
}
BoxExam 클래스
public class BoxExam {
public static void main(String[] args) {
Box box = new Box();
box.setObj(new Object());
Object obj = box.getObj();
box.setObj("hello");
String str = (String)box.getObj();
System.out.println(str);
box.setObj(1);
int value = (int)box.getObj();
System.out.println(value);
}
}
- Box는 매개변수로 Object를 하나 받아들이고, Object를 반환한다.
- Object를 받아들일 수 있다는 것은 Object의 후손이라면 무엇이든 받아들일 수 있다는 것
제네릭 사용 코드
Generic을 이용하여 Box 클래스 수정
public class Box<E> { // <E>가 제네릭 적용한 것
private E obj;
public void setObj(E obj){
this.obj = obj;
}
public E getObj(){
return obj;
}
}
- Box는 가상의 클래스 E를 사용한다는 의미.
- Object를 받아들이고, 리턴하던 부분이 E로 변경.
- E는 실제로 존재하는 클래스는 아니다.
Generic을 이용하여 수정한 Box를 이용하는 BoxExam 클래스
public class BoxExam {
public static void main(String[] args) {
Box<Object> box = new Box<>();
box.setObj(new Object());
Object obj = box.getObj();
Box<String> box2 = new Box<>();
box2.setObj("hello");
String str = box2.getObj();
System.out.println(str);
Box<Integer> box3 = new Box<>();
box3.setObj(1);
int value = (int)box3.getObj();
System.out.println(value);
}
}
- Generic을 사용함으로써 __선언할때는 가상의 타입으로 선언하고, 사용시에는 구체적인 타입을 설정__함으로써 다양한 타입의 클래스를 이용하는 클래스를 만들 수 있다.
- Generic을 사용하는 대표적인 클래스는 컬렉션 프레임워크와 관련된 클래스이다.
다시 돌아와서 Set, List, Map 인터페이스에 대해 알아보자.
Set 인터페이스
- HashSet
- 인스턴스의 해시값을 기준으로 저장하기 때문에 순서를 보장하지 않는다.
- NULL 값을 허용한다.
- TreeSet보다 삽입, 삭제가 빠르다.
- LinkedHashSet
- 입력된 순서를 보장한다.
- TreeSet
- 이진 탐색 트리(Red-Black Tree)를 기반으로 한다.
- 데이터들이 오름차순으로 정렬된다.
- 데이터 삽입, 삭제에는 시간이 걸리지만 검색, 정렬이 빠르다.
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class SetExam {
public static void main(String[] args) {
Set<String> set1 = new HashSet<>();
boolean flag1 = set1.add("kim");
boolean flag2 = set1.add("lee");
boolean flag3 = set1.add("kim");
System.out.println(set1.size()); //저장된 크기를 출력합니다. 3개를 저장하였지만, 이미 같은 값이 있었기 때문에 2개가 출력
System.out.println(flag1); //true
System.out.println(flag2); //true
System.out.println(flag3); //false
// 1️⃣ Iterator 사용
Iterator<String> iter = set1.iterator();
while (iter.hasNext()) { // 꺼낼 것이 있다면 true 리턴.
String str = iter.next(); // next()메소드는 하나를 꺼낸다. 하나를 꺼내면 자동으로 다음것을 참조한다.
System.out.println(str);
}
// 2️⃣ for문
for (String str : set1) {
System.out.println(str);
}
}
}
- 인덱스가 따로 존재하지 않기 때문에 Iterator 사용
List 인터페이스
- LinkedList
- 양방향 포인터 구조로 데이터 삽입, 삭제가 빠르다.
- ArrayList보다 검색이 느리다.
- ArrayList
- 단반향 포인터 구조로 데이터 순차적 접근에 강점을 가진다.
- 배열을 기반으로 데이터를 저장한다.
- 데이터 삽입, 삭제가 느리다.
- 데이터 검색이 빠르다. ```java import java.util.ArrayList; import java.util.List;
public class ListExam {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
// list에 3개의 문자열을 저장
list.add("kim");
list.add("lee");
list.add("kim");
System.out.println(list.size()); //list에 저장된 자료의 수를 출력 (중복을 허용하므로 3 출력)
for(int i = 0; i < list.size(); i++){
String str = list.get(i);
System.out.println(str);
}
} } ```
Map 인터페이스
- HashMap
- Key에 대한 중복이 없으며 순서를 보장하지 않는다.
- Key와 Value 값으로 NULL을 허용한다.
- 동기화가 보장되지 않는다.
- 검색에 가장 뛰어난 성능을 가진다.
- HashTable
- 동기화가 보장되어 병렬 프로그래밍이 가능하고 HashMap 보다 처리속도가 느리다.
- Key와 Value 값으로 NULL을 허용하지 않는다.
- LinkedHashMap
- 입력된 순서를 보장한다.
- TreeMap
- 이진 탐색 트리(Red-Black Tree)를 기반으로 키와 값을 저장한다.
- Key 값을 기준으로 오름차순 정렬되고 빠른 검색이 가능하다.
- 저장 시 정렬을 하기 때문에 시간이 다소 오래 걸린다.
public class MapExam {
public static void main(String[] args) {
// Key, Value가 모두 String 타입인 HashMap인스턴스
Map<String, String> map = new HashMap<>();
map.put("001", "kim");
map.put("002", "lee");
map.put("003", "choi");
map.put("001", "kang"); // 이미 '001'이라는 key가 존재하므로 'kim'이라는 값이 'kang'으로 변경
System.out.println(map.size()); // 3
System.out.println(map.get("001")); // kang
// map에 저장된 모든 key들을 Set자료구조로 꺼냄
Set<String> keys = map.keySet();
Iterator<String> iter = keys.iterator();
while (iter.hasNext()) {
String key = iter.next();
String value = map.get(key);
System.out.println(key + " : " + value);
}
}
}