자바 컬렉션 API (collection API)

Jimin·2022년 8월 17일
0

JAVA

목록 보기
11/23

자바 컬렉션 API (collection API)

자바에서 데이터 목록을 다루는 클래스를 말한다.

  • java.util.Collection 인터페이스를 구현한 객체이다.

List

  • 순서가 있는 요소의 모임
  • 빈요소를 허용하지 않는다.
  • 리스트에서 인덱스는 몇 번째 데이터인가 정도(순서)의 의미를 가진다.
    (배열, Array에서의 인덱스는 값에 대한 유일무이한 식별자)
  • 순차성을 보장하지 못하기 때문에 special locality 보장이 되지 않아서 cash hit가 어렵다.
    (데이터 갯수가 확실하게 정해져 있고, 자주 사용하게 된다면 Array가 더 효율적이다.)
  • 불연속적으로 메모리 공간을 차지
  • 포인터를 통한 접근

List의 장점

  • 포인터를 통하여 다음 데이타의 위치를 가르키고 있어서 삽입, 삭제가 용이하다.
  • 동적이므로 크기가 정해져있지 않다.
  • 메모리의 재사용 편리
  • 불연속적이므로 메모리 관리의 편리

List의 단점

  • 검색 성능이 좋지 않다.
  • 포인터를 통해 다음 데이터를 가르키므로 추가적인 메모리 공간 발생

배열 : 데이터의 크기가 정해져 있고, 추가적인 삽입 삭제가 일어 나지 않으며 검색을 필요로 할 때 유리.
리스트 : 데이터의 크기가 정해져 있지 않고, 삽입 삭제가 많이 일어나며, 검색이 적은 경우 유리.

Java List Collection

  • List는 Collection 인터페이스를 확장한 자료형으로, 데이터의 중복 입력이 가능하며 순차적이고 다량의 데이터를 입력할 때 주로 사용한다.
  • 종류는 Vector, ArrayList, LinkedList가 있다.

List 자료구조의 대표 기능

  • 처음, 끝, 중간에 요소를 추가, 삭제하는 기능
  • 리스트에 데이터가 있는지를 체크하는 기능
  • 리스트의 모든 데이터에 접근할 수 있는 기능

- List 추가

리스트명.add("값")

- List 삭제

리스트명.remove("값");
리스트명.remove(인덱스);

- List 값 변경

리스트명.set(인덱스, "바꿀값");

- List 크기 확인

리스트명.size();

- List에 특정 값 들었는지 확인

리스트명.contains("값");

- List가 비었는지 확인

리스트명.isEmpty();


ArrayList

배열을 이용하여 객체 목록을 다룬다.

  • 단점
    • 배열의 크기가 고정 ⇒ 배열의 크기가 초과 → 더 큰 새 배열을 만들고 기존 값을 복사해야한다.
    • 배열의 크기가 늘 때마다 가비지(garbage)가 생기는 문제가 있다.
    • 새 배열에 기존 배열의 값을 복사하기 때문에 속도가 느리다.
  • ArrayList에는 Generic이 적용되어 있기 때문에 저장할 객체의 타입을 명확하게 지정해주는 것이 편리하다.
    • 지정 안해주면 어느 타입이라도 동시에 들어갈 수 있다.

ArrayList 사용이유

  • 가장 큰 이유: 동적배열로 만들 수 있다는 점
  • 자바에서 배열은 정적 배열로 밖에 선언되지 않는다.
  • 따라서 배열의 크기를 동적으로 할당하고 싶을 때 ArrayList를 사용하면 된다.
  • 배열은 제네릭 타입을 사용할 수 없지만, ArrayList는 타입 안정성 보장을 위해 제네릭 사용이 가능하다.

List list = ArrayList로 선언해 사용하는 이유

List<Object> list = new ArrayList<Object>();
ArrayList<Object> list = new ArrayList<>();
  • 2번으로 안사용하고 1번으로 사용하는 이유?

    ⇒ 객체지향 프로그래밍의 일환으로, 다형성을 지원하기 위해서이다.
    처음부터 변경이 유리한 구조로 미리 설계하는 방식이라고 할 수 있다.

  • 예를 들자면, 2번으로 구현을 해버리면, 나중에 삽입/삭제가 유리한 LinkedList로 자료구조를 변경하고자 할 때 ArrayList로 선언된 모든 부분을 LinkedList로 다시 변경해주어야 한다.
    ⇒ 유지 보수가 어렵다.


ArrayList vs LinkedList

  1. 메모리
  • ArrayList
    • 고정 크기를 갖는다.
    • 크기를 초과하면 새로 배열을 만들어야 하기 때문에 메모리 낭비가 심하다.
    • 기존 배열은 가비지가 되기 때문에 가비지가 과다 생산된다.
  • LinkedList
    • 값을 넣을 때마다 새 메모리가 추가되는 가변 크기를 가진다.
    • ArrayList 보다 메모리 낭비가 적고 가비지를 덜 생산한다.
  1. 속도
  • ArrayList
    • 배열의 특징 상 인덱스를 이용하여 특정 항목을 찾을 때 속도 빠르다.
    • 삭제할 때 이전 항목을 당겨와야 하기 때문에 속도가 느리다.
    • 삽입할 때 현재 항목을 다음 항목으로 이동해야 하기 때문에 속도가 느리다.
  • LinkedList
    • 인덱스를 이용하여 특정 항목을 찾을 때 리스트의 처음부터 찾아야 하기 때문에 속도가 느리다.
    • 삭제할 때 이전 항목과 다음 항목을 바로 연결하면 되기 때문에 속도가 빠르다.
    • 삽입할 때 현재항목과 다음 항목을 새 항목과 연결하면 되기 때문에 속도가 빠르다.

ArrayList 목록 조회

반복문과 인덱스를 이용한 목록 조회

public class Exam0210 {
  public static void main(String[] args) {

    class Member {
      String name;
      int age;

      public Member(String name, int age) {
        this.name = name;
        this.age = age;
      }

    Member m1 = new Member("홍길동", 20);
    Member m2 = new Member("임꺽정", 30);
    Member m3 = new Member("유관순", 17);

    ArrayList<Member> list = new ArrayList<>();
    list.add(m1);
    list.add(m2);
    list.add(m3);

    for (int i = 0; i < list.size(); i++) {
      Member m = list.get(i);
      System.out.printf("이름: %s, 나이: %d\n", m.name, m.age);
    }
  }
}

toArray() 사용

리스트 형태를 배열로 만들어주는 메소드

- List.toArray()

  • List를 Array로 형 변환 시킬 때 사용한다.
List<String> list = new ArrayList<String>();

String[] arr = new String[5];

arr = list.toArray(arr);

- Object[] arr = list.toArray();

  • 장점: 모든 객체를 배열로 담을 수 있다.
  • 단점: 형변환을 해주어야 한다.
public class Exam0220 {
  public static void main(String[] args) {

    ...

    Member m1 = new Member("홍길동", 20);
    Member m2 = new Member("임꺽정", 30);
    Member m3 = new Member("유관순", 17);

    ArrayList<Member> list = new ArrayList<>();
    list.add(m1);
    list.add(m2);
    list.add(m3);

    Object[] arr = list.toArray();
    for (Object obj : arr) {
      Member m = (Member) obj;
      System.out.printf("이름: %s, 나이: %d\n", m.name, m.age);
    }
  }
}

- Member[] arr = list.toArray(new Member[list.size()]);

( Member[] arr = list.toArray(new Member[0] ) 이 방법도 가능하지만, 위의 방법 추천

  • Object로 배열을 받아서 형변환 하지 않고,
    바로 내가 원하는 데이터 타입으로 받기
 Member[] arr = list.toArray(new Member[list.size()]);

    for (Member m : arr) {
      System.out.printf("이름: %s, 나이: %d\n", m.name, m.age);
    }
  • for( : 여기에는 iterable 규칙을 만족하는 객체만 올 수 있다.)

Iterator 사용

Iterator<Member> iterator = list.iterator();

while (iterator.hasNext()) {
      Member m = iterator.next();
      System.out.printf("이름: %s, 나이: %d\n", m.name, m.age);
    }

java.util.Collection의 forEach() 사용법

  • forEach() 메서드에게 넘길 객체
    • Consumer 규칙에 따라 만들어야 한다.
    • List에 보관된 객체를 반복문을 통해 꺼낼 때마다 Consumer 규칙에 따라 accept()를 호출할 것이다.
  • forEach() 에서 반복문을 돌릴 때
    • Consumer 규칙에 따라 각 항목에 대해 이 메서드를 호출한다.
  • list안에 들어 있는 요소의 개수만큼 규칙에 맞추어 출력을 반복한다.
public class Exam0240 {
  public static void main(String[] args) {

    Member m1 = new Member("홍길동", 20);
    Member m2 = new Member("임꺽정", 30);
    Member m3 = new Member("유관순", 17);

    ArrayList<Member> list = new ArrayList<>();
    list.add(m1);
    list.add(m2);
    list.add(m3);

    class MyConsumer implements Consumer<Member> {
      @Override
      public void accept(Member m) {
        // forEach() 에서 반복문을 돌릴 때
        // Consumer 규칙에 따라 
        // 각 항목에 대해 이 메서드를 호출한다.
        System.out.printf("이름: %s, 나이: %d\n", m.name, m.age);
      }
    }

    // 의미:
    // => 야 List! 
    //    너가 갖고 있는 목록에서 값을 한 개 꺼낼 때 마다 
    //    지금 내가 파라미터로 넘겨주는 객체 있지?
    //    MyConsumer 객체 말이야.
    //    이 객체의 accept()를 호출해주렴.
    list.forEach(new MyConsumer());
  }
}
  • 익명 클래스 형태로 Consumer 객체 생성 후 forEach() 안에 넣어준다.
Consumer<Member> action = new Consumer<>() {
      @Override
      public void accept(Member m) {
        System.out.printf("이름: %s, 나이: %d\n", m.name, m.age);
      }
    };

    list.forEach(action);
  }
list.forEach(new Consumer<>() { // list안에 들어있는 요소의 개수만큼 호출한다.
      @Override
      public void accept(Member m) {
        System.out.printf("이름: %s, 나이: %d\n", m.name, m.age);
      }
    });
  }

- lambda문법으로 Consumer 구현체 만들기

collection.forEach(변수 -> 반복처리(변수))

list.forEach(m -> System.out.printf("이름: %s, 나이: %d\n", m.name, m.age));

- 메서드 레퍼런스로 인터페이스 구현체 전달하기

list.forEach(클래스A::클래스A에 있는 메소드)
단, 클래스A에 있는 메소드는 Consumer 클래스의 accpet()메소드와 같은 역할을 한다.
⇒ list안에 있는 요소들을 클래스 A에 있는 메소드의 형식에 맞추어 출력해줘!

 list.forEach(Exam0244::printMember);
public class Exam0244 {

  static class Member {
    String name;
    int age;

    public Member(String name, int age) {
      this.name = name;
      this.age = age;
    }

  public static void main(String[] args) {

    Member m1 = new Member("홍길동", 20);
    Member m2 = new Member("임꺽정", 30);
    Member m3 = new Member("유관순", 17);

    ArrayList<Member> list = new ArrayList<>();
    list.add(m1);
    list.add(m2);
    list.add(m3);

    // 메서드 레퍼런스로 인터페이스 구현체 전달하기
    list.forEach(Exam0244::printMember);
  }

  static void printMember(Member m) {
    System.out.printf("이름: %s, 나이: %d\n", m.name, m.age);
  }
}

profile
https://github.com/Dingadung

0개의 댓글