
1. 제네릭스(Generics)
제네릭스란?
제네릭스(generics)는 자바 J2SE 5.0 버전 이후에 도입된 개념으로, 자료형을 안전하게 사용할 수 있도록 만들어 주는 기능이다. 제네릭스를 사용하면 자료형을 강제로 바꿀 때 생길 수 있는 캐스팅(Casting) 오류를 줄일 수 있다.
 
ArrayList는 자바의 **제네릭스(Generics)**를 활용하는 대표적인 컬렉션 클래스이다. 제네릭스<>를 사용하면 ArrayList에 저장할 요소의 타입을 지정할 수 있고, 덕분에 형 변환 없이 데이터를 처리한다. (타입 안정성 보장)
 
ArrayList는 ArrayList<E> 형태로 정의하는데, 여기서 E는 저장할 요소의 타입을 나타내는 타입 매개변수이다. ArrayList<String>,ArrayList<Integer>처럼 제네릭스를 사용하여 특정 타입만 저장하도록 제한할 수 있다.
 
일반적인 방식 : ArrayList<String> pitches = new ArrayList<String>();
선호되는 방식 : ArrayList<String> pitches = new ArrayList<>(); 
 (뒷 부분의 자료형은 명확하므로 굳이 적지 않는 것.)
 (오히려 앞뒤에 자료형을 모두 적으면 인텔리제이에서는 경고 메시지를 출력한다. ㅎㅎ)
 
사실 제네릭스 도입 전(~J2SE 1.4)까지는 다음과 같이 사용했다.
ArrayList pitches = new ArrayList(); 
제네릭스 도입 전후의 차이는 ArrayList 자료형 다음에 <String>이 있는가에 있다. 제네릭스를 표현한 <String>은 ‘ArrayList에 담을 수 있는 자료형은 String뿐이다’라는 뜻이다. 즉, 제네릭스를 이용하면 자료형을 좀 더 명확하게 체크할 수 있다는 장점이 있다.
J2SE 5.0 이후 버전에서는 제네릭스를 사용하지 않고 코드를 작성해도 오류는 나지 않는다. 다만, 제네릭스 자료형을 명확하게 지정하라는 경고 메시지가 출력된다.
제네릭스를 안 쓰면?
ArrayList pitches = new ArrayList();
pitches.add("138");
pitches.add("129");
String one = (String) pitches.get(0);
String two = (String) pitches.get(1); 
제네릭스를 사용하지 않으면 ArrayList에 추가하는 객체는 Object 자료형으로 인식된다. Object 자료형은 모든 객체가 상속하고 있는 가장 기본적인 자료형이다. 따라서 ArrayList 객체에 값을 넣을 때는 문제가 없지만 값을 가져올 때는 매번 Object 자료형에서 String 자료형으로 형 변환(casting) 을 해야 한다.
String one = (String) pitches.get(0); // Object 자료형을 String 자료형으로 형 변환 
여기서 문제되는 점은  pitches에는 String 외의 다른 객체도 넣을 수 있어서 형 변환 오류가 발생할 수 있다는 것이다. 이러한 오류를 방지하고 타입 안정성을 보장해주는 것이 제네릭스이다. 
ArrayList<String> pitches = new ArrayList<>();
pitches.add("138");
pitches.add("129");
String one = pitches.get(0);  // 형 변환이 필요없다.
String two = pitches.get(1);  // 형 변환이 필요없다. 
제네릭스로 자료형을 선언만 하면 그 이후로는 자료형을 형 변환하는 과정이 필요 없다. pitches에는 반드시 String 자료형만 추가되어야 한다는 것을 컴파일러가 이미 알기 때문이다. 이처럼 제네릭스를 이용하면 형 변환과 관련된 불필요한 코딩을 줄일 수 있고, 잘못된 형 변환 때문에 발생하는 런타임 오류를 방지할 수 있다.
2. ArrayList 선언
ArrayList는 자바에서 동적으로 크기를 조절할 수 있는 배열로, java.util 패키지에 포함된 리스트(List) 인터페이스의 구현체입니다. 일반적인 배열과 달리, 요소를 추가하거나 제거할 때마다 크기가 자동으로 변경되기 때문에 데이터 저장이 유연하다.
 
다음은 여러 가지 방법으로 ArrayList 선언하는 법이다.
1. add 메서드로 ArrayList 객체에 요소 추가
2. asList 메서드로 이미 존재하는 String 배열을 가지고 ArrayList 만들기
3. String 배열 대신 String 자료형을 여러 개 전달하여 생성
2-1. add() 메서드
add() 메서드를 사용하면 ArrayList 객체에 요소를 추가할 수 있다.
import java.util.ArrayList;
public class Sample {
    public static void main(String[] args) {
        ArrayList<String> pitches = new ArrayList<>();  // 제네릭스를 사용한 표현
        pitches.add("138");
        pitches.add("129");
        pitches.add("142");
        System.out.println(pitches);  // [138, 129, 142] 출력
    }
}
2-2. Arrays.asList() 메서드
java.util.Arrays 클래스에서 제공하는 Arrays.asList()메서드를 사용하면 이미 존재하는 String 배열을 어레이리스트로 만들 수 있다.
import java.util.ArrayList;
import java.util.Arrays;
public class Sample {
    public static void main(String[] args) {
        String[] data = {"138", "129", "142"};  // 이미 투구수 데이터 배열이 있다.
        ArrayList<String> pitches = new ArrayList<>(Arrays.asList(data));
        System.out.println(pitches);  // [138, 129, 142] 출력
    }
}
2-3. String 배열 대신 String 자료형을 여러 개 전달
import java.util.ArrayList;
import java.util.Arrays;
public class Sample {
    public static void main(String[] args) {
        ArrayList<String> pitches = 
         new ArrayList<>(Arrays.asList("138", "129", "142"));
        System.out.println(pitches);
    }
}3. 주요 ArrayList 메서드
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ArrayListMethodsExample {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        // 요소 추가
        list.add("Apple");
        list.add("Banana");
        list.add(1, "Orange");  // 인덱스 1에 Orange 삽입
        System.out.println("초기 리스트: " + list);
        // 특정 요소 접근
        String fruit = list.get(1);
        System.out.println("인덱스 1의 요소: " + fruit);
        // 요소 수정
        list.set(1, "Grape");
        System.out.println("수정 후 리스트: " + list);
        // 특정 요소 제거
        list.remove("Grape");
        System.out.println("요소 제거 후 리스트: " + list);
        // 리스트가 비어 있는지 확인
        System.out.println("리스트가 비어 있는가? " + list.isEmpty());
        // 특정 요소 포함 여부 확인
        System.out.println("Banana가 포함되어 있는가? " + list.contains("Banana"));
        // 리스트 크기 확인
        System.out.println("리스트 크기: " + list.size());
        // 배열로 변환
        String[] array = list.toArray(new String[0]);
        System.out.println("배열로 변환: " + Arrays.toString(array));
        // 모든 요소 제거
        list.clear();
        System.out.println("모든 요소 제거 후 리스트: " + list);
    }
} 
 
add(E element)
리스트의 끝에 요소 추가
ArrayList<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
System.out.println(list);  // 출력: [Apple, Banana]
add(int index, E element)
지정한 인덱스에 요소를 삽입하고, 그 뒤의 요소들을 한 칸씩 뒤로 이동
list.add(1, "Orange");
System.out.println(list);  // 출력: [Apple, Orange, Banana]
get(int index)
지정한 인덱스의 요소를 주어진 값으로 변경
list.set(1, "Grape");
System.out.println(list);  // 출력: [Apple, Grape, Banana]
remove(int index)
지정한 인덱스의 요소를 제거하고, 그 뒤의 요소들을 한 칸씩 앞으로 이동
list.remove(1);
System.out.println(list);  // 출력: [Apple, Banana]
remove(Object o)
리스트에서 처음으로 발견된 지정된 객체를 제거합니다. 객체가 존재하지 않으면 변화 없음.
list.remove("Banana");
System.out.println(list);  // 출력: [Apple]
size()
리스트의 현재 요소 개수를 반환(크기 반환)
int size = list.size();
System.out.println(size);  // 출력: 1
clear()
리스트의 모든 요소를 제거
list.clear();
System.out.println(list);  // 출력: []
isEmpty()
리스트가 비어 있으면 true를, 그렇지 않으면 false를 반환합니다.
boolean empty = list.isEmpty();
System.out.println(empty);  // 출력: true
contains(Object o)
리스트에 지정된 요소가 포함되어 있는지 확인하여 true 또는 false를 반환
list.add("Apple");
boolean containsApple = list.contains("Apple");
System.out.println(containsApple);  // 출력: true
indexOf(Object o)
리스트에서 특정 요소가 처음 나타나는 인덱스를 반환합니다. 요소가 없으면 -1을 반환
list.add("Banana");
int index = list.indexOf("Banana");
System.out.println(index);  // 출력: 1
lastIndexOf(Object o)
리스트에서 특정 요소가 마지막으로 나타나는 인덱스를 반환합니다. 요소가 없으면 -1을 반환
list.add("Banana");
list.add("Apple");
list.add("Banana");
int lastIndex = list.lastIndexOf("Banana");
System.out.println(lastIndex);  // 출력: 3
toArray()
ArrayList의 모든 요소를 포함하는 Object 배열을 반환
Object[] array = list.toArray();
System.out.println(Arrays.toString(array));  // 출력: [Apple, Banana]
toArray(T[] a)
ArrayList의 모든 요소를 포함하는 특정 타입의 배열을 반환
String[] array = list.toArray(new String[0]);
System.out.println(Arrays.toString(array));  // 출력: [Apple, Banana]
subList(int fromIndex, int toIndex)
지정된 범위에 있는 요소의 **뷰(view)**를 반환, 시작 인덱스는 포함되고, 끝 인덱스는 불포함
List<String> subList = list.subList(0, 2);
System.out.println(subList);  // 출력: [Apple, Banana]4. String.join
ArrayList의 요소들에 구분자를 넣어서 1개의 문자열로 반환할 때 쓰는 메서드이다.
 
예를 들면, 현재 리스트에 들어 있는 내용이 ["바나나", "사과", "포도"]인데 콤마를 각 요소 중간에 넣어서 하나의 문자열로 반환해 주려고 한다.  
 
String.join 메서드를 쓰지 않으면 for 루프를 사용해 pitches 리스트의 모든 요소를 StringBuilder 객체에 추가한다. 각 요소 뒤에 ,를 추가하는데 이 때 result.substring(0, result.length() - 1)을 사용하여 마지막에 추가된 콤마를 제거해야만 우리가 원하는 값이 출력된다. 
import java.util.ArrayList;
import java.util.Arrays;
public class Sample {
    public static void main(String[] args) {
        ArrayList<String> pitches = new ArrayList<>(Arrays.asList("바나나", "사과", "포도"));
        StringBuilder result = new StringBuilder();
        
        for (int i = 0; i < pitches.size(); i++) {
            result.append(pitches.get(i));
            result.append(",");
        }
        
        result.setLength(result.length() - 1);  // 마지막 콤마 제거
        System.out.println(result.toString());  // 바나나,사과,포도
    }
} 
위 과정을 더 간단하게 해 주는 것이 String.join("구분자",리스트명) 메서드이다. 
import java.util.ArrayList;
import java.util.Arrays;
public class Sample {
    public static void main(String[] args) {
        ArrayList<String> pitches = new ArrayList<>(Arrays.asList("바나나", "사과", "포도"));
        String result = String.join(",", pitches);
        System.out.println(result);  // 바나나,사과,포도
    }
}5. ArrayList 정렬하기
리스트를 순서대로 정렬하기 위해서는 sort()메서드를 사용한다.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
public class Sample {
    public static void main(String[] args) {
        ArrayList<String> pitches = new ArrayList<>(Arrays.asList("138", "129", "142"));
        pitches.sort(Comparator.naturalOrder());  // 오름차순으로 정렬
        System.out.println(pitches);  // [129, 138, 142] 출력
    }
} 
sort 메서드의 파라미터는 정렬기준이다. 즉, 오름차순과 내림차순인지 매개변수를 통해 확정해준다. 
- 오름차순(순방향) 정렬 - Comparator.naturalOrder()
 - 내림차순(역방향) 정렬 - Comparator.reverseOrder()
 
포스팅 작성에 참조한 자료
 
박응용 - 점프 투 자바
chatGPT에 질문해가며 코드를 생성하였음. 
'Java' 카테고리의 다른 글
| [Java] 자바의 자료형 Map 과 Set 의 차이 (9) | 2024.11.07 | 
|---|---|
| [Java] JDBC 오라클이랑 자바 연동 (31) | 2024.11.04 | 
| StringBuffer 사용법, 예시, String 과 차이, StringBuilder (5) | 2024.11.03 | 
| [Java] BufferedReader / BufferedWriter (입출력 함수), StringTokenizer, split, BufferedWriter, StringBuilder (5) | 2024.10.27 | 
| [Java] 자바8 Stream으로 List를 map으로 변환하기 (1) | 2024.10.27 |