2. Data_Access

DAO

Data Access Object pattern은 비즈니스(어플리케이션)레이어와 영속성(persistence)레이어를 추상적 API를 이용해 분리하는 패턴을 의미한다. 이렇게 만들면 DB 벤더나 persistence 어플리케이션은 변경해도 DAO만 변경하면 대응 가능해서 유연성이 올라간다.

Collection Framework

자바의 Collection에 대해 알아보자

Collection 인터페이스의 명세는

public interface Collection<E> Extends Iterable<E>

로 Iterable 인터페이스를 확장하고 있다. Iterable 인터페이스의 메서드는

iterator() 하나로 Iterator 인터페이스를 이용해 데이터를 순차적으로 가져올 수 있다.

Collection 인터페이스의 메소드 목록은 다음과 같다.


List

List에서 많이 쓰이는 클래스들은 java.util 패키지의 ArrayList, Vector, Stack, LinkedList 등이 있다. ArrayList는 Thread safe 하지 않고 Vector는 Thread safe하다 Stack은 Vector를 extends해서 만들어졌고 LIFO다.

ArrayList

ArrayList는

java.lang.Object
    java.util.AbstractCollection<E>
        java.util.AbstractList<E>
            java.util.ArrayList<E>

AbstractCollection은 Collection의 공통 메서드 AbstractList는 List의 공통 메서드들을 구현해 놓은 것이다.

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable

생성자

ArrayList의 생성자는 3가지 종류가 있다.

위에 써져 있듯 ArrayList는 초기 크기가 10이기 때문에 10개 이상 데이터가 들어간다면 초기 크기를 지정하는게 낫다.

메서드

마지만 toArray 두 개의 차이는 첫번째는 Object 타입의 배열로만 리턴해서 제네릭을 이용한 ArrayList 객체를 배열로 생성할 때는 좋지 않다.

toArray를 사용할 때 주의할 점은

public static void main(String[] args) {

    ArrayList<String> list = new ArrayList<>();
    list.add("A");
    list.add("B");
    list.add("C");
    // String[] strList = list.toArray(new String[5]);
    String[] strList = list.toArray(new String[0]);
    for(String temp: strList) {
        System.out.println(temp);
    }
}

다음과 같이 타입을 정해줄 때 배열의 갯수를 정해주지 않고 길이를 0으로 지정해주는 게 낫다. 만약 초기 length를 5로 지정해준다면 나머지 두 칸은 null이, Collection의 size가 더 클 경우 모두 null이 들어가게 된다.

Stack

Stack은 Thread에 안전한 LIFO고 ArrayDeque는 Thread에 안전하지 않은 stack이다

java.lang.Object
    java.util.AbstractCollection<E>
        java.util.Vector<E>
            java.util.Stack<E>

Stack은 Vector 클래스의 자식 클래스로 상속을 잘못 받은 케이스다.

public class Vector<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

벡터는 다음과 같은 인터페이스들은 Implement하고 있다. ArrayList와 동일하다.

생성자


Set

Set은 값의 중복이 없는 순서없는 Collection이다. Set에는 3가지 주요 클래스가 존재하는데 HashSet: 순서가 필요 없는 데이터를 해시 테이블제 저장하며 Set 중 성능이 가장 좋다 TreeSet: 저장된 데이터의 값에 따라 정렬되는 셋으로 RB트리로 저장된다. Set보다는 살짝 느리다 LinkedHashSet: 연결된 목록 타입으로 구현해서 해시 테이블제 저장되며 저장된 순서에 따라 값이 정렬된다.

HashSet

java.lang.Object
    java.util.AbstractCollection<E>
        java.util.AbstractSet<E>
            java.util.HashSet<E>

상속관계는 다음과 같고

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable

implements들은 위와 같다. 다른것은 Set 하나밖에 없는데 Set은 순서가 없기 때문에 List와 다르게 get(int index), indexOf(Object o) 같은 메서드들이 존재하지 않는다.

생성자

로드팩터란 데이터의 개수/저장공간을 의미하는데 만약 개수가 증가해 로드팩터보다 커지면 저장공간의 크기가 커지고 재정리 작업을 해야한다. 데이터가 해시 정리를 시작하면 내부의 자료구조를 다시 생성해야 하므로 성능에 영향이 발생한다 대용량 시스템이 아니라면 굳이 건들 필요는 없다.

메서드

Queue

Queue는 FIFO로

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable

List, Deque, Queue를 모두 상속한다

java.lang.Object
    java.util.AbstractCollection<E>
        java.util.AbstractList<E>
            java.util.AbstractSequentialList<E>
                java.util.LinkedList<E>

AbstractSequentialList는 add(), set(), remove() 등의 메서드에 대한 구현 내용이 상이하다.

생성자

메서드

다음과 같이 같은 기능을 하는 것들이 중복되는 이유는 여러 종류의 인터페이스를 구현했기 때문인데 사용하려면 add만 사용하는 등의 방식이 좋을 것 같다.

여기서도 통일된 remove()를 사용하는 것이 나을 것이다.

마지막으로 LinkedList객체를 위한 Iterator 객체에는 특이한 것이 있다.

ListIterator는 next() 외에도 previous() 메서드를 제공해 이전 데이터를 확인할 수 있다.


Map

  • Map은 key와 value로 이루어져 있다

  • 키가 없이 데이터만 저장될 수 없다

  • 값 없이 키만 저장할 수도 없다

  • 키는 해당 Map에서 고유해야 한다

  • 값은 중복되어도 상관없다

Map은 java.util의 Map 인터페이스로 선언되어 있다.

Map을 구현한 클래스는 다양한데 HashMap, TreeMap, LinkedHashMap 등이 가장 유명하고 많이 쓰인다. 추가로 Hashtable 클래스도 있다. Hashtable은 Map과 달리 키나 값에 null이 저장 불가능하고 멀티스레드 상황에서 안전하다.

Hashtable과 Map의 차이는

  • Map은 컬렉션 뷰를 사용하지만, Hashtable은 Enumeration 객체로 데이터를 처리한다

  • Map은 키, 값, 키-값 쌍으로 데이터를 순환하지만 Hashtable은 키-값 쌍으로 데이터를 순환해 처리한다

  • Map은 이터레이션을 처리하는 도중 데이터를 삭제할 방법을 제공하지만 Hashtable은 그런 기능을 제공하지 않는다

동시성 제어를 위해서는 Collection에 Concurrent가 붙어있는 컬렉션을 쓰는 것이 적합하다.

Map m = Collections.sychronizedMap(new HashMap());

다음과 같이 java.util.concurrent 패키지에 소속되어 있다.

HashMap

java.lang.Object
    java.util.AbstractMap<K, V>
        java.util.HashMap<K, V>

HashMap 클래스는 AbstractMap을 확장했고 주요 메서드는 AbstractMap에 구현되어 있다.

생성자

ArrayList와 마찬가지로 데이터의 개수가 많을 때는 초기 크기를 지정해주는 것이 좋다. HashMap에 키에는 기본 자료형과 참조 자료형 모두 들어갈 수 있는데 보통은 int, long String을 많이 사용하지만 직접 클래스를 만들때는 Object의 hashCode()와 equals()메서드를 잘 구현해놔야 한다 hashCode()는 equals를 재정의할 때 꼭 재정의해야 하는데 알고리즘을 통해 버켓으로 객체들을 구분한 것으로 겹치는게 많아지면 성능상 손해가 생긴다.

메서드

    HashMap<String, String> map = new HashMap<>();
    map.put("A", "a");
    map.put("B", "b");
    map.put("C", "c");

    Collection<String> values = map.values();
    for(String tempValue: values) {
        System.out.println(tempValue);
    }

    Set<String> keySet = map.keySet();
    for(String tempKey: keyset) {
        System.out.println(tempkey + map.get(tempKey));
    }

    Set<Map.Entry<String, String>> entries = map.entrySet();
    for(Map.Entry<String, String> tempEntry: entries) {
        System.out.println("tempEntry = " + tempEntry.getValue());
    }

values() 메서드를 통해 값들을 받아올 수 있는데 위의 메서드 설명처럼 Collection객체로 받는다. entrySet() 메서드는 Entry 타입으로 데이터가 저장된다. keySet은 key 값만 필요할 때 사용하는데 만약 value도 필요하면 get() 메서드를 추가로 사용해야 된다. entrySet 둘 다 필요할 때 사용한다.

Map에 키나 값이 존재하는지 확인하는 containsKey()와 containsValue() 메서드가 있다. 존재하면 true, 없으면 false를 리턴한다.

그 외에 get, remove()등이 있다.

TreeMap

HashMap을 정렬하려면 Array로 변환해서 정렬해야 한다. 이런 단점을 보완하기 위해 정렬이 지원되는 TreeMap이 존재한다. 순서는 숫자, 알파벳 대문자, 알파벳 소문자, 한글 순이다. 매우 많은 데이터를 처리할 때는 HashMap을 Array로 정렬하는게 더 낫지만 많지 않은 1000건 정도의 데이터를 처리해야 할 때는 TreeMap이 낫다.

TreeMap은 SortedMap 인터페이스를 구현했기 때문인데 해당 인터페이스는 firstKey(), lastKey(), higherKey(), lowerKey() 등의 메서드를 제공한다.

출처: 오라클 자바의신

Last updated