https://blog.naver.com/pgh7092/221076907521

Map 컬렉션(Map<K, V>)

  Map 컬렉션은 (key)와 값(value)으로 구성된 객체를 저장하는 구조를 갖는다키는 중복으로 저장할 수 없고 값은 중복으로 저장할 수 있다.(동일한 key로 값을 추가하면 기존 key에 해당하는 값은 제거된다.) 따라서 Map 컬렉션은 (key)로 데이터를 관리한다. Map 컬렉션을 구현하는 대표적인 클래스들은 HashMap, Hashtable, LinkedHashMap, TreeMap 등이 있다. 공통적으로 사용되는 주요 메서드들은 put, get, remove, keySet 등이 있다.



 K와 V는 제네릭으로 K는 key 타입, V는 value 타입을 의미한다K와 V는 기본 자료형(int, double, boolean )은 사용할 수 없고 클래스나 인터페이스 타입만 사용이 가능하다.
 Map에 저장된 값을 가져오기 위해서는 get 메서드로 key 값을 입력하여 하나씩 가져오거나, keySet 및 entrySet 메서드를 이용하여 Set 컬렉션에 key 및 value를 저장하여 반복문을 통해 가져올 수 있다. 해당 내용에 대한 예제는 구현 클래스에서 알아보도록 하겠다.

HashMap

 HashMap은 Map 컬렉션의 대표적인 구현 클래스이다. HashMap을 생성하는 방법은 아래와 같다.



예제를 통해서 HashMap을 사용하는 방법에 대해서 간단히 알아보자.

import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; public class HashMapEx01 { public static void main(String[] args) { // key는 String 타입으로, value는 Integer 타입으로 지정 Map<String, Integer> hm = new HashMap<String, Integer>(); // put 메서드 이용 key와 value 추가 hm.put("A", 90); hm.put("B", 80); hm.put("C", 80); hm.put("D", 60); // size 메서드 이용 저장된 객체 수 출력 System.out.println("저장된 객체 수 : " + hm.size()); // get 메서드로 특정 key에 해당하는 값 출력 System.out.println("D의 값 : " + hm.get("D")); // 동일한 key로 추가(기존 내용 삭제) hm.put("C", 70); System.out.println("-------------------"); // keySet 메서드 이용 저장된 모든 key 값을 Set 컬렉션에 저장 Set<String> keys = hm.keySet(); // 반복자 생성 Iterator<String> it = keys.iterator(); while (it.hasNext()) { String key = it.next(); // Set의 key 값을 하나씩 key에 대입 int value = hm.get(key); // 해당 key에 해당하는 value 대입 / 오토 언박싱 System.out.println(key + " : " + value); } } }

 설명은 예제의 주석 부분을 참고하면 된다.

Hashtable

 Hashtable은 HashMap과 동일한 사용법을 갖는다. 차이점은 Hashtable은 동기화(synchronization)를 보장한다. 따라서 멀티 스레드에서는 Hashtable을 사용해야 하고, 단일 스레드에서는 HashMap을 사용하는 게 유리하다. Hashtable을 생성하는 방법은 아래와 같다.



 이번에는 Hashtable을 생성하고 entrySet 메서드를 이용하여 데이터를 출력하는 방법을 알아보자.

import java.util.Hashtable; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; public class HashtableEx01 { public static void main(String[] args) { Map<String, String> ht = new Hashtable<String, String>(); // key와 value값 저장 ht.put("사과", "Apple"); ht.put("복숭아", "Peach"); ht.put("망고스틴", "Mangosteen"); ht.put("석류", "Poemegranate"); ht.put("딸기", "Strawberries"); // entrySet 메서드 이용 Set 컬렉션에 key와 value 저장 Set<Entry<String, String>> fruits = ht.entrySet(); // 반복자 생성 Iterator<Entry<String, String>> it = fruits.iterator(); while (it.hasNext()) { Entry<String, String> entry = it.next(); String key = entry.getKey(); String value = entry.getValue(); System.out.println(key + " : " + value); } } }





Set

 Set은 List와는 다르게 객체(데이터)를 중복해서 저장할 수 없다. 또한 저장된 객체(데이터)를 인덱스로 관리하지 않기 때문에 저장 순서가 보장되지 않는다. 수학의 집합과 비슷한 내용이다. Set 컬렉션을 구현하는 대표적인 클래스들은 HashSet, TreeSet, LinkedHashSet 등이 있다. Set 컬렉션을 구현하는 클래스들이 공통적으로 사용하는 주요 메소드는 add, iterator, size, remove, clear 등이 있고 내용은 아래의 표와 같다.



 Set은 인덱스로 객체를 관리하지 않기 때문에 데이터를 검색하기 위해서는 iterator() 메서드로 Iterator(반복자)를 생성하고 데이터를 가져와야 한다. 해당 내용에 대해서는 밑의 예제에서 알아보자.

HashSet

 HashSet은 Set 컬렉션을 구현하는 대표적인 클래스이다. 데이터를 중복 저장할 수 없고 순서를 보장하지 않는다. HashSet을 생성하는 방법은 아래와 같다.

Set<E> 객체명 = new HashSet<E>();

 예제를 통해서 HashSet 사용방법에 대해서 알아보자.

import java.util.HashSet; import java.util.Iterator; import java.util.Set; public class HashSetEx01 { public static void main(String[] args) { Set<String> set = new HashSet<String>(); set.add("one"); // 데이터 저장(추가) set.add("two"); set.add("three"); set.add("one"); set.add("two"); set.add("4"); set.add("5"); set.add("six"); System.out.println("저장된 데이터 수 : " + set.size()); // 데이터 수 출력 Iterator<String> it = set.iterator(); // Iterator(반복자) 생성 while (it.hasNext()) { // hasNext() : 데이터가 있으면 true 없으면 false System.out.println(it.next()); // next() : 다음 데이터 리턴 } System.out.println("--------------------"); set.remove("three"); // 데이터 제거 System.out.println("저장된 데이터 수 : " + set.size()); // 저장된 데이터 수 출력 it = set.iterator(); // 반복자 재생성(위의 반복문에서 next 메서드로 데이터를 다 가져왔기 때문에 재생성을 // 해야함) while (it.hasNext()) { System.out.println(it.next()); } System.out.println("--------------------"); } }

 위의 예제를 실행하면 아래와 같은 출력 결과를 갖는다. 데이터의 순서가 보장되지 않고 데이터의 중복을 허용하지 않는 것을 알 수 있다. 또한 데이터를 가져오기 위해서 반복자(iterator)를 이용하였고 설명은 예제의 주석을 참고하면 된다.

 HashSet에 객체를 저장하고 가져오는 방법은 아래와 같다.

import java.util.HashSet; import java.util.Iterator; import java.util.Set; class Member { // Member 클래스 String name; // 필드 String id; public Member(String name, String id) { // 생성자 this.name = name; // 필드값 초기화 this.id = id; } } public class HashSetEx02 { public static void main(String[] args) { Set<Member> set = new HashSet<Member>(); // 제네릭 타입이 Member인 HashSet 생성 set.add(new Member("토니 스타크", "ironman")); // 객체 추가 set.add(new Member("피터 파커", "spierman")); // 객체 추가 Iterator<Member> it = set.iterator(); // 반복자 생성 while (it.hasNext()) { Member mb = it.next(); // set에 저장된 다음 객체의 참조값 저장 System.out.println("아이디 : " + mb.id); // id 출력 System.out.println("이름 : " + mb.name); // name 출력 System.out.println("-----------------"); } } }

 위의 예제는 Member 클래스의 생성자를 통해 필드를 초기화하고 객체를 HashSet에 저장하여 값을 가져오는 방법이다.

import java.util.*; class MemberInfo { private String name; // private 필드 private String id; public String getName() {return name;} // 저장된 name값 리턴 public void setName(String name) {this.name = name;} // name값 설정 public String getId() {return id;} // 저장된 id값 리턴 public void setId(String id) {this.id = id;} // id값 설정 } public class HashSetEx03 { public static void main(String[] args) { MemberInfo mm; // MemberInfo 객체 선언 Set<MemberInfo> set = new HashSet<MemberInfo>(); // HashSet 생성 Scanner sc = new Scanner(System.in); // 스캐너 객체 생성 do { mm = new MemberInfo(); // 반복될때 마다 인스턴스 생성(반복문 외부에서 new 연산자 사용 시 1개의 객체만 저장됨) System.out.println("이름 입력 : "); String name = sc.next(); System.out.println("아이디 입력 : "); String id = sc.next(); mm.setName(name); // 위에서 입력한 name 값 설정 mm.setId(id); // id 값 설정 set.add(mm); // set에 mm객체 저장 System.out.println("계속 Y, 정지 N"); String yn = sc.next(); if (yn.equals("y") || yn.equals("Y")) { continue; // Y나, y 입력시 반복문 계속 } else { break; // 다른값 입력시 반복문 종료 } } while (true); System.out.println("---------------"); Iterator<MemberInfo> it = set.iterator(); // Iterator 생성 while (it.hasNext()) { mm = it.next(); System.out.println("이름 : " + mm.getName()); // getName 메서드 호출(name 값 리턴) System.out.println("아이디 : " + mm.getId()); // getId 메서드 호출(id 값 리턴) System.out.println("----------------"); } } }

 위의 예제는 데이터를 키보드로 입력받아서 객체를 HashSet에 저장하고 Iterator를 통해 저장된 객체를 출력하는 조금 더 응용된 예제이다. 설명은 주석을 참고하면 된다.

TreeSet

 TreeSet도 HashSet과 같이 중복된 데이터를 저장할 수 없고 입력한 순서대로 값을 저장하지 않는다. 차이점은 TreeSet은 기본적으로 오름차순으로 데이터를 정렬한다. 예제를 통해서 알아보자.

import java.util.*; public class TreeSetEx01 { public static void main(String[] args) { Set<Integer> set = new TreeSet<Integer>(); set.add(4); // 데이터 추가 set.add(2); set.add(1); set.add(3); set.add(1); set.add(3); Iterator<Integer> it = set.iterator(); // 반복자 생성 while (it.hasNext()) { System.out.println(it.next()); } System.out.println("----------"); Set<String> ts = new TreeSet<String>(); ts.add("a"); ts.add("c"); ts.add("d"); ts.add("b"); Iterator<String> itr = ts.iterator(); while (itr.hasNext()) { System.out.println(itr.next()); } } }

 위의 예제를 실행하면 중복을 허용하지 않고 오름차순으로 데이터를 저장하는 것을 알 수 있다.

LinkedHashSet

LinkedHashSet도 중복된 데이터를 저장할 수 없다. 차이점은 입력된 순서대로 데이터를 관리한다. 

import java.util.*; public class LinkedHashSetEx01 { public static void main(String[] args) { Set<String> set = new LinkedHashSet<String>(); set.add("1"); set.add("1"); set.add("two"); set.add("3"); set.add("4"); set.add("five"); Iterator<String> it = set.iterator(); while (it.hasNext()) { System.out.println(it.next()); } System.out.println("--------------------------"); LinkedHashSet<Integer> lh = new LinkedHashSet<Integer>(); lh.add(1); lh.add(1); lh.add(4); lh.add(2); lh.add(3); Iterator<Integer> it2 = lh.iterator(); while (it2.hasNext()) { System.out.println(it2.next()); } } }

 예제를 실행하면 중복된 데이터는 제외되고 입력된 순서로 값을 출력하게 된다.



List 컬렉션

 이전 포스팅에서 설명하였 듯이 List 컬렉션은 인덱스 순서로 저장이 되며, 중복된 데이터가 저장이 가능하다. 구조적으로 데이터를 일렬로 늘여놓는 구조를 갖는다. 객체(데이터 등)를 저장하면 인덱스가 자동으로 부여되고 부여된 인덱스를 통해 데이터의 검색 및 삭제가 가능하다.(인덱스에는 데이터가 저장되어 있는 참조 값을 갖는다.)  List 컬렉션을 구현하는 대표적인 클래스들은 ArrayList, Vector, LinkedList가 있다. List 인터페이스를 구현하는 클래스들이기 때문에 공통적으로 사용할 수 있는 메서드들이 많다. 기본적인 List의 메소드는 아래와 같다.


 ArrayList

 ArrayList는 List 인터페이스를 구현한 클래스이다설정된 저장 용량(capacity)보다 많은 데이터가 들어오면 자동으로 용량이 늘어난다. ArrayList를 생성하는 방법은 아래와 같다.

List<E> 객체명 = new ArrayList<E>([초기 저장용량]);

 초기 저장용량을 생략하면 기본적으로 10의 저장용량을 갖는다E는 제네릭 타입을 의미하는데 생략하면 Object 타입이 된다. Object는 모든 데이터 타입을 저장 가능하지만 데이터를 추가하거나 검색할 때 형 변환을 해야 한다. 자료구조에는 주로 동일한 데이터 타입을 저장하기 때문에 제네릭 타입을 지정하고 사용하는 것이 좋다. 기본적으로 데이터를 추가할 때는 인덱스 순으로 자동 저장된다. 이때 중간에 데이터를 추가하거나 삭제할 경우에는 인덱스가 한 칸씩 뒤로 밀리거나 당겨진다




import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class ArrayListEx01 { public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("리스트1"); // 0번 index에 데이터 추가 list.add("리스트2"); // 1번 index에 데이터 추가 list.add("리스트3"); // 2번 index list.add("리스트4"); // 3번 index list.add(1, "리스트5"); // 1번 index에 데이터 추가(기존의 1번 이후의 인덱스들이 하나씩 뒤로 밀림) list.add("리스트6"); // 5번 index System.out.println(list.size()); // 저장된 데이터 용량 출력 : 6 System.out.println(list.get(0)); // 0번 index 값 출력 System.out.println(list.get(1)); // 1번 index 값 출력 System.out.println(); for (int i = 0; i < list.size(); i++) { // 반복문 이용 데이터 출력 System.out.println(i + "번 인덱스 데이터 : " + list.get(i)); } System.out.println(); list.remove(0); // 0번 index 삭제(하나씩 앞으로 당겨짐) list.remove("리스트5"); // 리스트5를 갖는 데이터 삭제(이후의 데이터들은 하나씩 앞으로 당겨짐) for (String value : list) { // for each 반복문 이용 데이터 출력 System.out.println(value); } System.out.println(); Iterator<String> elements = list.iterator(); // Iterator (반복자) while (elements.hasNext()) { // Iterator 이용 데이터 출력 / hasNext : 데이터가 있으면 true 없으면 false 리턴 System.out.println(elements.next()); // next() 다음 데이터 리턴 } } }

 아래 예제는 제네릭 타입을 클래스 타입으로 지정하고 데이터를 저장하고 갖고 오는 방법이다. 기본적인 방법은 똑같다.

class Avengers { // Avengers 클래스 정의 String name; // 필드 String id; int age; public Avengers(String name, String id, int age) { // 생성자 초기화 this.name = name; this.id = id; this.age = age; } } public class VectorEx01 { public static void main(String[] args) { List<Avengers> list = new ArrayList<Avengers>(); // ArrayList list.add(new Avengers("토니 스타크", "ironman", 53)); // 객체 생성 및 저장 list.add(new Avengers("피터 파커", "spiderman", 25)); list.add(new Avengers("브루스 배너", "Hulk", 50)); for (int i = 0; i < list.size(); i++) { // 반복문 Avengers av = list.get(i); System.out.println("이름 : " + av.name + "\n아이디 : " + av.id + "\n나이 : " + av.age); System.out.println("--------------------"); } } }

 처음 예제와 다른 점이라면 Avengers 자료형에 ArrayList의 인덱스에 저장된 참조값을 얻어오고, 해당 참조값으로 해당되는 객체의 데이터(name, id, age)를 구해와야 한다.


Vector

 ArrayList와 동일한 구조를 갖는다차이점이라면 Vector는 자동 동기화를 보장하므로 멀티 스레드 환경에서 안정적으로 사용이 가능하다하지만 단일 스레드에서는 ArrayList가 성능이 더 좋다. Vector를 생성하는 방법은 다음과 같다.

List<E> list = new Vector<e>([초기용량,</e> 증가용량]);

 초기용량과 증가용량을 생략하면 기본 값인 0으로 설정된다. 아래 예제는 키보드로 회원정보를 입력받고 데이터를 저장해 두었다가 출력하는 예제이다.

import java.util.List; import java.util.Scanner; import java.util.Vector; class MemberInfo{ private int no; private String name; private String phoneNumber; private String address; // getter(저장된 데이터를 리턴) / setter(필드값 세팅) 메소드 public int getNo() {return no;} public String getName() {return name;} public String getPhoneNumber() {return phoneNumber;} public String getAddress() {return address;} public void setNo(int no) {this.no = no;} public void setName(String name) {this.name = name;} public void setPhoneNumber(String phoneNumber) {this.phoneNumber = phoneNumber;} public void setAddress(String address) {this.address = address;} } public class VectorEx02 { public static void main(String[] args) { List<MemberInfo> list = new Vector<MemberInfo>(); do { Scanner sc = new Scanner(System.in); System.out.println("회원번호 입력"); int no = sc.nextInt(); System.out.println("이름 입력"); String name = sc.next(); System.out.println("핸드폰 번호 입력"); String phoneNumber = sc.next(); System.out.println("주소 입력"); String address = sc.next(); MemberInfo mm = new MemberInfo(); mm.setNo(no); // no 셋팅 mm.setName(name); // name 셋팅 mm.setPhoneNumber(phoneNumber); // phoneNumber 셋팅 mm.setAddress(address); // address 셋팅 list.add(mm); // list에 객체 저장 System.out.println("계속은 y, 아니면 아무키 입력"); String yn = sc.next(); if(yn.equals("y") || yn.equals("Y")){ continue; } else { break; } } while (true); for (int i = 0; i < list.size(); i++) { MemberInfo m = list.get(i); // list 인덱스에 저장된 객체의 참조값 저장 System.out.println("회원 번호 : " + m.getNo()); // 해당 데이터 출력 System.out.println("이름 : " + m.getName()); System.out.println("핸드폰 번호 : " + m.getPhoneNumber()); System.out.println("주소 : " + m.getAddress()); System.out.println("-------------------------------"); } } }
LinkedList

 List의 구현 클래스이므로 ArrayList나 Vector와 사용 방법은 동일하다하지만 구조는 다르게 구성되어 있다위의 컬렉션들은 인덱스로 데이터를 관리하지만 LinkedList는 인접한 곳을 링크하여 체인처럼 관리한다. LinkedList는 중간의 데이터를 삭제할 때 인접한 곳의 링크만을 변경하면 되기 때문에 중간에 데이터를 추가/삭제하는 경우 처리 속도가 빠르다차이점은 아래와 같다.

 상황에 따라 적절한 컬렉션을 사용하면 된다. LinkedList를 생성하는 방법은 아래와 같다.

List<E> list = new LinkedList<E>();



이번 포스팅은 자바에서 사용되는 컬렉션 프레임워크(자료구조)가 무엇이고 어떠한 종류가 있는지에 대해서 알아보도록 하겠습니다.

컬렉션 프레임워크(자료구조) ?

 객체나 데이터들을 효율적으로 관리(추가, 삭제, 검색, 저장)하기 위해서 사용하는 라이브러리를 의미한다. java.util 패키지에 포함된 인터페이스들(List, Set, Map, Queue, Stack 등)을 구현한 클래스들이 컬렉션 프레임워크로 사용된다.
 이전에 컬렉션 프레임워크와 비슷한 배열에 대해서 알아보았다. 배열은 저장할 크기가 배열을 생성할 때 결정되어 배열의 크기가 넘어가면 저장이 불가능하다. 또한 데이터를 삭제하면 해당 인덱스의 데이터는 비어있는 구조를 갖는 등 여러 문제점들이 발생된다. 이러한 배열이 갖는 문제점들을 컬렉션 프레임워크를 통해서 해결이 가능하다. 컬렉션 프레임워크들은 동적으로 데이터를 저장하는 크기(용량)가 자동적으로 늘어나고, 중간의 데이터를 삭제하면 인덱스가 하나씩 앞으로 당겨진다. 

컬렉션 프레임워크 종류 / 차이점

 컬렉션 프레임워크들은 데이터들을 담는 그릇이라는 점에서는 동일하지만 성격(특징)에 따라 다양한 클래스(인터페이스) 들이 존재한다. 대표적인 컬렉션 프레임워크에 대해서 알아보자.
 우선 java.util 패키지에 Collection과 Map 인터페이스가 있다. 먼저 Collection 인터페이스를 상속받는 대표적인 인터페이스들이 List, Set 등이 있고 이들을 구현하는 클래스들이 자료구조에 속한다. List를 구현하는 클래스들은 ArrayList, Vector, LinkedList 등이 있고, Set을 구현하는 클래스들은 HashSet, LinkedHashSet, TreeSet 등이 있다.
 또한 Map 인터페이스를 상속받는 구현 클래스(HashMap, Hashtable, TreeMap 등)가 있고 이들 또한 자료구조에 속한다. 

 이러한 컬렉션 프레임워크들은 어떠한 인터페이스를 구현하였는지에 따라서 기본적인 성격이 달라진다.우선 List를 구현하는 클래스들은 인덱스 순서에 따라 데이터가 저장되고 데이터의 중복 저장이 가능하다. Set을 구현하는 클래스들은 순서를 유지하지 않고 데이터를 저장하며 중복된 데이터의 저장이 불가능하다Map을 구현하는 클래스들은 키(key)와 값(value)을 동시에 저장한다.(즉, 키(key)를 통해 값을 불러온다.) 이때 키는 중복 저장이 불가능하다.

 해당 인터페이스를 구현하는 클래스들은 동일한 조작법(메소드)을 갖고 있지만 동작하는 내용들은 조금씩 다르다. 이들이 어떠한 동작을 하는지는 다음 포스팅부터 List, Set, Map 별로 하나씩 알아보도록 하겠다.



+ Recent posts