본문 바로가기

개발/Java

[Java 25] 자바 - 컬렉션 프레임워크 (1)

학습목표

  1. 컬렉션 프레임워크의 기본적인 개념을 이해한다.
  2. List, Set, Map의 사용법을 알고있다.

 

1. 컬렉션 프레임워크 소개

Collections Framework Class diagram
출처: 생활코딩

자료구조를 바탕으로 객체들을 효율적으로 추가, 삭제, 검색할 수 있도록 java.util 패키지에 컬렉션과 관련된 인터페이스와 클래스들을 포함시켜 놓았습니다. 이를 총칭해 컬렉션 프레임워크라고 합니다.

 

|---------|-------|--------|
| | 특징 | 구현 클래스 |
| collection | List | - 순서를 유지하고 저장 - 중복 저장가능 | ArrayList, Vetor, LinkedList |
| collection | Set | - 순서를 유지하지 앟고 저장 - 중복 저장 안됨 | HashSet, TreeSet |
| Map | - 키와 값의 쌍으로 저장 - 키는 중복 저장 안됨 | HashMap, Hashtable, TreeMap, Properties |

 

인터페이스 분류  특징  구현 클래스 
collection  List 순서를 유지하고 저장 - 중복 저장가능 ArrayList, Vetor, LinkedList
collection  Set 순서를 유지하지 않고 저장 - 중복 저장 안됨 HashSet, TreeSet
Map  키와 값의 쌍으로 저장 - 키는 중복 저장 안됨 ashMap, Hashtable, TreeMap, Properties


2. List 컬렉션

  • 객체를 일렬로 늘어놓은 구조
  • 객체를 인덱스로 관리하기 때문에 객체를 저장하면 자동 인덱스가 부여되고 인덱스를 통해 검색, 삭제가 가능
  • List 컬렉션은 객체 자체를 저장하는 것이 아니라 객체의 번지를 참조
  • 동일한 객체를 중복 저장이 가능한데, 이 경우 동일한 번지가 참조( null도 저장 가능 )


2-1. ArrayList

  • 생성할 때 크기가 고정되는 배열과 달리, ArrayList는 저장 용량을 초과한 객체들이 들어오면 자동적으로 저장용량 늘어남
  • ArrayList에 객체를 추가하면 인덱스 0부터 차례대로 저장
  • 특정 인덱스의 객체 제거하면 이후의 모든 인덱스가 1씩 당겨지고 특정 인덱스에 객체 삽입하면 이후의 모든 인덱스가 1씩 밀린다.
List<String> list = new ArrayList<String>();    //컬렉션 생성
list.add("홍길동");     //컬렉션에 객체를 추가
String name = list.get(0);    //컬렉션에서 객체 검색, 홍길동 바로 얻음


예제 1

import java.util.ArrayList;
import java.util.List;

public class ArrayListExample {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();

        //String 객체를 저장
        list.add("Java");
        list.add("JVM");
        list.add("abcd");
        list.add("ABCD");

        //저장된 총 객체수 반환
        int size = list.size();
        System.out.println("총 객체수 : " + size);
        System.out.println();

        //2번 인덱스의 객체 얻기
        String skill = list.get(2);
        System.out.println("2 : " + skill);
        System.out.println();

        // 저장된 객체 수만큼 루핑
        for(int i =0; i <list.size(); i++) {
            String str = list.get(i);
            System.out.println(i + ":" + str);
        }
        System.out.println();

        list.remove(2);    // 2번 인덱스 객체 삭제
        list.remove(2); // 2번 인덱스 객체 삭제
        list.remove("abcd");

        // 저장된 객체 수만큼 루핑
        for(int i =0; i <list.size(); i++) {
            String str = list.get(i);
            System.out.println(i + ":" + str);
        }
    }
}


실행결과

총 객체수 : 4

2 : abcd

0:Java
1:JVM
2:abcd
3:ABCD

0:Java
1:JVM


예제 2

import java.util.Arrays;
import java.util.List;

public class ArraysAsListExample {
    public static void main(String[] args) {
        List<String> list1 = Arrays.asList("홍길동", "김길동", "자바"); //왜 new 안해줌?
        for(String name: list1) {
            System.out.println(name);
        }

        List<Integer> list2 = Arrays.asList(1,2,3);
        for(int value : list2) {
            System.out.println(value);
        }
    }
}


실행결과

홍길동
김길동
자바
1
2
3


3. Set 컬렉션

  • List 컬렉션은 저장 순서를 유지하지만, Set 컬렉션은 저장 순서가 유지되지 않음
  • 수학의 집합이라고 생각하면 된다.
Set<String> set = ...;
set.add("홍길동");    //객체 추가
set.add("김길동");
set.remove("홍길동");    //객체 삭제
  • Set 컬렉션은 인덱스로 객체를 검색해서 가져오는 메소드는 없다. 대신 전체 객체를 대상으로 한번씩 반복해서 가죠오는 반복자(Iterator)를 제공한다.
Set<String> set = ...;
Iterator<String> iterator = set.iterator();
  • 다음은 Iterator 인터페이스에 선언된 메소드들이다.

    리턴 타입 메소드명  설명
    boolean  hasNext() 가져올 객채가 있으면 true 리턴하고 없으면 false 리턴
    E next() 컬렉션에서 하나의 객체를 가져온다
    void remove() Set 컬렉션에서 객체를 가져온다
  • Iterator에서 하나의 객체를 가져올 때는 next() 메소드를 사용한다. next() 메소드를 사용하기 전에 먼저 가져올 개체가 있는지 확인
  • hasNext() 메소드는 가져올 객체가 있으면 true 리턴하고 더 이상 가져올 객체가 없으면 false를 리턴
  • 따라서 true가 리턴될 때 next() 메소드를 사용해야 한다.
Set<String> set = ...;        
    Iterator<String> iterator = set.iterator();
    // 저장된 객체 수만큼 루핑한다
    while (iterator.hasNext()) {
        // String 객체 하나를 가져옴
        String str = iterator.next();
    }
  • Iterator 사용하지 않더라도 향상된 for문을 이용해서 전체 객체 대상으로 반복이 가능
Set<String> set = ...;
// 저장된 객체 수만큼 루핑한다
for(String str : set) {
}
  • remove() 메소드를 통한 객체 제거 가능
while(iterator.hasNext()) {
    String str = iterator.next();
    if(str.equals("홍길동")) {
        iterator.remove();
    }
}


3-1. HashSet

  • HashSet 생성을 위한 기본 생성자
Set<E> set = new HashSet<E>();
  • HashSet은 객체들을 순서 없이 저장하고 동일한 객체는 중복 저장하지 않는다.

public class HashSetExample {
public static void main(String[] args) {
    Set<String> set = new HashSet<String>();

    set.add("Java");
    set.add("JDBC");
    set.add("Servlet/JSP");
    set.add("Java");    // "Java"는 한번만 저장됨
    set.add("abcde");

    int size = set.size();    // 저장된 객체 수 얻기
    System.out.println("총 객체수 : " + size);

    Iterator<String> iterator = set.iterator(); // 반복자 얻기
    // 객체 수만큼 루핑
    while (iterator.hasNext()) {
        String element = iterator.next();    // 한개의 객체를 가져온다
        System.out.println("\t" + element);
    }
    // 객체 삭제
    set.remove("JDBC");
    set.remove("abcde");

    System.out.println("총 객체수: " + set.size());

    iterator = set.iterator();    // 반복자 얻기
    // 객체 수만큼 루핑
    while(iterator.hasNext()) {
        String element = iterator.next();
        System.out.println("\t" + element);
    }
    set.clear();    // 모든 객체 제거
    if(set.isEmpty()) { System.out.println("비어 있음"); }
}
}


실행결과

총 객체수 : 4
    Java
    abcde
    JDBC
    Servlet/JSP
총 객체수: 2
    Java
    Servlet/JSP
비어 있음


예제 2

사용자 정의 클래스인 Member를 만들고 hashCode()와 equals() 메소드를 오버라이딩하였다. 이를 통해 인스턴스가 달라도 이름과 나이가 동일하면 동일한 객체로 간주하여 중복 저장을 방지한다.

public class Member {
    public String name;
    public int age;

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

    @Override
    public boolean equals(Object obj) {    //name과 age 값이 같으면 true 리턴
        if (obj instanceof Member) {
            Member member = (Member) obj;
            return member.name.equals(name) && (member.age == age);
        } else {
            return false;
        }
    }

    @Override
    public int hashCode() {        //name과 age 값이 같으면 동일한 hashCode 리턴
        return name.hashCode() + age;
    }
}
import java.util.*;

public class HashSetExample2 {
    public static void main(String[] args) {
        Set<Member> set = new HashSet<Member>();

        //인스턴스는 다르지만 내부 데이터가 동일하므로 객체 1개만 저장
        set.add(new Member("홍길동", 20));
        set.add(new Member("홍길동", 20));    

        System.out.println("총 객체수 : " + set.size());
    }
}


실행결과

총 객체수 : 1


4. Map 컬렉션

  • 키(key)와 값(value)으로 구성된 Entry 객체를 저장하는 구조
  • 키와 값은 모두 객체이다.
  • 키는 중복 저장될 수 없고, 값은 중복 저장이 가능하다.
  • 만약 기존에 저장된 키와 동일한 키로 값을 저장하면 기존의 값은 없어지고 새로운 값으로 대치된다.
Map<String, Integer> map = ~;
map.put("홍길동", 30);    //객체 추가
int score = map.get("김길동");  //객체 찾기
map.remove("홍길동");    //객체 삭제
  • 키를 알고 있다면 get() 메소드로 간단하게 객체를 찾아오면 되지만, 저장되니 객체를 대상으로 하나씩 얻고 싶은 경우에는 두가지 방법을 사용할 수 있다.

방법 1

keySet() 메소드로 모든 키를 Set 컬렉션으로 얻은 다음, 반복자를 통해 키를 하나씩 얻고 get() 메소드를 통해 값을 얻는다.

Map<K, V> map = ~;
Set<K> keyset = map.keySet();        
Iterator<K> keyIterator = keySet.iterator();
    while (keyiterator.hasNext()) {
        K key = keyIterator.next();
        v value = map.get(key);
    }

방법 2

entrySet() 메소드로 모든 Map.Entry를 Set 컬렉션으로 얻은 다음, 반복자를 통해 Map.Entry를 하나씩 얻고 getValue() 메소드를 이용해 키와 값을 얻으면 된다.

Set<Map.Entry<K, V>> entrySet = map.entrySet();
Iterator<Map.Entry<K, V>> entryIterator = entrySet.iterator();
while(entryIterator.hasNext()) {
    Map.Entry<K, V> entry = entryIterator.next();
    K key = entry.getKey();
    V value = entry.getValue();
}


4-1. HashMap

  • HashMap의 키로 사용할 객체는 hasCode()와 equals() 메소드를 재정의해서 동등 객체가 될 조건을 정해야 한다.
  • 동등 개체, 즉 동일한 키가 될 조건은 hashCode()의 리턴값이 같아야 하고, equals() 메소드가 true를 리턴해야 한다.

  • 주로 키 타입은 String을 많이 사용하는데, String은 문자열이 같을 경우 동등 객체가 될 수 있도록 hashCode()와 equals() 메소드가 재정의되어 있다
  • HashMap을 생성하기 위해서는 키 타입과 값 타입을 파라미터로 주고 기본 생성자를 호출하면 된다.
Map<K, V> map = new HashMap<K, V>(); //K: 키 타입, V: 값 타입
  • 키와 값의 타입은 기본타입을 사용할 수 없고 클래스 및 인터페이스 타입만 가능
  • 키로 String 타입을 사용하고 값으로 Integer 타입을 사용하는 HashMap은 다음과 같이 생성된다.
Map<String, Integer> map = new HashMap<String, Integer>();

예제 1

public class HashMapExample {
public static void main(String[] args) {
    Map<String, Integer> map = new HashMap<String, Integer>();

    map.put("홍길동", 85);
    map.put("박길동", 90);
    map.put("김길동", 80);
    map.put("홍길동", 95);    // "홍길동" 키가 같기 떄문에 제일 마지막에 저장한 값으로 대치
    System.out.println("총 Entry 수 : " + map.size());

    // 객체 찾기
    System.out.println("\t홍길동 : " + map.get("홍길동")); //이름(키)으로 점수(값)를 검색
    System.out.println();

    // 객체를 하나씩 처리
    Set<String> keySet = map.keySet();    //Key Set 얻기
    Iterator<String> keyIterator = keySet.iterator();
    while (keyIterator.hasNext()) {
        String key = keyIterator.next();
        Integer value = map.get(key);
        System.out.println("\t" + key + " : " + value);
    }

    System.out.println();

    // 객체 삭제
    map.remove("홍길동");
    System.out.println("총 Entry 수 :" + map.size());

    // 객체 하나씩 처리
    Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
    Iterator<Map.Entry<String, Integer>> entryIterator = entrySet.iterator();

    while (entryIterator.hasNext()) {
        Map.Entry<String, Integer> entry = entryIterator.next();
        String key = entry.getKey();
        Integer value = entry.getValue();
        System.out.println("\t" + key + " : " + value);
    }
    System.out.println();

    map.clear();
    System.out.println("총 entry 수: " + map.size());
}
}

실행결과

총 Entry 수 : 3
    홍길동 : 95

    박길동 : 90
    김길동 : 80
    홍길동 : 95

총 Entry 수 :2
    박길동 : 90
    김길동 : 80

총 entry 수: 0


예제 2

class Student {
    public int sno;
    public String name;

    public Student(int sno, String name) {
        this.sno = sno;
        this.name = name;
    }

    public boolean equals(Object obj) {    //학번과 이름이 동일할 경우 true 리턴
        if (obj instanceof Student) {
            Student student = (Student) obj;
            return (sno == student.sno) && (name.contentEquals(student.name));
        } else {
            return false;
        }
    }
    // 학번과 이름이 같다면 동일한 값을 리턴
    public int hashCode() {
        return sno + name.hashCode();
    }
}
public class HashMapExample2 {
public static void main(String[] args) {
    Map<String, Integer> map = new HashMap<String, Integer>();

    //학번과 이름이 동일한 경우 같은 키로 인식
    map.put("홍길동", 95);
    map.put("홍길동", 95);
    System.out.println("총 Entry 수 : " + map.size());    
    }
}

'개발 > Java' 카테고리의 다른 글

[Java 26] 자바 - 컬렉션 프레임워크(2)  (0) 2021.05.29
[Java 24] 자바 - 제네릭  (0) 2021.05.29
[Java 23] 자바 - 기본 API 클래스  (0) 2021.05.29
[Java 22] 자바 - 예외 처리  (0) 2021.05.28
[Java 21] 자바 - 인터페이스(2)  (0) 2021.05.28