본문 바로가기

JAVA

[WEB]DAY20_Set(HashSet), Map(HashMap)

Set

 : Set은 집합이다.
  중복되는 원소를 포함할 수 없다.

  값을 중복해서 저장할 수 없다.
  저장된 값들은 인덱스가 없기 때문에
  순서가 고정되어 있지 않다.

 

- 구현 클래스
     HashSet

 

     Set타입은 값의 유무 검사이다.
     따라서 순서는 필요하지 않다.
     만약 안에 있는 값을 가지고 오고 싶다면 각 값에
     순서를 부여해주어야 한다. 바로 이 작업을
     iterator()가 해준다.

 

- Set을 사용하는 이유

     Set은 검색의 목적이 있기 때문에 순서정보를 관리할
     필요가 없다. 따라서 데이터 크기에 상관없이
     검색에 걸리는 시간이 매우 짧다.
     반면 List는 인덱스를 관리해야하기 때문에 상대적으로
     시간이 오래 걸린다.

     그러므로 기능적 차이가 없다면 HashSet을 사용한다.

 


- iterator으로 순서를 부여하고, 리턴 타입이 Iterator<>인 객체에 담는다.

- iter.next() : 다음 값을 가져오는 것. 

 

package day20;

import java.util.HashSet;
import java.util.Iterator;

public class SetTest {
	public static void main(String[] args) {
		HashSet<String> blood_types = new HashSet<>();
		
		blood_types.add("A");
		blood_types.add("AB");
		blood_types.add("B");
		blood_types.add("O");
		blood_types.add("O");
		blood_types.add("O");
		blood_types.add("O");
		blood_types.add("O");
		blood_types.add("O");
		blood_types.add("O");
		
		System.out.println(blood_types);
		blood_types.remove("O");
		System.out.println(blood_types.contains("O"));

		//Set타입에 있는 값들에 순서를 부여해주는 iterator()라는 메소드가 있다.
		//리턴 타입은 Iterator<>타입이다.
		Iterator<String> iter = blood_types.iterator();
		while(iter.hasNext()) {
			System.out.println(iter.next());
		}
	}
}





 


- 검색을 할 때, 왜 Set을 사용하는지

 : List보다 검색 시간이 더 빠르다. List는 인덱스를 관리하지만 Set은 값만 관리하기 때문에.

package day20;

import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.IntStream;

public class SpeedTest {
	public static void main(String[] args) {
		final int SIZE = 10_000_000;
		final int DATA = 9_000_000;
		
		final List<Integer> arrList = new ArrayList<>();
		final Set<Integer> hashSet = new HashSet<>();
		
		//순차 병렬 집계 연산
		IntStream.range(0, SIZE).forEach(value ->{
			arrList.add(value);
			hashSet.add(value);
		});
		
		Instant start = Instant.now();
		arrList.contains(DATA);
		Instant end = Instant.now();
		long elapsedTime = Duration.between(start, end).toMillis();
		System.out.println("array list 검색 시간 : " + elapsedTime * 0.001 + "초");
		
		start = Instant.now();
		hashSet.contains(DATA);
		end = Instant.now();
		elapsedTime = Duration.between(start, end).toMillis();
		System.out.println("hash set 검색 시간 : " + elapsedTime * 0.001 + "초");
		
	}
}







HashMap

 : Key, Value , 한 쌍(Entry)으로 데이터를 관리한다.
  그러므로 검색에 용이하다.

  Key는 중복이 불가능한 Set타입이고
  Value는 중복이 가능한 Collection타입이다.

 

  키 ↔ |해쉬 테이블| ↔ 값

 

  Map 자료구조는 순서를 따지지 않기 때문에
  Set으로 묶은 후 Iterator를 통해 순서를 부여해야 한다.

 

- key값만 가져오는 방법 : Map.keySet()  ->Set타입

- value값만 가져오는 방법 : values()      -> Collection타입

- key, value를 한 쌍으로 분류해오는 방법 : entrySet() -> Set타입(key값은 항상 중복이 불가능. 그래서 Set타입)

- Set< Entry< key, value> > e = ...

e.getkey()

e.getValue()

 


- Entry는 자동완성시 java.util.Map을 사용.

package day20;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;

public class MapTest {
	public static void main(String[] args) {
		HashMap<String, Integer> fruitMap = new HashMap<>();
		ArrayList<Integer> prices = new ArrayList<>();
		
		fruitMap.put("사과", 1500);
		fruitMap.put("배", 2000);
		fruitMap.put("자두", 500);
		fruitMap.put("수박", 8000);
		
		System.out.println(fruitMap);
		fruitMap.put("자두", 1000);
		System.out.println(fruitMap.get("자두"));
		System.out.println(fruitMap.containsKey("자두"));
		
		Set<String> set = fruitMap.keySet();
		Iterator<String> iter = set.iterator();
		
		while(iter.hasNext()) {
			System.out.println(iter.next());
		}
		
//		for(int data : fruitMap.values()) {
//			System.out.println(data + "원");
//		}
		
		//Collection타입에서는 forEach()메소드를 제공해준다.
		//forEach(변수명 -> 실행할 문장);
		//실행할 문장에서 화살표 뒤에 있는 변수를 사용할 수 있다.
		//값이 더 이상 없을 때까지 반복한다.
		fruitMap.values().forEach(price -> prices.add(price));
		System.out.println(prices);
		
		//Key, Value 한 쌍이 필요할 때에는 keySet(), values() 2개를 사용하지 않고
		//entrySet()을 사용하여 한 번에 다 가지고 올 수 있다.
		Iterator<Entry<String, Integer>> iter_entry = fruitMap.entrySet().iterator();
		
		while(iter_entry.hasNext()) {
			//안에 있는 객체가 Entry<String, Integer>타입이므로
			//해당 객체에 넣어주고 사용한다.
			Entry<String, Integer> entry = iter_entry.next();
			System.out.println(entry.getKey() + "-" + entry.getValue() + "원");
		}
	}
}






 


 

package map;

public class User {
	private int num;
	
	public User() {}

	public User(int num) {
		super();
		this.num = num;
	}

	public int getNum() {
		return num;
	}

	public void setNum(int num) {
		this.num = num;
	}
	
	@Override
	public boolean equals(Object obj) {
		if(obj instanceof User) {
			User u = (User)obj;
			if(u.num == this.num) {
				return true;
			}
		}
		return false;
	}
	
	@Override
	public int hashCode() {
		return this.num;
	}
	
}




 


- equals를 재정의 하지 않으면 주소값을 비교하기 때문에 new를 할 경우, 주소가 다르기 때문에 get을 하면 null이 뜬다.

package map;

import java.util.HashMap;

public class MapTest2 {
	public static void main(String[] args) {
		HashMap<User, String> userMap = new HashMap<>();
		userMap.put(new User(1), "한동석");
		System.out.println(userMap.get(new User(1)));
	}
}

To do List

?) hasNext()는 어떤 메소드인가

    A) 다음에 element가 있으면 참, 없으면 거짓

 

 

?) User에서 왜 hashCode도 재정의 하는지?

    A) equals는 주소값과 값 등등을 비교하기 때문에 주소값도 재정의 해야한다.

 

 

?) 왜 Integer는 new를 하지 않고 바로 값을 넣을 수 있나요?

             ArrayList<Integer> dataList = new ArrayList<>;

             dataList.add(10);

             만약 내가 정의한 User객체(String name, int num)에 추가할 때에는

             ArrayList<User> userList = new ArrayList<>;

             userList.add(new User("손서연", 1 ));으로 해주어야 한다.

    A) Integer의 경우, Auto Boxing으로 인해 값을 넣어도 알아서 new Integer(10)으로 만들어주기 때문이다.

 

 

??) User객체를 추가할 경우에는 hashCode()를 재정의 해주어야하는데 왜 Integer은 재정의 하지 않나요?

    A) Integer의 경우 이미 재정의가 되어있는 상태이기 때문이다. String도 마찬가지로 재정의 되어있다.

 

 

?) 값을 추가할 때에는 그냥 값만 넣어도 되는데 왜 remove에서는 new를 하는가?

    A) 만약 그냥 remove(30)을 할 경우, 30을 index번호로 보기 때문에 new Integer(30)을 넣어준다.