반응형

 

Hash 함수

 

: 임의의 길이의 데이터를 고정된 길이의 데이터로 매핑해주는 함수

속도가 빠름

 

충돌이 적고 빠른 Hash 함수가 좋다.

 

 

  • HashSet, HashMap, Hashtable 객체 사용시

equals()와 hashCode() 사용 => 추가하는 객체가 서로 같은지 비교
서로 같은지 여부 결정 시 두 메서드를 재정의하는 과정 필요.

 

equals() hashCode()
두 객체의 내용(값)이 같은지 비교 객체에 대한 해시코드값 반환
=> 해시 테이블 생성 시 사용

 

 

 

equals()메서드 & hashCode()메서드 작성 규칙(Convention)

 

1. 두 객체가 같으면 같은 hashCode()

package kr.or.ddit.basic;

import java.util.HashSet;
import java.util.Set;

public class T07EqualsHashCodeTest {
	public static void main(String[] args) {
		// 1. 두 객체가 같으면 반드시 같은 hashCode를 가져야 한다.
		String str1 = "홍길동";
		
		System.out.println(str1.hashCode());
		System.out.println(str1.hashCode());
		System.out.println();
	}
}

 

결과 화면1

 


2. 두 객체가 같으면 equals()를 호출 시 true 반환
즉, 객체 a, b가 같다면 a.equals(b)와 b.equals(a) 둘 다 true이어야 한다.

package kr.or.ddit.basic;

import java.util.HashSet;
import java.util.Set;

public class T07EqualsHashCodeTest {
	public static void main(String[] args) {
		String str1 = "홍길동";
		
		// 2. 두 객체가 같으면 equals()를 호출했을때 true를 반환해야 한다.
		System.out.println(str1.equals(str1));
		System.out.println();
	}
}

 

결과 화면2

 


3. hashCode() 같음 but 같은 객체 아님
하지만, 두 객체가 같으면 반드시 hashCode()가 같아야 한다.

package kr.or.ddit.basic;

import java.util.HashSet;
import java.util.Set;

public class T07EqualsHashCodeTest {	
	public static void main(String[] args) {
		// 3. 두 객체가 hashCode()가 같다고 해서 두 객체가 반드시 같은 객체는 아니다.
		String str3 = "Aa";
		String str4 = "BB";
		System.out.println(str3.hashCode());
		System.out.println(str4.hashCode());
		System.out.println();
	}
}

 

결과 화면3

 


4. equals() 재정의 시 => hashCode() 재정의

package kr.or.ddit.basic;

import java.util.HashSet;
import java.util.Set;

public class T07EqualsHashCodeTest {
	public static void main(String[] args) {		
		Person p1 = new Person(1, "홍길동");
		Person p2 = new Person(1, "홍길동");
		Person p3 = new Person(1, "이순신");
		
		System.out.println("P1 == P2 : " + (p1 == p2));
		System.out.println("p1.equals(p2) : " + p1.equals(p2));
	}
}

class Person {
	private int id;
	private String name;
	
	public Person(int id, String name) {
		super();
		this.id = id;
		this.name = name;
	}
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
	@Override
	public String toString() {
		return "Person [id=" + id + ", name=" + name + "]";
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + id;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}
	
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Person other = (Person) obj;
		if (id != other.id)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}	
}

 

결과 화면4

 


5. hashCode()는 Heap메모리에 있는 메모리 주소값을 기반으로 정수값 반환
그러므로 클래스에서 hashCode()메서드를 재정의 하지 않으면 절대로 두 객체가 같은 것으로 간주될 수 없다.

package kr.or.ddit.basic;

import java.util.HashSet;
import java.util.Set;

public class T07EqualsHashCodeTest {
	public static void main(String[] args) {
		Person p1 = new Person(1, "홍길동");
		Person p2 = new Person(1, "홍길동");
		Person p3 = new Person(1, "이순신");
		
		Set<Person> pSet = new HashSet<Person>();
		boolean isAdded = pSet.add(p1);
		System.out.println("p1 추가 후 : " + isAdded);
		
		isAdded = pSet.add(p2);
		System.out.println("p2 추가 후 : " + isAdded); // 중복이라 false
		
		isAdded = pSet.add(p3);
		System.out.println("p3 추가 후 : " + isAdded);
		System.out.println();
		
		System.out.println("pSet 데이터 : " + pSet);
	}
}

class Person {
	private int id;
	private String name;
	
	public Person(int id, String name) {
		super();
		this.id = id;
		this.name = name;
	}
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
	@Override
	public String toString() {
		return "Person [id=
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + id;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}
	
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Person other = (Person) obj;
		if (id != other.id)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
	
}

 

결과 화면5

 

 

 

HashMap

 

: key와 value (=> Entry)한 쌍으로 이루어진 자료형

 

 

- 특징

1. key + value(=> Entry)로 구성되어 한 쌍의 데이터로 관리한다.

2. key 값은 중복을 허용하지 않고, 순서를 유지하지 않는다. (Set의 특징)

3. value 값은 중복을 허용한다. (List의 특징)

 

 

 

  • 사용법
사용법
put(key값, value값) 데이터 추가, 수정
remove(key값) 데이터 삭제
get(key값) 데이처 가져오기

 

 

package kr.or.ddit.basic;

import java.util.HashMap;
import java.util.Map;

public class T08HashMapTest {
	public static void main(String[] args) {
		
		Map<String, String> map = new HashMap<String, String>();
		
		// 데이터 추가하기 : put(key값, value값)
		map.put("name", "홍길동");
		map.put("addr", "대전");
		map.put("tel", "010-1234-5678");
		
		System.out.println("map => " + map);
		
		
		// 데이터 수정하기 : put(key값, value값)
		// => 데이터를 저장할 때 key값이 같으면 나중에 입력한 값이 저장된다.
		map.put("addr", "서울");
		System.out.println("map => " + map);
		
		
		// 데이터 삭제하기 : remove(key값)
		map.remove("name");
		System.out.println("map => " + map);
		
		
		// 데이터 읽어오기 : get(key값)
		System.out.println("addr => " + map.get("addr"));
		System.out.println("============================");
	}
}

 

결과 화면6

 

 

  • key값들을 읽어와 데이터를 가져오는 3가지 방법

1. keySet() 메서드 이용

package kr.or.ddit.basic;

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

public class T08HashMapTest {
	public static void main(String[] args) {
		
		Map<String, String> map = new HashMap<String, String>();
		
		// 데이터 추가하기 : put(key값, value값)
		map.put("name", "홍길동");
		map.put("addr", "대전");
		map.put("tel", "010-1234-5678");
		
		// key값들을 읽어와 데이터를 가져오는 방법
		
		// 방법1 => keySet() 메서드 이용하기
		Set<String> keySet = map.keySet();
		
		System.out.println("Iterator를 이용하는 방법");
		
		Iterator<String> it = keySet.iterator();
		while(it.hasNext()) {
			String key = it.next();
			
			System.out.println(key + " : " + map.get(key));
		}
		System.out.println("------------------------------");
	}
}

 

결과 화면7

 


2. for문(forEach문) 이용

package kr.or.ddit.basic;

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

public class T08HashMapTest {
	public static void main(String[] args) {
		
		Map<String, String> map = new HashMap<String, String>();
		
		// 데이터 추가하기 : put(key값, value값)
		map.put("name", "홍길동");
		map.put("addr", "대전");
		map.put("tel", "010-1234-5678");
		Set<String> keySet = map.keySet();
		
		// 방법2 => Set형의 데이터를 '향상된 for문(forEach문)'으로 처리하면 Iterator를 사용하지 않아도 된다.
		System.out.println("향상된 for문(forEach문)을 이용한 방법");
		for (String key : keySet) {
			System.out.println(key + " : " + map.get(key));
		}
		System.out.println("------------------------------");
	}
}

 

결과 화면8

 


3. values()메서드 이용

package kr.or.ddit.basic;

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

public class T08HashMapTest {
	public static void main(String[] args) {
		
		Map<String, String> map = new HashMap<String, String>();
		
		// 데이터 추가하기 : put(key값, value값)
		map.put("name", "홍길동");
		map.put("addr", "대전");
		map.put("tel", "010-1234-5678");
		
		// 방법3 => value값만 읽어와 가져오기
		System.out.println("values()메서드 이용한 방법");
		for (String value : map.values()) {
			System.out.println(value);
		}
		System.out.println("------------------------------");
	}
}

 

결과 화면9

 


4. entrySet()을 이용

package kr.or.ddit.basic;

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

public class T08HashMapTest {
	public static void main(String[] args) {
		
		Map<String, String> map = new HashMap<String, String>();
		
		// 데이터 추가하기 : put(key값, value값)
		map.put("name", "홍길동");
		map.put("addr", "대전");
		map.put("tel", "010-1234-5678");
		
		// 방법4 => Entry객체를 가져와 처리하는 방법
		System.out.println("entrySet()을 이용한 방법");
		Set<Map.Entry<String, String>> entrySet = map.entrySet();
		
		Iterator<Map.Entry<String, String>> entryIt = entrySet.iterator();
		
		while (entryIt.hasNext()) {
			Map.Entry<String, String> entry = entryIt.next();
			System.out.println("key값 : " + entry.getKey());
			System.out.println("value값 : " + entry.getValue());
			System.out.println();
		}
	}
}

 

결과 화면10

 

 

 

  • 전화번호 관리 프로그램 예시
package kr.or.ddit.basic;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;

/*
문제) 이름, 주소, 전화번호 속성을 갖는 Phone클래스를 만들고, 이 Phone클래스를 이용하여 
	  전화번호 정보를 관리하는 프로그램을 완성하시오.
	  이 프로그램에는 전화번호를 등록, 수정, 삭제, 검색, 전체출력하는 기능이 있다.
	  
	  전체의 전화번호 정보는 Map을 이용하여 관리한다.
	  (key는 '이름'으로 하고 value는 'Phone클래스의 인스턴스'로 한다.)


실행예시)
===============================================
   전화번호 관리 프로그램(파일로 저장되지 않음)
===============================================

  메뉴를 선택하세요.
  1. 전화번호 등록
  2. 전화번호 수정
  3. 전화번호 삭제
  4. 전화번호 검색
  5. 전화번호 전체 출력
  0. 프로그램 종료
  번호입력 >> 1  <-- 직접 입력
  
  새롭게 등록할 전화번호 정보를 입력하세요.
  이름 >> 홍길동  <-- 직접 입력
  전화번호 >> 010-1234-5678  <-- 직접 입력
  주소 >> 대전시 중구 대흥동 111  <-- 직접 입력
  
  메뉴를 선택하세요.
  1. 전화번호 등록
  2. 전화번호 수정
  3. 전화번호 삭제
  4. 전화번호 검색
  5. 전화번호 전체 출력
  0. 프로그램 종료
  번호입력 >> 5  <-- 직접 입력
  
  =======================================
  번호   이름       전화번호         주소
  =======================================
   1    홍길동   010-1234-5678    대전시
   ~~~~~
   
  =======================================
  출력완료...
  
  메뉴를 선택하세요.
  1. 전화번호 등록
  2. 전화번호 수정
  3. 전화번호 삭제
  4. 전화번호 검색
  5. 전화번호 전체 출력
  0. 프로그램 종료
  번호입력 >> 0  <-- 직접 입력
  
  프로그램을 종료합니다...
  
*/
public class T09PhoneBookTest {
	private Scanner scan;
	private Map<String, PhoneVo> phoneBookMap;
	
	public T09PhoneBookTest() {
		scan = new Scanner(System.in);
		phoneBookMap = new HashMap<String, PhoneVo>();
	}
	
	// 메뉴를 출력하는 메서드
	public void displayMenu(){
		System.out.println();
		System.out.println("메뉴를 선택하세요.");
		System.out.println(" 1. 전화번호 등록");
		System.out.println(" 2. 전화번호 수정");
		System.out.println(" 3. 전화번호 삭제");
		System.out.println(" 4. 전화번호 검색");
		System.out.println(" 5. 전화번호 전체 출력");
		System.out.println(" 0. 프로그램 종료");
		System.out.print(" 번호입력 >> ");		
	}
	
	// 프로그램을 시작하는 메서드
	public void phoneBookStart(){
		System.out.println("===============================================");
		System.out.println("   전화번호 관리 프로그램(파일로 저장되지 않음)");
		System.out.println("===============================================");
		
		while(true){
			
			displayMenu();  // 메뉴 출력
			
			int menuNum = scan.nextInt();   // 메뉴 번호 입력
			
			switch(menuNum){
				case 1 : insert();		// 등록
					break;
				case 2 : update();		// 수정
					break;
				case 3 : delete();		// 삭제
					break;
				case 4 : search();		// 검색
					break;
				case 5 : displayAll();	// 전체 출력
					break;
				case 0 :
					System.out.println("프로그램을 종료합니다...");
					return;
				default :
					System.out.println("잘못 입력했습니다. 다시입력하세요.");
			} // switch문
		} // while문
	}
	
	/**
	 * 이름을 이용한 전화번호 정보를 검색하기 위한 메서드
	 */
	private void search() {
		System.out.println();
		System.out.println("검색할 전화번호 정보를 입력하세요.");
		System.out.print("이름 >> ");
		String name = scan.next();
		
		PhoneVo p = phoneBookMap.get(name);
		
		if(p == null) {
			System.out.println(name + "씨의 전화번호 정보가 존재하지 않습니다.");
		} else {
			System.out.println("이      름 : " + p.getName());
			System.out.println("전화번호 : " + p.getTel());
			System.out.println("주      소 : " + p.getAddr());
		}
		System.out.println("-----------------------------------");
		System.out.println("검색 작업 완료...");
		
	}

	/**
	 * 전화번호 정보를 삭제하기 위한 메서드
	 */
	private void delete() {
		System.out.println();
		System.out.println("삭제할 전화번호 정보를 입력하세요.");
		System.out.print("이름 >> ");
		String name = scan.next();
		
		// remove(key) => 삭제 성공하면 삭제된 value값을 리턴하고 실패하면 null을 리턴함.
		if(phoneBookMap.remove(name) == null) {
			System.out.println(name + "씨는 등록된 사람이 아닙니다.");
		} else {
			System.out.println(name + "씨 정보를 삭제하였습니다.");
		}
		
	}

	/**
	 * 전체 전화번호 정보를 출력하기 위한 메서드
	 */
	private void displayAll() {
		System.out.println("======================================");
		System.out.println(" 번 호\t이 름\t전화번호\t\t주 소");
		System.out.println("======================================");
		
		Set<String> keySet = phoneBookMap.keySet();
		
		if(keySet.size() == 0) {
			System.out.println("등록된 전화번호 정보가 존재하지 않습니다.");
		} else {
			Iterator<String> it = keySet.iterator();
			int cnt = 0;
			while(it.hasNext()) {
				cnt++;
				String name = it.next();
				PhoneVo p = phoneBookMap.get(name);
				System.out.println(" " + cnt + "\t" + p.getName() + "\t" + p.getTel() 
					+ "\t" + p.getAddr());
			}
			System.out.println("======================================");
			System.out.println("전체 출력 완료...");
		}
	}

	/**
	 * 전화번호 정보를 수정하기 위한 메서드
	 */
	private void update() {
		System.out.println();
		System.out.println("수정할 전화번호 정보를 입력하세요.");
		System.out.print("이름 >> ");
		String name = scan.next();
		
		// 이미 등록된 사람인지 검사
		// get()메서드로 값을 가져올때 해당 데이터가 존재하지 않으면 null을 반환함.
		if(phoneBookMap.get(name) == null) {
			System.out.println(name + "씨는 존재하지 않는 사람입니다.");
			return; // 메서드 종료
		}
		
		System.out.print("전화번호 >> ");
		String tel = scan.next();
		
		System.out.print("주소 >> ");
		scan.nextLine(); // 입력버퍼에 남아있는 엔터키를 읽어오기 위해 (버퍼 비우기용)
		String addr = scan.nextLine();
		
		phoneBookMap.put(name, new PhoneVo(name, tel, addr));
		System.out.println(name + "씨 전화번호 수정 완료... ");
	}

	/**
	 * 새로운 전화번호 정보를 등록하기 위한 메서드
	 */
	private void insert() {
		System.out.println();
		System.out.println("새롭게 등록할 전화번호 정보를 입력하세요.");
		System.out.print("이름 >> ");
		String name = scan.next();
		
		// 이미 등록된 사람인지 검사
		// get()메서드로 값을 가져올때 해당 데이터가 존재하지 않으면 null을 반환함.
		if(phoneBookMap.get(name) != null) {
			System.out.println(name + "씨는 이미 등록된 사람입니다.");
			return; // 메서드 종료
		}
		
		System.out.print("전화번호 >> ");
		String tel = scan.next();
		
		System.out.print("주소 >> ");
		scan.nextLine(); // 입력버퍼에 남아있는 엔터키를 읽어오기 위해 (버퍼 비우기용)
		String addr = scan.nextLine();
		
		phoneBookMap.put(name, new PhoneVo(name, tel, addr));
		System.out.println(name + "씨 전화번호 등록 완료... ");
	}

	public static void main(String[] args) {
		new T09PhoneBookTest().phoneBookStart();
	}
}

class PhoneVo {
	private String name; // 이름
	private String tel; // 전화번호
	private String addr; // 주소
	
	// Alt + Shift + S 
	public PhoneVo(String name, String tel, String addr) {
		super();
		this.name = name;
		this.tel = tel;
		this.addr = addr;
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getTel() {
		return tel;
	}
	public void setTel(String tel) {
		this.tel = tel;
	}
	public String getAddr() {
		return addr;
	}
	public void setAddr(String addr) {
		this.addr = addr;
	}
	
	@Override
	public String toString() {
		return "PhoneVo [name=" + name + ", tel=" + tel + ", addr=" + addr + "]";
	}
}

 

결과 화면11-1

 

결과 화면11-2

 

결과 화면11-3

 

결과 화면11-4

 

결과 화면11-5

 

 

반응형

'자바' 카테고리의 다른 글

[Java 고급] 3.5.2장 Test2  (1) 2024.01.27
[Java 고급] 3.5.1장 Test1  (1) 2024.01.27
[Java 고급] 2장 Hash Set, Tree Set  (1) 2024.01.27
[Java 고급] 1.5장 Test  (0) 2024.01.27
[Java 고급] 1장 Collection Framework와 List  (1) 2024.01.27