Java Collection Framework (JCF)

컬렉션은 기본 데이터형이 아닌 참조 데이터형만 저장이 가능하다 따라서 Collection에서의 데이터는 Object 타입의 객체로서 저장이 된다.
기본 데이터 형은 Wrapper 클래스를 이용하여 Boxing 시켜주거나 Integer num = new Integer(5) 기본 데이터형인 5를 Wrapper 클래스의 Integer 타입 객체로 변환 autoboxing으로 저장할 수 있다 즉 오토박싱을 통해 기본 데이터형 컬렉션에 직접 대입하여 저장해도 컴파일러가 자동으로 Wrapper 클래스로 변환해준다 collection.add(11) 저장된 값을 얻어올 때에도 객체화된 데이터를 기본 데이터형으로 바로 얻어올 수 있는데 이 경우는 unboxing이다 collection.get(n)
List 인터페이스
- 동일한 데이터의 중복을 허용한다
- 데이터 저장 순서가 유지된다
- 힙 (heap) 영역 내에서 List는 객체를 일렬로 늘어놓은 구조를 하고 있다
- 객체를 인덱스로 관리하기 때문에 객체를 저장하면 자동으로 인덱스가 부여되고 인덱스로 객체를 검색, 삭제할 수 있다. 이 때 List 컬렉션은 객체 자체를 저장하여 인덱스를 부여하는 게 아니라, 해당하는 인덱스에 객체의 주소값을 참조하여 저장한다
- List 컬렉션에서 공통적으로 사용가능한 추가, 검색, 삭제 메소드를 갖고있다
ArrayList
여기서의 객체는 인덱스로 관리된다. ArrayList에 객체를 추가하면 객체가 인덱스로 관리되는 것이다. 이 점은 일반 배열과 별 다를바 없지만 자바에서 배열은 초기화 시 크기가 고정되어야하고 사용 중에 크기를 변경할 수 없다.
// n 크기만큼 초기화 시
List<T> list = new Arraylist<T>(n);
예시 코드
import java.util.*;
public class Examples {
public static void main(String[] args){
int size;
List<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");
list.add("D");
list.add("E");
list.add("F");
list.add("G");
list.add("H");
list.add("I");
list.add(1, "J");
size = list.size();
System.out.println("저장된 객체 수 : "+ size);
for(int i=0; i<size; i++){
//데이터를 인덱스로 관리
System.out.print(i + "번째 : " + list.get(i));
System.out.println();
}
System.out.println();
System.out.println("----------------변경 후----------------");
System.out.println();
list.remove(1);
list.remove(1);
size = list.size();
for(int i=0; i<size; i++){
System.out.print(i + "번째 : " + list.get(i));
System.out.println();
}
}
}

Vector
동기화된 메소드로 구성되어 있기 때문에 멀티 스레드가 동시에 이 메소드들을 실행할 수 없고 하나의 스레드가 실행을 완료해야만 다른 스레드가 실행을 할 수 있다. 따라서 멀티 스레드 환경에서 안전하게 객체를 추가, 삭제할 수 있다.
LinkedList
List 구현 클래스이므로 ArrayList와 사용하는 메소드는 같지만 내부 구조는 완전 다르다. ArrayList는 내부 배열 객체를 저장해서 인덱스로 관리하지만, LinkedList는 양방향 포인터 구조로, 각각 인접하는 참조를 링크해서 체인처럼 관리한다. 따라서 특정 인덱스의 객체를 제거하거나 삽입하면, 앞 뒤 링크만 변경되고 나머지 링크는 변경되지 않는다 그러므로 중간 삽입/삭제가 빈번할 수록 쓰는 것이 효율적이다.

LinkedList -> Queue 자료구조 구현
import java.util.*;
class QueueExample{
public void method(){
LinkedList<Integer> queue = new LinkedList<Integer>();
//Queue에 삽입
queue.offer(11);
queue.offer(22);
queue.offer(33);
queue.offer(44);
queue.offer(55);
System.out.println(queue);
System.out.println(queue.poll()); //Queue에서 맨 앞 요소 제거하며 읽기
System.out.println(queue);
System.out.println(queue.peek()); //Queue에서 제거하지 않고 맨 뒤 요소 읽기
System.out.println();
ListIterator<Integer> it = queue.listIterator();
if(it.hasNext()){
System.out.println(it.next());
System.out.println(it.next());
System.out.println(it.previous());
System.out.println(it.previous());
}
}
}
public class Sample {
public static void main(String[] args){
QueueExample ex = new QueueExample();
ex.method();
}
}
/*
----------------------------------print----------------------------------
[11, 22, 33, 44, 55]
11
[22, 33, 44, 55]
22
22
33
33
22
-------------------------------------------------------------------------
*/
Set 인터페이스
- 데이터의 저장 순서를 유지하지 않는다
- 같은 데이터의 중복 저장을 허용하지 않는다 따라서 null도 하나의 null만 저장할 수 있다
- Set 컬렉션은 List 컬렉션처럼 인덱스로 객체를 검색해서 가져오는 메소드가 없다. 대신 전체 객체를 대상으로 한번 씩 다 가져오는 반복자, Iterator을 제공한다
Set<String> setExample = new...;
Iterator<String> iterator = setExample.iterator();
이 코드를 구현하여 iterator 객체를 통해 사용할 수 있다
Set<String> setExample = new...;
Iterator<String> iterator = setExample.iterator();
while(iterator.hasNext()){
String getin = iterator.next();
}
Set 인터페이스를 구현한 주요 클래스는 3개가 있다

HashSet
아래는 해시 알고리즘이다, 해시 함수 (hash function)를 사용하여 데이터를 해시 테이블에 저장하고 다시 그것을 검색하는 것이다.

자바에서 해시 알고리즘을 이용한 자료구조는 위의 그림과 같이 배열과 연결 리스트로 구현된다.
저장할 key값과 value를 넣으면 해시함수는 int index = key.hashCode() % capacity 연산으로 배열의 인덱스를 구하여 해당 인덱스에 저장된 연결 리스트에 데이터를 저장하게 된다.
예를 들어, key = 16이라면, hashCode() 메소드가 해당하는 int값을 그대로 반환하며, 16크기의 배열이 존재하므로 이 key의 인덱스는 16%16 = 0 이 된다. 따라서 첫번째 요소에 연결된 연결 리스트에서 검색을 시작한다.
HashSet에서는 순서 없이, 동일한 객체의 중복 저장 없이 저장을 수행한다는 점을 언급했다. 따라서 add() 메소드를 사용하여 해당 HashSet에 이미 존재하고 있는 요소를 추가하려하면, 해당하는 요소를 바로 저장하지 않고 내부적으로 객체의 hashCode()메소드와 equals() 메소드를 호출하며 검사한다. 이 때 사용하는 hashCode()와 equals() 코드는 자신이 정의한 클래스 인스턴스에 대해 프로그래머가 직접 오버라이딩하여 구현할 수 있다.
문자열을 HashSet에 저장할 경우, 같은 문자열을 갖는 String 객체는 동등한 객체로, 다른 문자열을 갖는 String 객체는 다른 객체로 간주된다. 그 이유는 String 클래스가 hashCode()와 equals()메소드를 오버라이딩하여, 같은 문자열일 경우 hashCode()의 리턴값을 같게, equals()의 리턴값은 true로 나온다.
Map 인터페이스
Map 컬렉션에는 키(key)와 값(value)으로 구성된 Entry 객체를 저장하는 구조를 가지고 있다. 여기서 키와 값은 모두 객체이다.
값은 중복 저장이 가능하지만, 키는 중복 저장이 불가능하다. Set과 마찬가지로, Map 컬렉션에서는 키 값의 중복 저장이 허용되지 않는 데, 만약 중복 저장 시 먼저 저장된 값은 저장되지 않은 상태가 된다. 즉, 기존 값은 없어지고 새로운 값으로 대체되는 것이다.
HashSet에서처럼, 프로그래머는 HashMap과 HashTable 모두 키로 사용할 객체에 대해 hashCode()와 equals() 메소드를 오버라이딩하여 같은 객체가 될 조건을 정의할 수 있다.
HashMap
import java.util.*;
class Phone{
private String name;
private String address;
private String telephone;
Phone(String name, String address, String telephone){
this.name = name;
this.address= address;
this.telephone = telephone;
}
public String getName(){ return name; }
public String getAddress(){ return address; }
public String getTelephone(){ return telephone; }
}
public class TelManagement {
//삽입
public static void insert(HashMap<String, Phone> map){
Phone p;
String name, address, telephone;
Scanner s = new Scanner(System.in);
System.out.print("이름 >> ");
name = s.next();
System.out.print("주소 >> ");
address = s.next();
System.out.print("전화번호 >> ");
telephone = s.next();
p = new Phone(name, address, telephone);
map.put(p.getName(), p);
}
//삭제
public static void delete(HashMap<String, Phone> map){
String deletename;
Scanner s = new Scanner(System.in);
System.out.print("이름>> ");
deletename = s.next();
if(map.containsKey(deletename)){
map.remove(deletename);
System.out.println("삭제가 정상적으로 완료되었습니다.");
}
else System.out.println(deletename + "은 등록되지 않은 사람입니다.");
}
//찾기
public static void search(HashMap<String, Phone> map){
Scanner s = new Scanner(System.in);
String searchname;
System.out.print("이름 >> ");
searchname = s.next();
if(map.containsKey(searchname))
System.out.println(searchname + " " + map.get(searchname).getAddress() + " "
+ map.get(searchname).getTelephone());
else System.out.println(searchname + "은 등록되지 않은 사람입니다. ");
}
//전체보기
public static void printall(HashMap<String, Phone> map){
Set<String> names = map.keySet();
Iterator<String> it = names.iterator();
while(it.hasNext()){
String name = it.next();
Phone student = map.get(name);
System.out.println(name + " " + student.getAddress() + " " + student.getTelephone());
}
}
//main
public static void main(String[] args){
int menu;
HashMap<String, Phone> map = new HashMap<String, Phone>();
Scanner s = new Scanner(System.in);
System.out.println("----------------------------------------------------");
System.out.println(" 전화번호 관리 프로그램을 시작합니다. ");
System.out.println("----------------------------------------------------");
while(true){
System.out.print("삽입:0, 삭제:1, 찾기:2, 전체보기:3, 종료:4 >> ");
menu = s.nextInt();
switch(menu){
case 0:
insert(map);
break;
case 1:
delete(map);
break;
case 2:
search(map);
break;
case 3:
printall(map);
break;
case 4:
System.out.println("프로그램을 종료합니다.");
return;
default:
System.out.println("잘못 입력하셨습니다. 다시 입력해주세요.");
}
}
}
'프로그래밍 언어 > Java' 카테고리의 다른 글
[Java] switch문 (0) | 2025.02.02 |
---|---|
[Java] Comparable과 Comparator : 비교를 위한 인터페이스 (0) | 2024.10.22 |
[Java] Collections 클래스 (0) | 2024.10.22 |
[Java] Generic 제네릭 (0) | 2024.10.22 |
[Java] BigDecimal (0) | 2024.09.04 |