컬렉션 프레임워크
- 프로그램을 개발 할 때 사용하는 자료를 관리하는 방법론이 자료구조다.
- 자료구조는 프로그램 실행 중 메모리에 자료를 유지, 관리 하기 위해 사용한다.
- Java에서는 필요한 자료구조를 미리 구현하여
java.util
패키지에 제공하고 있고, 이것을 컬렉션 프레임워크 라고 한다.
DAO
- 컬렉션 예제에 사용할 DAO 코드
memberId
와memberName
로 이루어진 getter/setter를 가지는 간단한 DAO다.
Collection 인터페이스
Collection 주요 메소드
boolean add(E e)
: Collection에 객체를 추가한다.void clear()
: Collection의 모든 객체를 제거한다.Iterator<E> iterator
: Collection에 이터에이터를 반환한다.boolean remove(Object o)
: Collection의 인자에 해당하는 인스턴스가 존재하면 제거한다.int size()
: Collection의 요소의 갯수를 반환한다.
List 인터페이스
ArrayList 클래스
- array의 reference type
- ArrayList를 사용하여 작성한 회원 관리 클래스
ArrayList와 Vector 클래스
- Vector역시 ArrayList와 같이 배열을 구현한 클래스다. (since Java 2)
- 둘의 가장 큰 차이는 동기화 (synchronized) 지원 여부다. 두개 이사으이 thread가 동시에 Vector를 사용할 때 오류가 나지 않도록 동기성을 보장한다.
- multi-thread 환경이 아닐때는 ArrayList사용을 권장한다. 동기화를 구현하기 위해 동시에 작업이 이루어지는 자원에 대해 lock을 수행하기 때문이다.
- 즉, 메소드를 호출할 때 배열 객체에 잠금을 하고, 메소드 수행이 끝나면 잠금을 해제한다. Vector는 모든 호출에 대해서 잠금과 해제가 알어나 ArrayList보다 비용이 크다.
- ArrayList로 구현한 코드를 변경하지 않고 동기화가 필요하다면 생성 코드를 다음과 같이 바꾸면 된다.
Collections.synchronizedList(new ArrayList<String>());
다만 이 역시 자료형이 아닌 내부 구현된 메소드 (add, remove 등)에 한해서만 동기성을 보장할 뿐이다. 이에 대해서는 멀티스레드를 다루면서 다시 돌아오겠다.
LinkedList 클래스
- ArrayList와 비교해 배열은 처음 생성시 정적 크기로 선언하고, 물리적 순서와 눈리적 순서가 동일하다. 다만 중간에 자료를 삽입하거나 삭제할 때 나머지 자료를 이동시키거나 배열이 작은 경우 새로운 배열을 만들어 복사한다. 여기서 많은 비용이 발생한다.
- 이런점을 개선한 것이 LinkedList다.
- Linked list는 각 객체에 data부분과 다음 인스턴스를 가리키는 주소를 위한 변수가 있다.
- 논리적으로는 순서가 같으나 물리적인 순서는 메모리 주소에 의해 결정된다.
- 배열에 비해 중간에 자료를 삽입하고 추출하는데 적은 시간이 소요된다.
ArrayList(Array) vs LinkedList
- Array는 자료의 위치가 메모리의 물리적 위치와 같기 때문에 index로 바로 current를 이동 할 수 있지만 LinkedList는 물리적 위치가 같지 않기 때문에 모든 노드를 digging해서 찾아가야 한다.
- LinkedList는 자료를 삽입하거나 추출할 때 새로 생성한 노드 인스턴스를 원하는 위치의 노드와 주소 교환만 하면 되지만 Array는 추출시 뒤에 있는 원소들을 모두 앞으로 이동해야하고 삽입시 배열의 크기가 모자라다면 새롭게 배열을 할당해 기존의 원소들까지 복사해야한다.
- 자료의 변동이 많은 경우는 LinkedList, 변동이 없고 조회가 잦은 경우 Array를 시용하는 것이 효율적이다.
Collection을 순회하는 Iterator
- 순서가 없는 Set을 포함하여 모든 collection은 미리 정의되어 있는 이터레이터를 사용할 수 있다.
Iterator iterator = memberService.iterator();
Iterator 메소드
boolean hasNext()
: 다음 요소(노드)가 있는지 여부를 반환한다E next()
: 다음 요소(노드)를 반환한다
Iterator로 구현한 removeMember()
public boolean removeMember(int memberId) {
Iterator<Member> iterator = list.iterator();
while (iterator.hasNext()) {
Member member = iterator.next();
int id = member.getMemberId();
if (id == memberId) {
list.remove(member);
return true;
}
}
System.out.println("Invalid " + memberId);
return false;
}
더 최적화 된 것은 스트림 할 때 다시 다루자.
Set 인터페이스
- 중복된 요소를 허용하지 않는 자료구조다.
- 순서가 없는 것이 특징이다.
HashSet
HashSet을 활용한 회원관리
public class MemberTest {
public static void main(String[] args) {
MemberService memberService = new MemberService();
Member member1 = new Member(500, "강민철");
Member member2 = new Member(501, "김민철");
Member member3 = new Member(502, "이민철");
Member member3_1 = new Member(502, "아니요");
Member member4 = new Member(503, "최민철");
memberService.addMember(member1);
memberService.addMember(member2);
memberService.addMember(member3);
memberService.addMember(member3_1);
memberService.addMember(member4);
memberService.showAllMember();
}
}
member3_1
을 보면 502로 중복 회원을 추가한다. 아래는 실행결과다
강민철Member ID is 500.
최민철Member ID is 503.
김민철Member ID is 501.
이민철Member ID is 502.
아니요Member ID is 502.
===End of Member===
- 보면
memberId
가 중복인 회원이 추가된 것을 알 수 있다.
인스턴스의 판단을 위한 hashCode()와 equals()
- 인스턴스가 동일한지 판단하기 위한 기준을
memberId
로 만들면 해결할 수 있다. hashCode()
와equals()
를 오버라이딩 하면된다.
//Member.java
@Override
public int hashCode() {
return memberId;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Member){
Member member = (Member)obj;
return this.memberId == member.memberId;
}
return true;
}
- 아래는
Member
수정 뒤 출력화면이다.
강민철Member ID is 500.
김민철Member ID is 501.
이민철Member ID is 502.
최민철Member ID is 503.
- 그러고 나면 중복 회원이 추가되지 않는것을 확인할 수 있다.
TreeSet
- Java의 collection 인터페이스나 Map 인터페이스를 구현한 클래스 중 Tree로 시작하는 클래스는 데이터를 추가후 출력하면 값이 정렬되어있다.
- Java에서는 정렬을 구현하기 위해 binary-tree를 사용한다.
TreeSet
은 데이터를 추가 할때 어떤 기준으로 노드를 비교하여 트리를 형성해야 하는지 정해야 한다.
HashSet과의 차이에 대해서는 자료가 보강되는대로 업데이트 하겠다.
Comparable
- 이것은 Comparable 인터페이스를 DAO에 구현함으로써 기준을 정하면 된다.
- 그리고
compareTo
를 구현함으로써 id를 비교하면 된다.
//MemberComparable
public class MemberComparable implements Comparable<MemberComparable> {
private int memberId;
private String memberName;
...
@Override
public int compareTo(MemberComparable member) {
return (this.memberId - member.memberId);
}
- 다음과 같이 뒤죽박죽 넣더라도
memberService.addMember(member1);
memberService.addMember(member3);
memberService.addMember(member2);
memberService.addMember(member3_1);
memberService.addMember(member4);
- 아래와 같이
memberId
를 기준으로 정렬되는 것을 확인할 수 있다.
강민철Member ID is 500.
김민철Member ID is 501.
이민철Member ID is 502.
최민철Member ID is 503.
===End of Member===
- 오름차순에서 내림차순으로 바꿀려면
compareTo
의 반환을 음수로 하면 된다.
//MemberComparable
@Override
public int compareTo(MemberComparable member) {
return (this.memberId - member.memberId);
}
Comparator
- Comparator는 두개의 인자를 받아 정렬 순위를 결정하는 인터페이스다
compare()
를 구현해 인자를 비교하는 기능을 만든다.
class Compare implements Comparator<String> {
@Override
public int compare(String arg1, String arg2) {
return arg1.compareTo(arg2);
}
}
public class ComparatorTest {
public static void main(String[] args) {
Set<String> set = new TreeSet<String>(new Compare());
set.add("esens");
set.add("ximya");
set.add("mastawu");
System.out.println(set);
}
}
- 아래 실행 결과에서 정렬이 되는것을 확인할 수 있다.
[esens, mastawu, ximya]
- 일반적으로
Comparable
을 더 많이 선호한다. Comparable
이 이미 구현되어 있는 경우 정렬 방식을 정의할 때Comparator
을 사용할 수 있다.
멀티스레드 환경에서 동시성이나 메소드 단위의 언급은 없었는데 멀티스레드를 하면서 다시 다루겠다.