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 + "]";
}
}
: Set 인터페이스에서 지원하는 구현 클래스로 비선형 구조이기에 순서와 인덱스가 존재하지 않는다.
- 특징
1. 중복된 값을 포함하지 않는다.
2. 인덱스 개념이 존재하지 않는다. => 수정 시 삭제 후 추가하여 진행해야 한다.
3. 데이터의 순서를 보장하지 않는다.
사용법
사용법
뜻
add()
데이터 추가
Iterator 이름 = Set이름.iterator()
데이터 접근을 위한 객체 (get이 존재하지 않음)
hasNext()
Iterator의 다음 데이터가 있는지 확인
next()
Iterator의 다음 데이터 가져오기
remove()
데이터 삭제
clear()
전체 데이터 삭제
size()
데이터 개수 확인
package kr.or.ddit.basic;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
public class T05HashSetTest {
public static void main(String[] args) {
Set hs1 = new HashSet();
// Set 에 데이터를 추가할 때에도 add() 사용한다.
hs1.add("DD");
hs1.add("AA");
hs1.add(2); // int 값이 integer 값으로 들어감 => 기본 타입의 데이터가 객체 타입으로 들어감. (래퍼 클래스)
hs1.add("CC");
hs1.add("BB");
hs1.add(1); // 모든 걸 다 객체화 시킴 => 변환이나 이용이 편리함
hs1.add(3);
System.out.println("Set 데이터 : " + hs1);
// Set은 데이터 중복을 허용하지 않는다.
// 그래서 이미 존재하는 데이터를 또다시 추가하면
// false를 반환하고, 데이터는 추가되지 않는다.
boolean isAdded = hs1.add("FF");
System.out.println("중복 되지 않을 때 : " + isAdded);
System.out.println("Set 데이터 : " + hs1);
System.out.println();
isAdded = hs1.add("CC");
System.out.println("중복 될 때 : " + isAdded);
System.out.println("Set 데이터 : " + hs1);
System.out.println();
// Set의 데이터를 수정하려면 수정하는 메서드가 따로 존재하지 않기 때문에
// 해당 데이터를 삭제 후 새로운 데이터를 추가해 주면 된다.
// 데이터 삭제하기
// 1) clear() => 모든 데이터 삭제
// 2) remove(삭제할 데이터) => 해당 데이터 삭제
// 'FF'를 'EE'로 수정하기
hs1.remove("FF"); // FF 삭제
System.out.println("FF 삭제 후 Set 데이터 : " + hs1);
System.out.println();
hs1.add("EE");
System.out.println("EE 추가 수 Set 데이터 : " + hs1);
System.out.println();
// hs1.clear(); // 전체 데이터 삭제
// System.out.println("clear() 후 Set 데이터 : " + hs1);
System.out.println("Set의 데이터 개수 : " + hs1.size());
System.out.println();
// Set은 인덱스 개념이 존재하지 않기 때문에 List처럼 인덱스로 데이터를 하나씩 불러올 수 없다.
// 그래서 데이터를 접근하기 위해서 Iterator 객체를 사용해야 한다.
Iterator it = hs1.iterator(); // get이 없기에 사용
// 데이터 개수만큼 반복하기
// 데이터의 순서를 보장하지 않음
while(it.hasNext()) { // 다음 데이터가 있는지 확인
System.out.println(it.next()); // 다음 데이터 가져오기
}
}
}
Collection 유형의 객체들은 서로 다른 자료 구조로 쉽게 변경해서 사용할 수 있다. 다른 유형의 객체를 생성할 때 생성자에 해당 데이터를 넣어주면 된다.
package kr.or.ddit.basic;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
public class T05HashSetTest {
public static void main(String[] args) {
// 1 ~ 100 사이의 중복되지 않는 정수 5개 만들기
Set<Integer> intRnd = new HashSet<Integer>();
while (intRnd.size() < 5) {
int num = (int) (Math.random() * 100 + 1);
intRnd.add(num);
}
System.out.println("생성된 난수들 : " + intRnd);
// Collection 유형의 객체들은 서로 다른 자료 구조로 쉽게 변경해서 사용할 수 있다.
// 다른 유형의 객체를 생성할 때 생성자에 해당 데이터를 넣어주면 된다.
List<Integer> intRndList = new ArrayList<Integer>(intRnd);
System.out.println("intRndList 데이터 출력 : ");
for(Integer num : intRndList) {
System.out.print(num + " ");
}
System.out.println();
}
}
Collection 유형의 객체 이용 결과 화면
- Tree Set
: 트리 형태로 관리하는 Set (자료구조)를 말한다.
데이터를 검색할 때 검색 속도가 빠르다는 장점이 존재한다.
그러나 정렬이 되어 있어야 사용할 수 있다. => Tree Set에서 데이터 저장 시 자동정렬을 한다.