quilt code

[고급자바] 컬렉션 프레임워크 (3) 본문

daily/고급자바

[고급자바] 컬렉션 프레임워크 (3)

김뱅쇼 2023. 2. 1. 21:16

1. EqualsHashCodeTest


해시함수(hash function)임의의 길이의 데이터를 고정된 길이의 데이터로 매핑하는 함수이다.
해시함수에 의해 얻어지는 해시값은 해시코드, 해시 체크섬 또는 간단하게 해시라고 부른다.


HashSet, HashTable, HashMap과 같은 객체들을 사용할 경우
객체가 서로 같은지를 비교하기 위해 equals()메소드hashCode()메소드를 이용한다.
그래서 객체가 서로 동일한지 결정하려면 두 메소드를 재정의해야 한다.
HashSet, HashTable, HashMap에서는 객체 동일 여부를 데이터 추가 시에 검사한다.


equals() 메소드두 객체의 내용(값)이 같은지 비교하는 메소드이고,
hashCode() 메소드객체에 대한 해시코드값을 반환하는 메소드이다.
   => 해시테이블 작성시 사용됨.


equals()와 hashCode() 메소드에 관련된 규칙

 1. 두 객체가 같으면 반드시 같은 hashCode를 가져야 한다.
 2. 두 객체가 같으면 equals()메소드를 호출했을 때 true값을 반환해야한다. 
     즉, 객체 a, b가 같다면 a.equals(b)와 b.equals(a) 둘다 true값을 반환해야 한다.
 3. 두 객체의 hashCode가 같다고 해서 두 객체가 반드시 같은 객체는 아니다.
     하지만, 두 객체가 같으면 반드시 hashCode가 같아야 한다.
 4. equals() 메소드를 override하려면 반드시 hashCode() 메소드도 override 해야 한다.
 5. hashCode()는 기본적으로 힙메모리에 존재하는 각 객체에 대한 메모리 주소값을 기반으로한 정수값을 반환한다.
    그러므로, hashCode()를 재정의하지 않고서는 절대로 두 객체가 같은 해시코드가 반환될 수 없다.


hashCode()에서 사용하는 '해싱 알고리즘'에서 서로다른 객체에 대하여 같은 hashCode를 만들어낼 수 있다.
그래서 객체가 같지 않더라도 hashCode가 같을 수 있다.


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.equals(p2) : " + p1.equals(p2));
System.out.println("p1 == p2 : " + (p1 == p2));

Set<Person> hs = new HashSet<>(); //다이아몬드 문법 java8부터 사용가능 <Person>생략

System.out.println("add(p1) 성공여부 : " + hs.add(p1));
System.out.println("add(p2) 성공여부 : " + hs.add(p2));

System.out.println("p1, p2 등록 후 데이터");
for(Person p : hs) {
System.out.println(p.getId() + " : " + p.getName());
}

System.out.println("add(p3) 성공여부 : " + hs.add(p3));

System.out.println("p3 등록 후 데이터");
for(Person p : hs) {
System.out.println(p.getId() + " : " + p.getName());
}

System.out.println("remove(p2) 성공여부 : " + hs.remove(p2));

System.out.println("remove(p2) 후 데이터");
for(Person p : hs) {
System.out.println(p.getId() + " : " + p.getName());
}
}

p1.equals(p2) : true

p1 == p2 : false
add(p1) 성공여부 : true
add(p2) 성공여부 : false
p1, p2 등록 후 데이터
1 : 정재현
add(p3) 성공여부 : true
p3 등록 후 데이터
1 : 홍길동
1 : 정재현
remove(p2) 성공여부 : true
remove(p2) 후 데이터
1 : 홍길동

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() {
return Objects.hash(id, name);
}
@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;
return id == other.id && Objects.equals(name, other.name);
}

 

 

2. HashMap


Map
  1. key값과 value값을 한쌍으로 관리하는 객체
  2. key값은 중복을 허용하지 않고 순서가 없다. (Set의 특징)
  3. value값은 중복을 허용한다.

 
public static void main(String[] args) {

Map<String, String> map = new HashMap<>();

자료 추가 : put(key값, value값);


map
.put("name", "정재현");

map.put("addr", "대전");
map.put("tel", "010-1234-5678");

System.out.println("map => " + map);

map => {name=정재현, tel=010-1234-5678, addr=대전}
 

자료 수정 : 데이터를 저장할 때 key값이 같으면 나중에 입력한 값이 저장된다.
    put(수정할 key값, 새로운 value값);


map.put("addr", "서울");
System.out.println("map => " + map);

map => {name=정재현, tel=010-1234-5678, addr=서울}

자료 삭제: remove(삭제할 값);


map.remove("name");
System.out.println("map => " + map);

map => {tel=010-1234-5678, addr=서울}

자료 읽기 : get(key값);


System.out.println("addr = " + map.get("addr"));

addr = 서울

**key값들을 읽어와 자료를 출력하는 방법**

방법1. keySet() 메소드 이용하기 Map의 key값들을 Set타입의 객체를 반환한다.

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));
}

Iterator를 이용한 방법
tel : 010-1234-5678
addr : 서울
방법2. Set형의 데이터를 '향상된 for문'으로 처리하면 Iterator를 사용하지 않아도 된다.

System.out.println("향상된 for문을 이용한 방법");
for(String key : keySet) {
System.out.println(key + " : " + map.get(key));
}

향상된 for문을 이용한 방법
tel : 010-1234-5678
addr : 서울
방법3. value값만 읽어와 출력하기

System.out.println("values()메소드 이용한 방법");
for(String value : map.values()) {
System.out.println(value);
}

values()메소드 이용한 방법
010-1234-5678
서울
방법4. Map관련 클래스는 Map.Entry타입의 내부 class가 만들어져 있다. 
           이 내부 클래스는 key와 value라는 멤버변수로 구성되어 있다.
           Map에서 이 Map.Entry 타입의 객체들을 Set 타입의 데이터로 저장하여 관리한다
   Map.Entry타입의 객체 모두 가져오기 => entrySet() 메소드 이용함.

Set<Map.Entry<String,String>> entrySet = map.entrySet();
 
가져온 Entry 객체들을 순서대로 처리하기 위해 Iterator 객체 사용하기

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();
}

key값 : tel
value값 : 010-1234-5678
key값 : addr
value값 : 서울