본문 바로가기
Java Study

자바 ArrayList 특징 & 사용법 정리

by xogns93 2024. 7. 16.

ArrayList는 자바 컬렉션 프레임워크에서 가장 많이 사용되는 클래스 중 하나로, 동적으로 크기를 조절할 수 있는 배열 기반의 리스트를 구현합니다. ArrayList는 List 인터페이스를 구현하며, 배열의 장점인 인덱스를 통한 빠른 접근과 리스트의 장점인 크기 조절의 유연성을 결합한 자료구조입니다.

 

ArrayList의 주요 특징

  1. 동적 크기 조절: ArrayList는 요소를 추가하거나 제거할 때 자동으로 크기를 조절합니다.
  2. 인덱스를 통한 빠른 접근: 배열과 마찬가지로 인덱스를 통해 요소에 빠르게 접근할 수 있습니다.
  3. 순차적인 요소 저장: 요소는 순차적으로 저장되며, 요소의 순서는 유지됩니다.
  4. 제네릭 지원: ArrayList는 제네릭 클래스로, 특정 타입의 요소만 저장할 수 있습니다.

 

ArrayList의 주요 메소드

1. 생성자

  • ArrayList(): 기본 용량(초기 크기)으로 빈 리스트를 생성합니다.
  • ArrayList(int initialCapacity): 지정된 초기 용량으로 빈 리스트를 생성합니다.
  • ArrayList(Collection<? extends E> c): 지정된 컬렉션의 요소를 포함하는 리스트를 생성합니다.

2. 요소 추가

  • boolean add(E e): 리스트의 끝에 요소를 추가합니다.
  • void add(int index, E element): 지정된 위치에 요소를 추가합니다.

3. 요소 제거

  • E remove(int index): 지정된 위치의 요소를 제거하고, 제거된 요소를 반환합니다.
  • boolean remove(Object o): 리스트에서 첫 번째로 일치하는 요소를 제거합니다.

4. 요소 접근

  • E get(int index): 지정된 위치의 요소를 반환합니다.
  • E set(int index, E element): 지정된 위치의 요소를 주어진 요소로 대체합니다.

5. 기타 메소드

  • int size(): 리스트의 요소 개수를 반환합니다.
  • boolean isEmpty(): 리스트가 비어 있는지 여부를 반환합니다.
  • void clear(): 리스트의 모든 요소를 제거합니다.

 

ArrayList의 장단점

 

장점

  1. 빠른 요소 접근: 인덱스를 통한 요소 접근 시간이 O(1)입니다.
  2. 동적 크기 조절: 요소를 추가하거나 제거할 때 자동으로 크기를 조절합니다.
  3. 사용 편의성: 다양한 메소드를 제공하여 사용이 편리합니다.

단점

  1. 느린 요소 삽입/제거: 리스트 중간에 요소를 삽입하거나 제거하는 경우, 나머지 요소를 이동시켜야 하므로 O(n)의 시간이 소요됩니다.
  2. 메모리 사용: 배열 기반의 구현이므로, 사용하지 않는 공간을 차지할 수 있습니다.

 


ArrayList 사용법

ArrayList 객체 생성

ArrayList를 사용하기 위해선 상단에 패키지를 명시하여 가져와야 한다.

import java.util.ArrayList;

 

// 타입설정 Integer 객체만 적재가능
ArrayList<Integer> members = new ArrayList<>();

// 초기 용량(capacity)지정
ArrayList<Integer> num3 = new ArrayList<>(10);

// 배열을 넣어 생성
ArrayList<Integer> list2 = new ArrayList<>(Arrays.asList(1,2,3));

// 다른 컬렉션으로부터 그대로 요소를 받아와 생성 (ArrayList를 인자로 받는 API를 사용하기 위해서 Collection 타입 변환이 필요할 때 많이 사용)
ArrayList<Integer> list3 = new ArrayList<>(list2);

 

ArrayList 요소 추가

ArrayList에 요소를 추가할때 제네릭 타입 파라미터로 명시된 타입의 데이터만 추가가 가능하다. 그리고 ArrayList를 처음 접할때 용량(capacity)과 크기(size)에 대한 용어 차이가 모호할 수 있는데, capacity는 리스트의 공간 용량라고 보면되고, size는 리스트 안에 들어있는 요소들의 총 갯수라고 이해하면 된다.

 

메서드  설 명
boolean add(Object obj) ArrayList의 마지막에 객체를 추가한다. 추가에 성공하면 true를 반환
void addAll(Collection c) 주어진 컬렉션의 모든 객체를 저장한다.(마지막 index의 뒤로 붙임)

 

ArrayList<String> list = new ArrayList<>(10); // 용량(capacity)를 10으로 설정

list.add("A");
list.add("B");
list.add("C");
list.add("D");
list.add("E");
list.add("F");

list.size(); // 크기(size)는 6 (들어있는 요소의 총 개수)

 


ArrayList 요소 삽입

리스트에 데이터를 추가하되 추가되는 위치를 지정하여 삽입할 수 있다. 이때 지정된 위치에 요소를 넣을수 있게 기존의 요소들이 한칸씩 뒤로 이동되면서 빈공간을 만들어준다. 유의할점은 한칸씩 데이터들을 뒤로 밀어내는 동작은 꽤나 비용이 크기 때문에 ArrayList의 사이즈가 커질 수록 비효율적이 된다. (이는 ArrayList 컬렉션의 단점이기도 하다) 

ArrayList<String> list = new ArrayList<>(8); 

list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");

// 3번째 인덱스 자리에 요소 삽입
list.add(3, "A");

System.out.println(list); // [1, 2, 3, A, 4, 5]

ArrayList 요소 삭제

요소의 삭제 역시 중간에 위치한 요소를 제거할경우, 나머지 요소들이 빈 공간을 채우려 앞쪽으로 이동되게 된다.

ArrayList<String> list = new ArrayList<>(8); 

list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");

// 2번째 인덱스 자리의 요소 삭제
list.remove(2);

System.out.println(list); // [1, 2, 4, 5]

 

 

만일 모든 값을 싹 제거하려면, 일일히 반복문을 돌려 제거하지말고 간단히 clear() 메소드를 사용하면 된다. 

ArrayList<String> list1 = new ArrayList<>();
list1.add("1");
list1.add("2");
list1.add("3");
 
list1.clear(); // list1의 데이터를 모두 비운다.

System.out.println(list1); // []

 


ArrayList 요소 검색

ArrayList<String> list1 = new ArrayList<>();
list1.add("A");
list1.add("B");
list1.add("C");
list1.add("A");

// list에 A가 있는지 검색 : true
list1.contains("A"); 

// list에 D가 있는지 순차적으로 검색하고 index를 반환 (만일 없으면 -1)
list1.indexOf("A"); // 0

// list에 D가 있는지 연순으로 검색하고 index를 반환 (만일 없으면 -1)
list1.lastIndexOf("A"); // 3

ArrayList 요소 얻기

ArrayList<String> list = new ArrayList<>(18); 

list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");

list.get(0); // "1"
list.get(3); // "4"

ArrayList 요소 변경

ArrayList<String> list1 = new ArrayList<>();

list1.add("list1");
list1.add("list1");
list1.add("list1");
 
// index 1번의 데이터를 문자열 "setData"로 변경한다.
list1.set(1, "setData"); 

System.out.println(list1); // [list1, setData, list1]

ArrayList 용량 확장

ArrayList는 생성할때 용량이 정할 수 있지만, 데이터가 추가되면서 자동으로 용량(Capacity)을 늘려준다.

만일 정해진 용량보다 넘게 데이터를 적재할 경우, 자체적으로 내부 배열을 큰 사이즈로 새로 만들고 기존의 배열에서 요소들을 복사함으로써, 간접적으로 리스트의 용량을 확장시키게 된다. 하지만 이러한 가변적인 동작은 리스트를 다루는데에는 편하지만, 배열 복사 동작 자체가 성능이 그리 좋지않아 오버헤드(Overhead)를 발생 시키게 된다.

 

ArrayList<String> list = new ArrayList<>(10); // 용량(capacity)를 10으로 설정

// 용량 10을 넘은 요소 13개 추가
list.add("A");
list.add("B");
list.add("C");
list.add("D");
list.add("E");
list.add("F");
list.add("G");
list.add("H");
list.add("I");
list.add("J");
list.add("K");
list.add("L");
list.add("M");

list.size(); // 크기(size)는 13 : 자동으로 용량이 증가되어 데이터를 적재함

 


ArrayList 복사

ArrayList<Integer> number = new ArrayList<>();
number.add(1);
number.add(3);
number.add(5);

// ArrayList는 내부적으로 Object[] 배열로 저장하기 때문에 형변환이 필요함
ArrayList<Integer> cloneNumber = (ArrayList<Integer>) number.clone();

System.out.println("ArrayList: " + number); // [1, 3, 5]
System.out.println("Cloned ArrayList: " + cloneNumber); // [1, 3, 5]

 


ArrayList 배열 변환

ArrayList<String> languages= new ArrayList<>();
languages.add("Java");
languages.add("Python");
languages.add("C");

/* ArrayList<String> 을 String[] 배열로 변환 */

// 방법 1 : 배열로 변환하고 반환
String[] arr1 = languages.toArray();

// 방법 1 : 매개변수로 지정된 배열에 담아 바환
String[] arr2 = new String[languages.size()]; // 먼저 리스트 사이즈에 맞게 배열 생성
languages.toArray(arr2);

ArrayList 정렬

 

ArrayList를 정렬할때 주의할점은 sort() 메서드는 정렬된 값을 반환하는 것이 아닌, 원본 리스트 자체를 변경 시킨다. 

ArrayList list1 = new ArrayList();
list1.add("3");
list1.add("2");
list1.add("1");
 
// 오름차순 정렬
list1.sort(Comparator.naturalOrder());
System.out.println(list1); // [1, 2, 3]

// 내림차순 정렬
list1.sort(Comparator.reverseOrder());
System.out.println(list1); // [3, 2, 1]

ArrayList 순회

 

보통 ArrayList의 요소들을 순회할 일이 있다면 다음과 같이 for문으로 처리하는 것이 일반적일 것이다.

ArrayList<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);

for(Integer i : list) { 
    System.out.println(i);
}

ArrayList 이터레이터

다만 몇몇 컬렉션에서는 저장된 요소를 Iterator 인터페이스로 읽어오도록 하는 순회 패턴을 지향하기도 한다. 

 

Collection 인터페이스에서는 Iterator 인터페이스를 구현한 클래스의 인스턴스를 반환하는 iterator() 메소드를 정의하여 각 요소에 접근하도록 정의 하고 있다. 따라서 Collection 인터페이스를 상속받는 List나 Set 인터페이스에서도 iterator() 메소드를 사용할 수 있다. (Map은 X) 

// 이터레이터 객체 반환
Iterator<Integer> iter = lnkList.iterator();

// 만일 다음 요소가 있을 경우 반복
while(iter.hasNext()) {
    System.out.println(iter.next()); // 요소를 출력하고 반복 위치를 이동
}

 

또한 ArrayList에는 Iterator 뿐만 아니라 리스트 전용 이터레이터 객체인 ListIterator도 지원한다. ListIterator 인터페이스는 Iterator 인터페이스를 상속받아 여러 기능을 추가한 인터페이스로서, Iterator는 컬렉션의 요소에 접근할 때 단 방향으로만 이동할 수 있는 반면, ListIterator 인터페이스는 컬렉션 요소의 대체, 추가 그리고 인덱스 검색 등을 위한 작업에서 양방향으로 이동하는 것을 지원하여 더욱 쓰임새가 넓다. 그리고 Iterator는 Collection 인터페이스를 구현한 컬렉션에서 모두 사용할수 있는 반면, ListIterator는 오로지 List 컬렉션에서만 사용이 가능하다. 

// ListIterator 객체 반환
ListIterator<Integer> iter = lnkList.listIterator();

// 만일 다음 요소가 있다면 반복
while (iter.hasNext()) {
    System.out.println(iter.next()); // 요소를 출력하고 반복 위치를 뒤로 이동
} 

// -- 리스트를 끝까지 순회한 상태

// 만일 이전 요소가 있다면 반복
while (iter.hasPrevious()) {
    System.out.println(iter.previous()); // 요소를 출력하고 반복 위치를 앞으로 이동
}

 

결론

ArrayList는 자바에서 가장 많이 사용되는 컬렉션 중 하나로, 동적 배열의 장점을 활용하여 유연하게 요소를 관리할 수 있습니다. 요소의 빈번한 삽입과 제거가 발생하지 않는 경우에 특히 유용하며, 다양한 메소드와 제네릭을 지원하여 강력한 기능을 제공합니다. ArrayList의 사용법과 특징을 잘 이해하고 활용하면 효과적인 자료구조를 구현할 수 있습니다.

 


  1. ArrayList와 LinkedList:
    • ArrayList는 동적 배열을 사용하여 요소를 저장합니다. 요소에 대한 접근 속도가 빠르지만, 중간에 요소를 삽입하거나 삭제하는 작업은 느릴 수 있습니다.
    • LinkedList는 이중 연결 리스트를 사용하여 요소를 저장합니다. 중간에 요소를 삽입하거나 삭제하는 작업이 빠르지만, 요소에 대한 접근 속도는 느릴 수 있습니다.
  2. List 인터페이스:
    • List 인터페이스를 사용하면 ArrayList나 LinkedList의 내부 구현 방식에 관계없이 동일한 방식으로 컬렉션을 조작할 수 있습니다.
    • printList 메서드는 List 인터페이스를 파라미터로 받아 리스트의 모든 요소를 출력합니다.

내부 구현의 차이점

  • ArrayList:
    • 내부적으로 배열을 사용하여 요소를 저장합니다.
    • 요소 접근 속도가 빠르며, 인덱스를 사용하여 랜덤 접근이 가능합니다.
    • 배열 크기가 고정되어 있어 크기를 동적으로 변경해야 하는 경우 배열을 재할당해야 합니다.
  • LinkedList:
    • 내부적으로 이중 연결 리스트를 사용하여 요소를 저장합니다.
    • 요소 삽입 및 삭제가 빠르며, 특히 리스트 중간에서의 작업이 효율적입니다.
    • 요소 접근 속도가 느리며, 리스트의 처음부터 순차적으로 접근해야 합니다.

 

출처 : https://inpa.tistory.com/