[Java Tips] Generic & Collection

Sierra·2023년 1월 28일
0

JAVA Tips

목록 보기
3/3
post-thumbnail

Intro

Java 를 어느정도 쓰다보면 Generic 과 Collection이라는 개념을 마주하게 된다. 사실 크게 어려운 개념은 아니다. Java를 쓰다보면 상당히 자주 마주치게 되는 개념이다.

이번 포스팅의 주제는 Generic과 Collection. Java 나 Kotiln 사용자라면 반드시 알아야 하는 개념들이다.

Generic

하나의 값이 여러가지 데이터 타입을 가질 수 있게 해주는 방법이다.

객체는 여러가지 형태로 존재할 수 있다. 최종적으로 Object 객체에 수렴하겠지만 이러한 객체들은 고유한 데이터 타입을 가지게 된다. 결국 클래스라는 개념은 또 다른 사용자 지정 타입을 어떻게 확장하고 활용하느냐 라는 고민에서 나왔으니까.

우리가 어떠한 함수든, 클래스든 생성하다보면 매개변수를 받게 되는 경우가 있다. 이 매개변수의 값은 상황에 따라서는 타입이 고정되어 있어야 하지만, 또 어떤 상황에 따라서는 타입을 유연하게 변동시켜야 하는 경우도 존재한다.

예를 들어 Spring Framework 로 API 서버를 개발하는 중, 전역 Response 클래스를 개발하는 상황이라면, N개의 요청마다 모두 같은 데이터타입을 사용할 일은 거의 없을것이라 본다. 즉, 최악의 경우에는 N개의 데이터타입을 모두 감안해야 하는 경우도 생긴다는 것이다. 그런 경우엔 아래와 같이 처리하면 된다.

@Data
@RequiredArgsConstructor
public class BaseResponse<T> {
    private final HttpStatus status;
    private final T data;
    private final String message;

    public static <T> BaseResponse<T> success(T resultData) {
        return new BaseResponse<>(HttpStatus.OK, resultData, "SUCCESS");
    }

    public static BaseResponse<String> fail(HttpStatus status, String errorMsg){
        return new BaseResponse<>(status, errorMsg, "FAIL");
    }
}

Request가 성공했다면, 당연히 Response를 보내줘야 한다. 그 안에 첨부되는 데이터는 상황에 따라 다를 수 있기에 Generic으로 처리 한 예시이다. <T> 라는 제네릭으로 어떠한 형태의 객체가 첨부 되더라도 처리할 수 있도록 코딩 해 둔 예시다.

Generic을 명명하는 데 규칙은 정해진게 없다. <T> 자리에 알파벳을 무엇을 넣든 개발자의 마음이다. 하지만 마음대로 했다간, 협업에 문제가 생길 수 있기에 아래와 같은 룰이 존재한다.

네이밍의미
EElement
KKey
NNumber
TType
VValue
S, U, V, etc...2nd, 3rd, 4th types

또한 제네릭을 통해 여러개의 타입을 가지는 클래스를 개발 할 수도 있다.

public interface pair<T, U> {
	public T getFirst();
    public U getSecond();
}

public class PairClass<T, U> implements pair<T, U> {
	
    private T first;
    private U second;
    
    public PairClass(T first, U second) {
    	this.first = first;
        this.second = second;
    }
    
    @Override
    public T getFirst() {return first;}
    
    @Override
    public U getSecond() {return second;}

}

위는 Key값과 Value 값을 가지는 pair라는 클래스를 구현 한 예시이다. C++에서도 마찬가지로 Pair STL이 존재한다. 해당 Collection 은 Java에서 따로 제공하지 않지만, 이렇게 쉽게 만들어서 활용할 수 있다.

Collection


Collection이라는 영어단어 그대로 데이터 타입의 집합이라는 의미다. 어떠한 형태의 객체들을 모아다가 처리할 수 있는 집합, 및 그룹을 의미하며 Java Collection Framework 는 이러한 자료구조 클래스와 이를 구현하는 클래스를 정의하는 인터페이스를 제공한다. 위 사진은 Java Collection Framework 의 상속 구조이다.

Map 은 따로 Collection 인터페이스를 상속받고 있지않다. 하지만 Collection으로 분류된다. 이유는 같은 성격을 가지고 있기 때문이다.

여러 Collection 들을 사용 해보면 드는 의문들이 조금씩 생긴다. 무슨 차이지...? Map 도 종류가 한두개가 아니지 않은가. 보통 알고리즘 문제 풀 때 쓰는 HashMapHashTable 과 무슨 차이인가? 한번 정리 해보았다.

Iterable

우선 Iterable 인터페이스를 상속받는 Collection 들에 대해 정리 해 보자. 그 전에 Iterable 에 대해 조금 알아보자면, Collection 내 데이터를 하나 씩 읽어올 때 사용할 수 있는 hasNext(), next(), remove() 등의 메소드를 제공해주는 Interator 메소드를 제공해주는 인터페이스다. Collection 내에 있는 모든 데이터가 인덱스로 접근할 수 있는 것은 아니기 때문에 이러한 기능을 제공 해 주어야지만 Collection 내의 데이터를 하나 씩 접근해서 처리할 수 있다.

List<Integer> list = new ArrayList<>();
//데이터 입력 과정 생략
Iterator it = list.iterator();

while(it.hasNext()){
	System.out.println(it.next());
}

List

우리가 자료구조를 배우게 되면 아주 초창기에 알게 되는 개념이다. 순서가 존재하고 데이터의 중복을 허용한다. 구현 클래스는 아래와 같다.

  • LinkedList
    • 양방향 포인터 구조를 이룬다. 데이터의 삽입 삭제가 자주 있는 경우 유리하다.
    • Stack, Queue, Deque 를 만드는 용도로 쓰인다.
    • 단, 탐색 속도가 느리다는 단점이 있다.
  • Vector
    • 내부에서 동기화 처리가 일어나 성능이 좋지 않아 잘 쓰이지 않는다.
  • ArrayList
    • 단방향 포인터구조를 가진다.
    • 각 데이터에 대한 인덱스를 가져야 하는 경우 쓰인다. 탐색 성능이 뛰어나다.
    • 단, 단방향 포인터 구조인 만큼 삽입 및 삭제 성능에서 LinkedList에 비해 불리하다.
  • Stack
    • FILO 방식의 Collection

Queue

Queue 는 LinkedList를 통해 구현된다. 하지만 Priority Queue는 구현 방식이 List 가 아니라 Heap 으로 처리되기 때문에 Queue를 상속받지만, 다른 클래스로 분리되었다.
Priority Queue의 특징에 대한 설명은 생략하겠다.

Set

데이터의 중복을 허용하지 않는다는 특징을 가졌다. 구현 클래스는 아래와 같다.

  • HashSet
    • hashCode() 메소드를 통해 코드를 얻은 다음, 저장 되어있는 객체들의 해시 코드와 비교하여 중복된 코드가 있는 경우 저장하지 않는 방식으로 중복을 제거한다.
    • hash 를 쓰기 때문에 탐색속도가 매우 빠르다.
    • 순서를 보장할 수 없다.
  • TreeSet
    • NavigableSet, SortedSet 인터페이스를 상속받는다.
    • Binary Search Tree 구조로 이루어져있다.
    • 데이터의 추가 삭제에 시간은 걸리지만 정렬 된 순서를 보장한다.

Map

Key, Value 쌍으로 이루어져있다. Iterable 인터페이스를 따로 상속받지는 않기에 값을 탐색하려면 또 다른 방법을 사용해야 한다. KeySet() 과 같이 해당 Map 을 이루고 있는 데이터들의 Iterable Collection 을 따로 뽑아서 탐색하거나 해야 한다.

Map 인터페이스를 상속받는 구현 클래스들은 아래와 같다.

  • TreeMap
    • Red black Tree로 이루어져있다. 그러므로 트리의 형태가 루트 값 기준으로 균형잡힌 채 유지된다.
    • 정렬 된 순서대로 key와 Value를 저장하여 검색이 빠르다.
    • 데이터 추가 및 삭제 과정에서 정렬이 이뤄지므로 HashMap 보다 느리다.
  • HashMap
    • 중복과 순서가 허용되지 않는다.
    • null 값이 올 수 있다.
  • HaspTable
    • null 값이 올 수 없다.
    • HashMap 에 비해 느리지만 동기화를 지원한다.
  • LinkedHashMap
    • 입력 된 순서대로 Key의 순서가 보장된다.

Outro

지금까지 Generic 과 Collection 에 대해 알아 보았다. 자주 쓰고 있고 이미 알고 있는 내용이더라도 주로 쓰는 것은 정해져 있는 편이라 주로 쓰지 않던 Collection 들의 원리까지 알아볼 수 있었다. 각 Collection 들의 특징을 잘 이해하여 상황에 따라 유동적으로 Collection 을 선언하여 데이터를 처리할 수 있다면 코드도 좀 더 깔끔해질 수 있고 로직 상에 성능 로스를 일으킬 요소도 줄일 수 있다.

다음 주제는 에러 처리로 돌아오겠다.

Reference

https://www.geeksforgeeks.org/how-to-learn-java-collections-a-complete-guide/

profile
블로그 이전합니다 : https://swj-techblog.vercel.app/

0개의 댓글