Home > Backend > Java > ☕️[Java] 변수와 자료형(4)

☕️[Java] 변수와 자료형(4)
Java Programming Language Backend

변수와 자료형(4)

1️⃣ 자료형에 대한 이해

1. List

자바 프로그래밍에서 List 는 일련의 요소를 저장하는 데 사용되는 순차적인 컬렉션을 나타냅니다.
이는 자바의 java.util.List 인터페이스를 통해 제공되며, 이는 주문된 컬렉션을 관리하기 위한 다양한 메소드를 제공합니다.
List 는 중복된 요소를 포함할 수 있고, 각 요소는 리스트 내에서 특정 위치를 가집니다.
사용자는 이 위치를 인덱스로 사용하여 리스트의 요소에 접근할 수 있습니다.

List 인터페이스의 주요 특징은 다음과 같습니다.

  • 1. 순서 보장 : 리스트는 요소들이 추가된 순서를 유지하며, 각 요소는 특정 인덱스를 통해 접근할 수 있습니다.
  • 2. 요소의 중복 허용 : 같은 값을 가진 요소를 여러 개 포함할 수 있습니다.
  • 3. 동적 배열 : 리스트의 크기는 고정되어 있지 않고, 요소를 추가하거나 삭제함에 따라 동적으로 조절됩니다.

자바에서는 List 인터페이스를 구현하는 몇 가지 클래스가 있습니다.
가장 흔히 사용되는 구현체는 다음과 같습니다.

  • 'ArrayList' : 내부적으로 배열을 사용하여 요소를 저장합니다.
    • 요소의 추가와 인덱스를 통한 접근이 매우 빠르지만, 크기 조절이 필요할 때는 비용이 많이 들 수 있습니다.
  • 'LinkedList' : 각 요소가 다음 요소에 대한 참조와 함께 저장되는 연결 리스트를 사용합니다.
    • 요소의 추가와 삭제는 빠르지만, 인덱스를 통한 요소 접근은 시작부터 요소를 찾을 때까지 순차적으로 검색해야 하므로 시간이 더 걸립니다.
  • 'Vector' : 'ArrayList' 와 비슷하지만, 다중 스레드 환경에서 안전하게 사용할 수 있도록 동기화된 메소드를 제공합니다.

📝 정리.

'List' 는 자바 컬렉션 프레임워크의 일부이며, 데이터를 관리하고 처리하는 데 매우 유용합니다.
프로그래머는 이러한 컬렉션을 사용하여 데이터를 유연하게 조작할 수 있습니다.

1.2 List의 주요 메서드.

자바의 'List' 인터페이스에는 여러 가지 중요한 메서드들이 포함되어 있으며, 이를 통해 리스트 내의 요소들을 조작하고 접근할 수 있습니다.

다음은 'List' 인터체이스에서 제공하는 몇 가지 주요 메서드들입니다.

  1. 'add(E e)': 리스트의 끝에 요소를 추가합니다.
  2. 'add(int index, E element)': 지정된 위치에 요소를 삽입합니다.
  3. 'addAll(Collection<? extends E> c)': 지정된 컬렉션의 모든 요소를 리스트의 끝에 추가합니다.
  4. 'addAll(int index, Collection<? extends E> c)': 지정된 위치부터 컬렉션의 모든 요소를 리스트에 추가합니다.
  5. 'clear()': 리스트에서 모든 요소를 제거합니다.
  6. 'contains(Object o)': 리스트가 특정 요소를 포함하고 있는지 확인합니다.
  7. 'get(int index)': 지정된 위치의 요소를 반환합니다.
  8. 'indexOf(Object o)': 주어진 요소의 첫 번째 인덱스를 반환합니다. 요소가 리스트에 없는 경우 -1을 반환합니다.
  9. 'lastIndexOf(Object o)': 주어진 요소의 마지막 인덱스를 반환합니다. 요소가 리스트에 없는 경우 -1을 반환합니다.
  10. 'isEmpty()': 리스트가 비어 있는지 확인합니다.
  11. 'iterator()': 리스트의 요소에 대한 반복자를 반환합니다.
  12. 'listIterator()': 리스트의 요소를 리스트 순서대로 반복하는 리스트 반복자를 반환합니다.
  13. 'remove(Object o)': 주어진 요소를 리스트에서 처음 발견되는 위치에서 제거하고, 그 결과를 반환합니다.
  14. 'remove(int index)': 지정된 위치에 있는 요소를 리스트에서 제거하고, 그 요소를 반환합니다.
  15. 'replaceAll(UnaryOperator<E> operator)': 주어진 연산자를 사용하여 리스트의 모든 요소를 대체합니다.
  16. 'size()': 리스트에 있는 요소의 수를 반환합니다.
  17. 'sort(Comparator<? super E> c)': 주어진 비교자를 사용하여 리스트를 정렬합니다.
  18. 'subList(int fromIndex, int toIndex)': 지정된 범위의 부분 리스트를 반환합니다.
  19. 'toArray()': 리스트 요소를 배열로 반환합니다.

📝 정리.

이 메서드들을 통해 리스트를 생성, 조회, 수정 및 관리하는 다양한 작업을 수행할 수 있습니다.
List 인터페이스를 사용함으로써 데이터를 효율적으로 처리하고 구조화할 수 있습니다.


2. Map

자바 프로그래밍에서 'Map' 은 키(key)와 값(value)의 쌍을 저장하는 객체입니다.
이는 키를 기반으로 빠르게 값을 검색할 수 있게 해주는 데이터 구조로, 각 키는 고유해야 합니다.(즉, 중복된 키를 가질 수 없습니다.)
'Map''java.util.Map' 인터페이스를 통해 정의되며, HashMap, TreeMap, LinkedHashMap 등 다양한 구현체를 가집니다.

자바의 Map 인터페이스는 키-값 쌍으로 데이터를 저장하고 관리하는 데 중점을 두는 데이터 구조로서, 특히 다음과 같은 주요 특징을 가지고 있습니다.

  • 1. 키에 의한 값 접근 : Map 은 각 값에 고유한 키를 할당하며, 이 키를 사용하여 빠르게 해당 값을 검색할 수 있습니다.
    • 이는 데이터베이스의 인덱스와 유사한 방식으로 작동합니다.
  • 2. 키의 유일성 : 맵 내에서 모든 키는 고유해야 합니다.
    • 즉, 같은 키가 두 번 이상 존재할 수 없으며, 새로운 키-값 쌍을 추가할 때 이미 존재하는 키를 사용하면 기존의 값이 새 값으로 대체됩니다.
  • 3. 값의 중복 허용 : 키는 유일해야 하지만 값은 중복될 수 있습니다.
    • 다른 키가 동일한 값을 가리킬 수 있습니다.
  • 4. 순서의 유무 : 일반적인 Map 구현체들은 키-값 쌍의 순서를 보장하지 않습니다.
    • 그러나 LinkedHashMap 과 같은 일부 구현체는 요소가 추가된 순서대로 반복할 수 있는 기능을 제공합니다.
    • TreeMap 은 키에 따라 정렬된 순서를 유지합니다.
  • 5. 비동기화 및 동기화 : 기본적으로 대부분의 Map 구현체는 동기화되지 않습니다.(HashMap). 이는 멀티 스레드 환경에서 동시 수정이 발생할 경우 안전하지 않을 수 있음을 의미합니다.
    • 반면에 Hashtable 과 같은 구현체는 기본적으로 동기화가 되어 있어 멀티 스레드 환경에서 안전합니다.
    • 또한, Collections.synchronizeMap 메소드를 사용하여 맵을 동기화된 맵으로 변환할 수 있습니다.
  • 6. Null 허용 : 대부분의 Map 구현체는 키와 값으로 null 을 허용합니다.(HashMap, LinkeHashMap).
    • 하지만 Hashtablenull 키나 값을 허용하지 않으며, TreeMap 은 자연 정렬 또는 Comparatornull 을 처리할 수 있는 경우에만 null 키를 허용합니다.

📝 정리.

이러한 특징들로 인해 Map 은 다양한 애플리케이션에서 유연하고 효율적인 데이터 관리를 가능하게 합니다.
데이터를 쉽게 추가, 검색, 삭제할 수 있어 데이터 관리의 복잡성을 줄이고 성능을 최적화하는 데 기여합니다.


3. Generics.

자바 프로그래밍에서 제네릭스(Generics)는 클래스나 메소드에서 사용될 데이터 타입을 추상화하여 코드 작성 시점에는 구체적인 타입을 명시하지 않고, 객체 생성이나 메소드 호출 시점에 실제 사용할 타입을 지정할 수 있도록 하는 프로그래밍 기법입니다.

제네릭스(Generics)는 코드의 재사용성을 높이고, 타입 안정성을 강화하며, 캐스팅에 대한 오류 가능성을 줄이는 데 도움을 줍니다.

3.1 제네릭스(Generics)의 주요 특징.

  • 1. 타입 안전성(Type Safety) : 제네릭스를 사용하면 컴파일 시점에 타입 체크가 가능하여, 실행 시점에서 발생할 수 있는 'ClassCastException' 과 같은 오류를 사전에 방지할 수 있습니다.

  • 2. 재사용성(Reusability) : 하나의 코드를 다양한 타입에 대해 재사용할 수 있습니다.
    • 예를 들어, 제네릭 클래스나 메소드를 정의하면, 다양한 타입의 객체를 저장하거나 처리하는 로직을 단 한번만 작성하여 여러 타입에 걸쳐 사용할 수 있습니다.
  • 3. 코드 간결성(Code Clarity) : 캐스팅을 줄여 코드가 더욱 간결하고 읽기 쉬워집니다.

3.2 제네릭스의 기본 문법.

  • 클래스 선언 : 클래스 이름 뒤에 '<T>' 를 추가하여 제네릭 클래스를 선언합니다.
    • ‘T’ 는 타입 파라미터를 나타내며, 이는 클래스 내에서 사용될 데이터 타입을 대체하는 플레이스홀더 역할을 합니다.
public class Box<T> {
    private T t; // T 타입의 객체를 위한 변수
    
    public void set(T t) {
        this.t = t;
    }
    
    public T get() {
        return t;
    }
}
  • 메소드 선언 : 메소드 반환 타입 앞에 <T> 를 추가하여 제네릭 메소드를 선언합니다.
public <T> T genericMethod(T t) {
    return t;
}
  • 제네릭 타입 제한(Bounded Type Parameters) : 특정 클래스의 하위 클래스만 타입 파라미터로 받도록 제한할 수 있습니다.
    • 이는 'extends' 키워드를 사용하여 지정합니다.
public class Box<T extends Number> {
    private T t;
    
    public void set(T t) {
        this.t = t;
    }
    
    public T get() {
        return t;
    }
}

📝 정리.

제네릭스(Generics)를 사용함으로써 개발자는 보다 타입-안전하고 유지보수가 용이한 코드를 작성할 수 있으며, 실행 시 타입 관련 문제를 효과적으로 줄일 수 있습니다.