도도한 개발자

[Java] #22-2. Collection(컬렉션) - List(2) 본문

Backend/Java

[Java] #22-2. Collection(컬렉션) - List(2)

Kiara Kim 2022. 4. 7. 20:00

* ArrayList에서 데이터를 꺼내자

 

ArrayList 객체에서 어떻게 값을 꺼내고 삭제하는지 알기 전에 객체에 들어있는 데이터의 개수를 가져오는 size() 메소드를 알아야 한다. 이 size() 메소드의 리턴 타입은 int다. 배열.length는 배열의 저장 공간 개수를 의미하지만, size() 메소드의 결과는 ArrayList에 들어가 있는 데이터 개수를 의미한다.

 

public void checkArrayList5() {
    ArrayList<String> list = new ArrayList<String>();
    list.add("A");
    list.add("B");
    int listSize = list.size();
    for(int loop = 0; loop < listSize; loop++) {
        System.out.println("list.get(" + loop + ") = " + list.get(loop));
    }
}

 

ArrayList 객체에 있는 값을 가져올 때에는 get() 메소드를 사용한다. 여기에 loop는 데이터가 들어있는 위치를 의미한다. 그리고 위치로 데이터를 꺼내는 get()이라는 메소드롸 반대로 데이터로 위치를 찾아내는 indexOf()하는 메소드와 lastIndexOf()라는 메소드도 있다.

 

리턴 타입 메소드 이름 및 매개 변수 설명
int size() ArrayList 객체에 들어가 있는 데이터의 개수를 리턴한다.
E get(int index) 매개 변수에 지정한 위치에 있는 데이터를 리턴한다.
int indexOf(Object o) 매개 변수로 넘어온 객체와 동일한 데이터의 위치를 리턴한다.
int lastIndexOf(Object o) 매개 변수로 넘어온 객체와 동일한 마지막 데이터의 위치를 리턴한다.

 

indexOf()와 lastIndexOf() 메소드가 따로 있는 이유는 무엇일까? ArrayList는 중복된 데이터를 넣을 수 있다. 0번째에 "해리"라는 값을 넣고, 1번째에서 "해리"라는 값을 넣을 수 있다. 앞에서부터 찾을 땐 indexOf()를, 뒤에서부터 찾을 땐 lastIndexOf()를 사용한다. 그런데 간혹 ArrayList 객체에 있는 데이터들을 배열로 뽑아낼 필요도 있는데 그럴때 toArray() 메소드를 사용하면 된다.

 

리턴 타입 메소드 이름 및 매개 변수 설명
Object[] toArray() ArrayList 객체에 있는 값들을 Object[] 타입의 배열로 만든다.
<T> T[] toArray(T[] a) ArrayList 객체에 있는 값들을 매개 변수로 넘어온 T 타입의 배열로 만든다.

 

여기서 중요한 것은 매개 변수가 없는 toArray() 메소드는 Object 타입의 배열로만 리턴을 한다는 것이다. 그러므로 제네릭을 사용하여 선언한 ArrayList 객체를 배열로 생성할 때에는 이 메소드보단 두 번째 메소드를 사용하는 것이 권장된다. 사용법을 알아보자.

 

public void checkArrayList6() {
    ArrayList<String> list = new ArrayList<String>();
    list.add("A");
    String[] strList = list.toArray(new String[0]);
    System.out.println(strList);
}

 

위와 같이 toArray() 메소드의 매개 변수로 변환하려는 타입의 배열을 지정해주면 된다. 여기서 매개 변수로 넘어가는 "new String[0]"을 유심히 보자. 매개 변수로 넘기는 배열은 이와 같이 의미 없이 타입만을 지정하기 위해 사용할 수도 있다. 그런데 실제로는 매개 변수로 넘긴 객체에 값을 담아준다. 그러나 ArrayList 객체의 데이터 크기가 매개 변수로 넘어간 배열의 객체 크기보다 클 경우 매개 변수로 모든 값이 null로 채워진다. 코드로 이해해보자.

 

public void checkArrayList7() {
    ArrayList<String> list = new ArrayList<String>();
    list.add("A");
    list.add("B");
    list.add("C");
    String[] tempArray = new String[3];
    String[] strList = list.toArray(tempArray);
    for(String temp:strList) {
        System.out.println(temp);
    }
}

 

결과는

 

A
B
C

 

이번에는 tempArray라는 배열의 크기를 5로 지정하고 컴파일 및 실행하보자. 

 

A
B
C
null
null

 

0번부터 2번 위치의 데이터는 정상적으로 데이터가 들어가고, 나머지 값들은 모두 null로 저장되었다. 

따라서 toArray() 메소드를 사용할 때에는 가장 처음에 사용한 것과 같이 크기가 0인 배열을 넘겨주는 것이 가장 좋다.

 

 

* ArrayList에 있는 데이터를 삭제하자.

 

ArrayList 클래스에서 제공하는 데이터 삭제 관련 메소드이다.

 

리턴 타입 메소드 이름 및 매개 변수 설명
void clear() 모든 데이터를 삭제한다.
E remove(int index) 매개 변수에서 지정한 위치에 있는 데이터를 삭제하고, 삭제한 데이터를 리턴한다.
boolean remove(Object o) 매개 변수에 넘어온 객체와 동일한 첫 번째 데이터를 삭제한다.
boolean removeAll(Collection<?> c) 매개 변수로 넘어온 컬렉션 객체에 있는 데이터와 동일한 모든 데이터를 삭제한다.

 

객체를 넘겨주는 remove() 메소드와 컬렉션 객체를 넘겨주는 removeAll() 메소드를 살펴보자. 거의 동일한 기능을 하는 것으로 보일 수 있지만, 객체를 넘겨주는 remove() 메소드는 매개 변수로 넘어온 객체와 동이한 첫 번째 데이터만 삭제한다. 하지만 removeAll() 메소드는 매개 변수로 넘어온 컬렉션에 있는 데이터와 동일한 모든 데이터를 삭제한다. 코드를 보자.

 

public void checkArrayList8() {
    ArrayList<String> list = new ArrayList<String>();
    list.add("A");
    list.add("B");
    list.add("C");
    list.add("A");
    for(int loop = 0; loop < list.size(); loop++) {
        System.out.println("list.get(" + loop + ") = " + list.get(loop));
    }
}

 

결과는 다음과 같다.

 

Removed A
list.get(0) = B
list.get(1) = C
list.get(2) = A

 

0번째 값인 "A"를 삭제했고 삭제한 값이 리턴된 것을 볼 수 있다. 코드를 살짝 바꿔보자.

 

public void checkArrayList8() {
    ArrayList<String> list = new ArrayList<String>();
    list.add("A");
    list.add("B");
    list.add("C");
    list.add("A");
    System.out.println(list.remove("A"));
    for(int loop = 0; loop < list.size(); loop++) {
        System.out.println("list.get(" + loop + ") = " + list.get(loop));
    }
}

 

결과는 다음과 같다.

 

true
list.get(0) = B
list.get(1) = C
list.get(2) = A

 

"A" 값을 삭제하고 그 결과인 true 값이 리턴되어 출력되는 것을 볼 수 있다. 그러나 가장 마지막에 있는 "A"는 삭제되지 않았다. 코드를 조금 더 수정해보자.

 

public void checkArrayList8() {
    ArrayList<String> list = new ArrayList<String>();
    list.add("A");
    list.add("B");
    list.add("C");
    list.add("A");
    ArrayList<String> temp = new ArrayList<String>();
    temp.add("A");
    list.removeAll(temp);
    for(int loop = 0; loop < list.size(); loop++) {
        System.out.println("list.get(" + loop + ") = " + list.get(loop));
    }
}

 

결과는 어떻게 될까?

 

list.get(0) = B
list.get(1) = C

 

"A"라는 값을 갖는 모든 데이터가 사라진 것을 볼 수 있다. 

마지막으로 ArrayList 객체에 있는 값을 변경하는 메소드를 보자.

 

리턴 타입 메소드 이름 및 매개 변수 설명
E set(int index, E element) 지정한 위치에 있는 데이터를 두 번째 매개 변수로 넘긴 값으로 변경한다. 그리고, 해당 위치에 있던 데이터를 리턴한다.

 

이 메소드로 인해 특정 위치에 있는 데이터를 삭제(remove)하고 그 위치에 데이터를 넣어야(add)하는 두 단계를 한번에 끝낼 수 있게 된다.

 

 

* Stack 클래스와의 차이점은?

 

List 인터페이스를 구현한 또 하나의 클래스인 Stack 클래스에 대해 살펴보자. 먼저 Stack 클래스의 상속관계를 살펴보자.

 

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

 

Stack 클래스의 부모 클래스는 Vector인 것을 볼 수 있다. 즉, Vector 클래스에서 제공하는 모든 메소드를 사용할 수 있다. 

Stack 클래스의 생성자는 하나다.

 

생성자 설명
Stack() 아무 데이터도 없는 Stack 객체를 만든다.

 

다음의 메소드를 보자.

 

리턴 타입 메소드 이름 및 매개 변수 설명
boolean empty() 객체가 비어있는지를 확인한다.
E peek() 객체의 가장 위에 있는 데이터를 리턴한다.
E pop() 객체의 가장 위에 있는 데이터를 지우고, 리턴한다.
E push(E item) 매개 변수로 넘어온 데이터를 가장 위에 저장한다.
int search(Oject o) 매개 변수로 넘어온 데이터의 위치를 리턴한다.

 

여기서 peek()과 pop() 메소드의 차이는 무엇일까? peek()는 데이터를 리턴만 하지만, pop()은 지우고 꺼낸다. 

 

 

* 정리

 

1. Collection 인터페이스를 구현하는 대표적인 3개의 자료구조에는 어떤 것들이 있나요?
Collection 인터페이스를 구현한 대표적인 타입은 List, Set, Queue 이다. 

2. 배열과 같이 순서가 있는 목록형을 나타내는 대표 인터페이스는 무엇인가요?
배열과 같은 형태는 List 인터페이스에서 선언되어 있다.

3. ArrayList라는 클래스의 생성자 중 매개변수가 없는 기본 생성자를 사용하면 기본적으로 몇 개의 저장공간을 가지나요?
별도로 정하지 않을 경우 자바에서 제공하는 List 를 구현한 클래스의 데이터 개수는 10개이다.

4. 만약 ArrayList 클래스의 저장 공간 개수를 처음부터 지정하려면 어떤 생성자를 사용하면 되나요?
ArrayList(int initialCapacity) 를 사용하면 초기 데이터 개수를 생성과 동시에 지정할 수 있다.

5. ArrayList 객체를 생성할 때 제네릭을 사용하는 이유는 무엇인가요?
제네릭을 사용하면 컴파일 시점에 타입을 잘못 지정한 부분을 걸러낼 수가 있기 때문이다.

6. ArrayList에 데이터를 담는 메소드 두가지의 이름은 무엇인가요?
add()와 addAll()메소드를 사용하면 ArrayList에 데이터를 담을 수 잇다.

7. Collection 인터페이스를 구현한 클래스의 객체에서 사용할 수 있는 for 루프의 구조는 어떻게 되나요? 코드를 작성하세요.
만약 String타입을 담는 list라는 ArrayList를 만들었다면
for(String data:list) { 
//
}
와 같이 사용하면 된다.

8. Collection 인터페이스를 구현한 클래스의 객체에 저장된 데이터의 갯수를 확인하는 메소드 이름은 무엇인가요?
size() 메소드를 사용하면 Collection을 구현한 클래스들에 들어 있는 데이터 개수를 확인할 수 있다. 

9. ArrayList에서 특정 위치에 있는 데이터를 확인하는 메소드는 무엇인가요?
get() 메소드를 사용하면 매개변수로 넘긴 위치에 있는 값을 리턴한다.

10. ArrayList에서 특정 위치에 있는 데이터를 삭제하는 메소드는 무엇인가요?
remove() 메소드를 사용하면 매개변수로 넘긴 위치에 있는 값을 삭제한다. 만약 매개변수로 객체를 넘기면, 동일한 첫번째 객체를 삭제한다. 

11. ArrayList에서 특정 위치에 있는 데이터를 수정하는 메소드는 무엇인가요?
set() 메소드를 사용하면 첫번째 매개변수로 넘긴 위치에 있는 값은 두번째 매개변수로 넘긴 값으로 대체한다.

12. java.util 패키지에 있는 Stack 이라는 클래스는 어떤 클래스를 확장한 것인가요?
Stack 클래스는 List 인터페이스를 구현하였다.

13. Stack 클래스에서 데이터를 담는 메소드는 무엇인가요?
Stack 클래스에 데이터를 담을 때에는 push() 메소드를 사용한다. 

14. Stack 클래스에서 가장 위에 있는 데이터를 확인만 하는 메소드는 무엇인가요?
Stack 클래스의 peek() 메소드는 가장 위에 있는 값을 리턴만한다.

15. Stack 클래스에서 가장 위에 있는 데이터를 삭제하고 리턴하는 메소드는 무엇인가요?
Stack 클래스의 pop() 메소드는 가장 위에 있는 데이터를 지우고 리턴한다.