개발/Java

Java] Do it 자바 프로그래밍 입문 요약 (셋째마당 - 12)

펭귀니 :) 2019. 6. 18. 18:53

12) 컬렉션 프레임워크

 

제네릭이란?

어떤 값이 하나의 참조 자료형이 아닌 여러 참조 자료형을 사용할 수 있도록 프로그래밍하는 것을 '제네릭(Generic) 프로그래밍'. 참조 자료형이 변환될 때 이에 대한 검증을 컴파일러가 하므로 안정적이다. '컬렉션 프레임워크'도 많은 부분이 제네릭으로 구현되어있다. 

ex) 3D프린터 재료는 파우더, 플라스틱 액체 등 여러 재료를 사용할 수 있습니다.

public class ThreeDPrinter{
	private Object material;
    
    public void setMaterial(Object material){
    	this.material = material;
    }
    
    public Object getMaterial(){
    	return material;
    }
}

이 때, 아래와 같이 사용해야 하는데 Object클래스를 사용하면 다시 원래 자료형으로 반환해주기 위해 매번 형 변환을 해야 하는 번거로움이 있다. 이런 경우 사용하는 프로그래밍 방식이 '제네릭'

ThreeDPrinter printer = new ThreeDPrinter();

Powder p1 = new Powder();
printer.setMaterial(p1); //자동 형변환 됨

Powder p2 = (Powder)printer.getMaterial(); //자동 형변환 안됨

 

제네릭 클래스 정의하기

위의 3D프린터를 제네릭 클래스로 정의하면

public class GenericPrinter<T> { //제네릭 클래스 , t는 type의 약자
	private T material; 
    
    public void setMaterial(T material){
    	this.material = material;
    }
    
    public T getMaterial(){
    	return material;
    }
}

위와 같이 되고, 제네릭 클래스의 사용은 아래와 같이 하면 된다.

GenericPrinter<Powder> powderPrinter = new GenericPrinter<Powder>();
powderPrinter.setMaterial(new Powder());
Powder powder = powderPrinter.getMaterial(); //명시적 형변환을 하지 않음

 

T 자료형에 사용할 자료형을 제한하는 <T extends 클래스>

public class GenericPrinter<T extends Material>{

예를 들어 물을 3D프린터의 재료로 쓸 순 없으니까 제한을 하자. Material이라는 추상클래스를 만들어 해당 클래스를 상속받은 클래스의 재료만 사용 할 수 있도록 하자. ( public class Powder extends Material 등)

 

제네릭 메서드

형식 : public <자료형 매개변수> 반환형 메서드 이름(자료형 매개변수 ...) { }

public static <T, V> double makeRectangle(Point<T, V> p1, Point<T, V> p2){ }

 

컬렉션 프레임워크

자바에서는 필요한 자료구조를 미리 구현하여 java.utill 패키지에서 제공하고 있는데 이를 컬렉션 프레임워크.

컬렉션 프레임워크의 전체 구조는 Collection 인터페이스와 Map 인터페이스 기반으로 이루어져 있다. Collection인터페이스는 하나의 자료를 모아서 관리하는데 필요한 기능을 제공, Map인터페이스는 쌍으로 된 자료들을 관리하는데 유용하다.

 

Collection 인터페이스

Collection인터페이스 하위에 List 인터페이스와 Set 인터페이스가 있다. List를 구현할 클래스는 순차적인 자료를 관리하는 데 사용하는 클래스이고, Set 인터페이스는 수학의 집합처럼 중복되지 않는 객체를 다루는 데 사용한다.

 

Map 인터페이스

보통 key-value 쌍이라고 표현하는데 이 때 키 값은 중복될 수 없다. (학번, 학생) value값은 여러 개일 수도 중복될 수도 있다. 

 

List 인터페이스

ArrayList, Vector, LinkedList 중 ArrayList 먼저 알아보자

ArrayList 클래스

객체 배열을 구현한 클래스이며 컬렉션 인터페이스와 그 하위 List 인터페이스를 구현하였습니다. 객체 순서를 기반으로 순차적으로 자료를 관리하는 프로그램을 구현할 때 사용합니다. 

add()와 remove() 메서드를 사용해 객체를 추가, 제거 할 수 있습니다.

Vector 클래스

ArrayList처럼 배열을 구현한 클래스인데, Vector는 동기화를 지원한다. 멀티스레드 환경이 아닌 경우 속도 때문에 ArrayList를 권장한다. 혹시 ArrayList에서 동기화가 필요할 경우 다음과 같이 사용할 수 있다.

Collections.synchronizedList(new ArrayList<String>());

LinkedList 클래스

배열은 처음 생성할 때 정적 크기로 선언하고, 물리적 순서와 논리적 순서가 동일하다. 처음 선언한 배열 크기 이상으로 요소가 추가되는 경우 크기가 더 큰 배열을 새로 생성하여 각 요소를 복사해야 하는 번거로움이 있다. 이런 점을 개선한 자료 구조를 링크드 리스트라고 한다.

구조 : (자료, 다음 요소의 주소) -> (자료, 다음 요소의 주소) -> (자료, 다음 요소의 주소) -> (자료, 다음 요소의 주소) -> ...

물리적인 메모리는 떨어져 있어도 논리적으로 앞뒤 순서가 있다. ArrayList에 비해 중간에 자료를 넣고 제거하는 데 시간이 적게 걸리고, 크기를 동적으로 증가시킬 수 있다. 다만, i번째 요소를 찾을 때는 배열이 더 쉽고, 구현도 쉽다. 따라서 링크드 리스트는 자료 변동이 많은 경우에 사용하는 것이 효율적이다.

 

Collection 요소를 순회하는 Iterator

Iterator를 사용하여 요소를 순회할 때 사용하는 메서드

1. boolean hashNext() - 이후에 요소가 더 있는지를 체크하는 메서드이며, 요소가 있다면 true를 반환한다.

2. E next() - 다음에 있는 요소를 반환한다.

 

Set 인터페이스

순서와 상관없이 중복을 허용하지 않는 경우에 사용. (주민등록번호, 회원 아이디 등) 대표 클래스에는 HashSet과 TreeSet이 있다.

HashSet 클래스

집합 자료 구조이며, 중복을 허용하지 않는다. 삭제할 때 사용하는 remove()메서드는 삭제할 아이디를 가진 회원을 찾기 위해 Iterator을 사용하며, 만약 아이디가 같으면 HashSet의 remove()메서드를 사용하여 해당하는 회원을 삭제해야 한다.

Integer클래스에 객체가 동일한 경우 처리 방법이 구현되어 있는데, 다른 클래스는 같은 객체를 처리할 방법을 따로 구현해줘야 한다. (equals()와 hashCode() 메서드를 재정의하여 사용)

TreeSet 클래스

TreeSet은 자료의 중복을 허용하지 않으면서 출력 결과 값을 정렬하는 클래스이다. 정렬에는 이진 트리(binary tree)를 사용한다. 이진 검색 트리(BST)는 노드에 저장되는 자료의 중복을 허용하지 않고, 부모가 가지는 자식 노드의 수가 2개 이하이다. 또한 왼쪽에 위치하는 자식 노드는 부모 노드보다 항상 작은 값을 가진다. 반대로 오른쪽에 놓인 자식 노드는 부모 노드보다 항상 큰 값을 가진다. 

- Comparable과 Comparator 인터페이스

Comparable 인터페이스 - 자기 자신과 전달받은 매개변수를 비교 , Comparable은 compareTo()추상 메서드를 포함하고 있다.

@Override
public int compareTo(Member member){
	return (this.memberID - member.memberID);
}

Comparator 인터페이스 - 두 매개변수를 비교 

@Override
public int compare(Member2 mem1, Member2 mem2) {
	return mem1.getMemberId() - mem2.getMemberId();
}

보통은 Comparable을 더 많이 사용한다.

public class Member implements Comparable<member> {

 

Map 인터페이스

key-value 쌍으로 이루어진 객체의 key값은 유일하고, value값은 중복될 수 있다. 가장 많이 사용하는 HashMap 클래스.

HashMap 클래스

자료 관리 방식은 해시 방식으로 한다. 해시 방식의 자료를 저장하는 공간을 해시 테이블이라고 한다. key값이 정해지면 그에 대응하는 해시 테이블의 저장 위치가 정해지는데 이런 위치를 계산하는 함수가 '해시 함수'이다.

index = hash(key) //index는 저장 위치

key를 알고 있는 상태에서 value를 검색하거나 자료를 추가하면 속도가 빠르다. 서로 다른 key값에 같은 index가 반환되는 충돌이 발생하는 경우도 있어서 해시 테이블에 데이터를 꽉 채우지 않고 적정 수준이 되면 테이블을 확장해 충돌 발생 확률을 낮춘다.

- HashMap과 Hashtable

둘 다 쌍으로 이루어진 자료를 관리하는데 사용하지만 Hashtable은 Vector와 마찬가지로 멀티스레드를 위한 동기화를 제공한다. 멀티스레드 환경이 아니라면 HashMap 클래스 사용을 권장.

TreeMap 클래스

key값으로 자료를 정렬하려면 TreeMap을 사용할 수 있다. 이진 검색 트리로 구현되어있으며 key값으로 정렬하므로 key값에 해당하는 클래스에 Comparable이나 Comparator 인터페이스를 구현해야 한다.