본문 바로가기
Backend/Java

[자바의 신] 22장

by unknownomad 2022. 8. 9.

22장 - 자바랭 다음으로 많이 쓰는 애들은 컬렉션 ― Part1 (List)


자바의 컬렉션

  • 목록성 데이터를 처리하는 자료 구조를 통칭


자료 구조

  • Data Structure
  • 어떤 정보를 담는 것
  • 하나의 데이터가 아닌 여러 데이터 담을 때 사용
  • ex) 배열, DTO, ...

 

배열

  • 가장 기본적인 자료 구조
  • 성능상이나 메모리 효율면에서 가장 좋음
  • But 그 크기가 정해져 있있을 때 유용

자바의 데이터 담는 자료 구조

List Set Queue Map
순서 있는 목록 순서 중요하지 않음 먼저 들어온 것이 먼저 나감 키-값(key-value)으로 저장됨

 

Collection


Collection

Collection 인터페이스 선언

public interface Collection<E> extends Iterable<E>

 

Iterable 인터페이스의 메서드

리턴 타입 메서드 이름 및 매개 변수
Iterator<T> iterator()

 

Iterator 인터페이스의 메서드

hasNext() next() remove()
추가 데이터가 있는지 확인 현재 위치를 다음 요소로 넘기고 그 값 리턴 데이터 삭제
  • Collection 인터페이스가 Iterable 인터페이스를 확장했다
    = Iterator 인터페이스를 사용해 데이터를 순차적으로 가져올 수 있다

 

Collection 인터페이스의 주요 메서드 목록

리턴 타입 메서드 이름 및 매개 변수 설명
boolean add(E e) 요소 추가
boolean addAll(Collection) 매개 변수로 넘어온 컬렉션의 모든 요소 추가
void clear() 컬렉션에 있는 모든 요소 데이터 삭제
boolean contains(Object) 매개 변수로 넘어온 객체가 해당 컬렉션에 있는지 확인
동일한 값 있으면 true 리턴
boolean containsAll(Collection) 매개 변수로 넘어온 객체들이 해당 컬렉션에 있는지 확인
매개 변수로 넘어온 컬렉션에 있는 요소들과 동일한 값들이 모두 있으면 true 리턴
boolean equals(Object) 매개 변수로 넘어온 객체와 같은 객체인지 확인
int hashCode() 해시 코드값 리턴
boolean isEmpty() 컬렉션이 비어있는지 확인
비어있으면 true 리턴
Iterator iterator() 데이터를 한 건씩 처리하기 위한 Iterator 객체 리턴
boolean remove(Object) 매개 변수와 동일한 객체 삭제
boolean removeAll(Collection) 매개 변수로 넘어온 객체들을 해당 컬렉션에서 삭제
boolean retainAll(Collection) 매개 변수로 넘어온 객체들만을 컬렉션에 남겨둠
int size() 요소의 개수 리턴
Object[] toArray() 컬렉션에 있는 데이터들을 배열로 복사
<T> t[] toArray(T[]) 컬렉션에 있는 데이터들을 지정한 타입의 배열로 복사

List 인터페이스

List

  • Collection 인터페이스 확장함
  • 배열처럼 순서 있음
  • java.util 패키지에서 ArrayList, Vector, Stack, LinkedList 많이 사용

 

ArrayList Vector
Thread safe X Thread safe
사용법 및 기능 거의 비슷함
확장 가능한 배열이라 생각하면 됨


Thread safe한 ArrayList 객체 생성 방법

List list = Collections.synchronizedList(new ArrayList(...));
  • 위와 같이 생성하지 않을 시 원치 않는 데이터 나올 수도 있음

Stack 클래스

  • Vector 클래스 확장함
  • LIFO (Last In First Out; 가장 마지막에 추가한 값을 가장 처음 빼내는 것) 지원
  • 프로그래밍 언어에서의 스택 : 보통 메서드가 호출된 순서를 기억하는 장소를 일컬음

Stack

  • 컬렉션, 쓰레드, IO, 네트워크 등의 관련 클래스 사용할 때 상속 관계 살펴보는 게 좋음
  • 그 클래스의 API에 있는 메서드나 상수 뿐만 아니라 부모 클래스에 선언된 메서드도 사용할 수 있기에

 

ArrayList 클래스

ArrayList 상속 관계

java.lang.Object
    java.util.AbstractCollection<E>
        java.util.AbstractList<E>
            java.util.ArrayList<E>

 

 

ArrayList가 구현한 인터페이스

인터페이스 용도
Serializable 원격으로 객체 전송 및 파일에 저장할 수 있음을 지정
Cloneable Object 클래스의 clone() 메서드가 제대로 수행될 수 있음을 지정 ➡ 복제가 가능한 객체임을 의미
Iterable<E> 객체가 foreach 문장 사용할 수 있음을 지정
Collection<E> 여러 개의 객체를 하나의 객체에 담아 처리할 때의 메서드 지정
List<E> 목록형 데이터를 처리하는 것과 관련된 메서드 지정
RandomAccess 목록형 데이터에 보다 빠르게 접근할 수 있게 임의로(random하게) 접근하는 알고리즘이 적용된다는 것을 지정

 

ArrayList의 생성자

생성자 설명
ArrayList() 객체 저장할 공간이 10개인 ArrayList 만듦
ArrayList(Collection<? extends E> c) 매개 변수로 넘어온 컬렉션 객체가 저장된 ArrayLIst 만듦
ArrayList(int initialCapacity) 매개 변수로 넘어온 initialCapacity 개수만큼 저장 공간을 갖는 ArrayList 만듦

 

  • ArrayList는 대부분 서로 다른 종류의 객체를 하나의 배열에 넣지 않고, 한 가지 종류의 객체만 저장
  • 여러 종류를 하나의 객체에 담을 때에는 되도록 DTO 객체를 하나 만들어 담는 것이 좋음
  • ➡ 컬렉션 관련 클래스의 객체 선언 시 제네릭 사용하여 선언하는 것이 좋음

 

  • ArrayList 객체 선언 시 매개 변수 넣지 않으면 초기 크기 = 10
  • ➡ 10개 이상의 데이터가 들어가면 크기를 늘이는 작업이 ArrayList 내부에서 자동으로 수행됨
  • ➡ 애플리케이션 성능에 영향을 주게 됨
  • ➡ 저장되는 데이터 크기가 어느 정도 예측 가능하다면 예측한 초기 크기를 지정하는 게 좋음

 

ArrayList에 데이터 담는 메서드

리턴 타입 메서드 이름 및 매개 변수 설명
boolean add(E e) 매개 변수로 넘어온 데이터를 가장 끝에 담음
void add(int index, E e) 매개 변수로 넘어온 데이터를 지정된 index 위치에 담음
boolean addAll(Collection<? extends E> c) 매개 변수로 넘어온 컬렉션 데이터를 가장 끝에 담음
boolean addAll(int index, Collection<? extends E> c) 매개 변수로 넘어온 컬렉션 데이터를 index에 지정된 위치부터 담음
  • ArrayList는 확장된 배열 타입 ➡ 순서가 매우 중요
  • 배열처럼 가장 첫 위치는 0부터 시작

 

add(int index, E e)

  • 지정된 위치에 데이터 담음
  • ➡ 지정된 위치에 있던 기존 데이터들은 위치가 하나씩 뒤로 밀려남
IndexOutOfBoundsException
  • ArrayList 작업 시 위치(index)를 잘못 지정하면 실행 시 예외 발생
  • ex) 위치가 ArrayList 길이보다 큰 숫자인 경우 등

 

  • Collection 인터페이스 구현한 모든 클래스에서 사용할 수 있는 향상된 for문
for(타입이름 임시변수명 : 반복대상객체) {
}


addAll()

public void checkArrayList3() {
    ArrayList<String> list = new ArrayList<String>();
    list.add("A");
    list.add("B");
    list.add("C");
    list.add("D");
    list.add("E");
    list.add(1, "A1");
    
    ArrayList<String> list2 = new ArrayList<String>();
    list2.add("0 ");
    list2.addAll(list);
    for(String tempData : list2) {
        System.out.println("List2 " + tempData);
    }
}
//결과
//List2 0
//List2 A
//List2 A1
//List2 B
//List2 C
//List2 D
//List2 E
  • 비어있는 list2 객체에 list 값들이 들어감
  • list에 있는 내용들이 list2에 복사됨
  • 만약 list의 값을 list2에 복사해야 할 일이 생길 때 생성자 이용하면 편리함
ArrayList<String> list2 = new ArrayList<String>(list);
  • ArrayList에는 Collection 인터페이스를 구현한 어떤 클래스도 포함시킬 수 있는 생성자가 있기 때문
public void checkArrayList4() {
    ArrayList<String> list = new ArrayList<String>();
    list.add("A");
    
    ArrayList<String> list2 = list;
    list.add("Ooops");
    for(String tempData : list2) {
        System.out.println("List2 " + tempData);
    }
}
//결과
//List2 A
//List2 Ooops
  • list2는 list 값만 사용하겠다는 게 아님
  • list라는 객체가 생성되어 참조되고 있는 주소까지도 사용하겠다는 의미
  • (자바의 모든 객체는 생성되면 그 객체가 위치하는 주소가 내부적으로 할당됨)
  • ➡ list2 = list: 두 객체의 변수는 다르나, 하나의 객체가 변경되면 다른 이름의 변수를 갖는 객체의 내용도 바뀜

Shallow copy & Deep copy

Shallow copy Deep copy
list2 = list 같이 다른 객체에 원본 객체의 주소값만 할당하는 것 객체의 모든 값을 복사해 복제된 객체에 있는 값을 변경해도 원본에 영향이 없도록 할 때 수행
ex) 배열 복사할 때 System 클래스에 있는 arraycopy() 같은 메서드 이용 시 Deep copy 쉽게 처리 가능
  • ➡ 하나의 Collection 관련 객체를 복사할 일 있을 때 이처럼 생성자 사용하거나 addAll() 메서드 사용하는 게 좋음

ArrayList에서 데이터 꺼내기

size()

  • ArrayList 객체에 들어있는 데이터의 개수 가져오는 메서드
배열.length size()
배열의 저장 공간 개수 들어있는 데이터 개수
  • ArrayList 초기 크기 지정할 때
    • 그 크기는 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 < lilstSize; loop++) {
        System.out.println("list.get(" + loop + ") = " + list.get(loop));
    }
}
  • get() 메서드로 ArrayList 객체에 있는 값 가져옴
리턴 타입 메서드 이름 및 매개 변수 설명
int size() ArrayList 객체에 들어있는 데이터의 개수 리턴
E get(int index) 매개 변수에 지정한 위치에 있는 데이터 리턴
int indexOf(Object o) 매개 변수로 넘어온 객체와 동일한 데이터의 위치 리턴
int lastIndexOf(Object o) 매개 변수로 넘어온 객체와 동일한 마지막 데이터의 위치 리턴
  • ArrayList는 중복된 데이터 넣을 수 없기에 indexOf()와 lastIndexOf() 메서드 필요

 

toArray()

  • ArrayList 객체에 있는 데이터 ➡ 배열로 뽑아낼 때
리턴 타입 메서드 이름 및 매개 변수 설명
Object[] toArray() ArrayList 객체에 있는 값들을 Object[] 타입의 배열로 만듦
<T> T[] toArray(T[] a) ArrayList 객체에 있는 값들을 매개 변수로 넘어온 T 타입의 배열로 만듦
  • 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[0]);
}
  • toArray() 메서드의 매개 변수에 변환하려는 타입의 배열 지정
  • ArrayList 객체의 데이터 크기가 매개 변수로 넘어간 배열 객체의 크기보다 클 때 매개 변수인 배열의 모든 값이 null로 채워짐
  • 여기서의 new String[0] : 위의 상황인 배열의 모든 값이 null로 지정되는 걸 방지함
public void checkArrayList7() {
    ArrayList<String> list = new ArrayList<String>();
    list.add("A");
    list.add("B");
    list.add("C");
    
    String[] tempArray = new String[5];
    String[] strList = list.toArray(tempArray);
    for(String temp : strList) {
        System.out.println(temp);
    }
}
//결과
//A
//B
//C
//null
//null
public void checkArrayList7() {
    ArrayList<String> list = new ArrayList<String>();
    list.add("A");
    list.add("B");
    list.add("C");
    
    String[] tempArray = new String[2];
    String[] strList = list.toArray(tempArray);
    for(String temp : strList) {
        System.out.println(temp);
    }
}
//결과
//null
//null
  • toArray() 사용 시 new String[0]처럼 크기가 0인 배열 넘겨주는 게 Best

 

ArrayList에 있는 데이터 삭제

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

 

remove(int index)

public void checkArrayList8() {
    ArrayList<String> list = new ArrayList<String>();
    list.add("A");
    list.add("B");
    list.add("C");
    list.add("A");
    
    System.out.println("Removed " + list.remove(0));
    
    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

 

remove(Object o)

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

 

removeAll(Collection c)

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

 

ArrayList 객체에 있는 값 변경

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


trimToSize()

  • ArrayList 객체 공간의 크기를 데이터 개수만큼으로 변경
  • String 클래스의 trim() 메서드와 같은 역할
  • 저장할 수 있는 공간은 만들었으나 데이터가 저장되어 있지 않을 때 해당 공간 없애줌
  • ex) ArrayList의 객체를 원격 전송 및 파일로 저장하는 경우 이 메서드 통해 데이터 크기 줄일 수 있음

Stack 클래스

  • List 인터페이스 구현
  • LIFO (Last In First Oust): 마지막에 들어온 데이터를 가장 처음에 꺼냄
LIFO 기능 가능한 클래스
Stack ArrayDeque
ArrayDeque보다 성능 약간 떨어짐
Thread safe
Stack보다 LIFO 빠름
Thread safe X

 

Stack 클래스의 상속 관계

java.lang.Object
    java.util.AbstractCollection<E>
        java.util.AbstractList<E>
            java.util.Vector<E>
                java.util.Stack<E>
  • Vector: Stack의 부모 클래스 ➡ Stack은 Vector 클래스에서 제공하는 모든 메서드 사용 가능
  • Stack 클래스는 자바에서 상속을 잘못 받은 클래스임(Vector에 속하면 안 됨, but 자바의 하위 호환성 위해 이 상속 관계 계속 유지 중)

 

Stack 클래스의 생성자

생성자 설명
Stack() 아무 데이터도 없는 Stack 객체 만듦

 

Stack 클래스의 메서드

리턴 타입 메서드 이름 및 매개 변수 설명
boolean empty() 객체가 비어있는지 확인
E peek() 객체의 가장 위에 있는 데이터 리턴
E pop() 객체의 가장 위에 있는 데이터 지운 후 해당 값 리턴
E push(E item) 매개 변수로 넘어온 데이터를 가장 위에 저장
int search(Object o) 매개 변수로 넘어온 데이터의 위치 리턴

 

package d.collection;

import java.util.Stack;

public class StackSample {
    public static void main(String[] args) {
        StackSample sample = new StackSample();
        sample.checkPeek();
    }
    public void checkPeek() {
        Stack<Integer> intStack = new Stack<Integer>();
        for(int loop = 0; loop < 5; loop++) {
            intStack.push(loop);
            System.out.println(intStack.peek());
        }
        System.out.println("size = " + intStack.size());
    }
}
//결과
//0
//1
//2
//3
//4
//size = 5

 

  • Stack에서 값 하나씩 확인하며 제거하려면
public void checkPop() {
    Stack<Integer> intStack = new Stack<Integer>();
    for(int loop = 0; loop < 5; loop++) {
        intStack.push(loop);
        System.out.println(intStack.pop());
    }
    System.out.println("size = " + intStack.size());
}
//결과
//0
//1
//2
//3
//4
//size = 0

'Backend > Java' 카테고리의 다른 글

[자바의 신] 24장  (0) 2022.08.09
[자바의 신] 23장  (0) 2022.08.09
[자바의 신] 21장  (0) 2022.08.09
[자바의 신] 20장  (0) 2022.07.26
[자바의 신] 19장  (0) 2022.07.26

댓글