JAVA

[자바/JAVA] 프로그래밍 - 컬렉션(Collection)_HashSet

21종 2023. 6. 20. 19:18

Student Class

package com.kh.chap02_set.part01_hashSet.model.vo;

public class Student {

	private String name;
	private int age;
	private int score;
	
	public Student() {
	
	}

	public Student(String name, int age, int score) {
		super();
		this.name = name;
		this.age = age;
		this.score = score;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public int getScore() {
		return score;
	}

	public void setScore(int score) {
		this.score = score;
	}

	@Override
	public String toString() {
		return "Stuent [name=" + name + ", age=" + age + ", score=" + score + "]";
	}

Object equals() => 두 객체의 "주소값"을 비교해서 일치하면 true / 일치하지 않으면 false 반환
Object hashCode() => 해당 객체의 "주소값"을 가지고 10진수 형태로 만들어서 반환

String equals()  => "실제 담긴 문자열"을 가지고 비교해서 일치하면 true / 일치하지 않으면 false 반환
String hashCode() => "실제 담긴 문자열"을 가지고 10진수의 형태롤 만들어서 반환

 

HashSet<String> hs1 = new HashSet<>();

hs1.add("반갑습니다.");		// 0x123
hs1.add(new String("반갑습니다."));	// 0x234
// 1. equals() => true
// 2. hashCode() => true	
// 중복으로 판단
// 오버라이딩돼서 hashCode는 같아짐 System.identityHashCode()는 다름

hs1.add(new String("여러분"));
hs1.add(new String("안녕하세요"));
hs1.add(new String("여러분"));


HashSet<Student> hs2 = new HashSet<>();

// 존잘월드 3명이 산다고 가정하자
hs2.add(new Student("공유", 43, 100));
hs2.add(new Student("차은우", 26, 85));
hs2.add(new Student("주지훈", 24, 20));
hs2.add(new Student("공유", 43, 100));	// 공유가 있는데 실수로 하나를 떠 씀 => hashSet이니까 중복값인 공유는 사라지겠지 ?

System.out.println(hs2);	// 저장 순서 유지 안됨!!(인덱스 개념 없음) / 중복 제거 안됨 => 왜> 동일객체로 판단이 안되고 있어서

첫번째 공유와 네번째 공유와 hashCode 값이 다르다. => 즉 다른 값으로 판단.
해결방법 : Student에서 hashCode를 오버라이딩 한다.

HashSet 이라는 공간에 객체가 추가 될 때마다 동일 객체인지 비교
동일 객체 : 각 객체마다 hashCode() 호출결과가 일치하고, equals() 비교시 true 일 경우

Student equals() 오버라이딩 => "실제 각 필드에 담긴 데이터" 들이 다 일치하면 true / false
Student hashCode() 오버라이딩  => "실제 각 필드에 담긴 데이터" 들이 일치하면 동일한 10진수 반환


정말로 HashCode가 다른지 확인 해보자

System.out.println(new Student("공유",43,100).hashCode());
System.out.println(new Student("공유",43,100).hashCode());
System.out.println(new Student("공유",43,100).equals(new Student("공유",43,100)));


같은 문자열의 해쉬코드 값을 같게 해주기 위해서 Student Class 에서 hashCode 와 equals를 오버라이딩 해야된다,

오버라이딩한 Student Class

package com.kh.chap02_set.part01_hashSet.model.vo;

public class Student {

	private String name;
	private int age;
	private int score;
	
	public Student() {
	
	}

	public Student(String name, int age, int score) {
		super();
		this.name = name;
		this.age = age;
		this.score = score;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public int getScore() {
		return score;
	}

	public void setScore(int score) {
		this.score = score;
	}

	@Override
	public String toString() {
		return "Stuent [name=" + name + ", age=" + age + ", score=" + score + "]";
	}

//		내 마음대로 만든 hashCode(),
	@Override
	public int hashCode() {	// 모든 필드에 담긴 값이 일치하면 동일한 hashCode 반환 하도록
		String str = name + age + score;	// 하나의 문자열 "공유43100", "차은우2685", "주지훈2420"
		return str.hashCode();
	}
		
	
	@Override
	public boolean equals(Object obj) {	// 현재객체와 전달받은 객체의 각 필드값이 모두 일치하면 true, 하나라도 일치하지 않으면 false 반환
		//	this (현객체)		vs 		obj(전달받은 객체)
		// Student 타입				Object타입
		
		Student other = (Student)obj;
		//	this(현객체)		vs		obj(전달받은객체)
		// 	this.name		vs		obj.name		일치하는지
		//	this.age		vs		obj.age			일치하는지
		//	this.score		vs		obj.score		일치하는지
		if(name.equals(other.name) && (age == other.age) && (score == other.score) ) {
			return true;
		}else {
			return false;
		}
		
	}
	
}

 

다시 아래의 코드를 실행해보자

System.out.println(new Student("공유",43,100).hashCode());
System.out.println(new Student("공유",43,100).hashCode());
System.out.println(new Student("공유",43,100).equals(new Student("공유",43,100)));


HashSet<Student> hs2 = new HashSet<>();
hs2.add(new Student("공유", 43, 100));
hs2.get(1);	=> 인덱스의 개념도 없고 get메소드 자체가 정의되어 있지 않음!! (한 객체만 뽑아올 수 없음!!)

 

HashSet에 담긴 모든 객체들에 순차적으로 접근하는 방법

 

1. for문 사용 가능 (단, 향상돤 for문(for each문)만 사용 가능!!)

for(Student s : hs2) {
    System.out.println(s);
}

 

2. ArrayList에 담아준 다음 ArrayList를 반복문 돌려가며 접근

 

    ArrayList에 담는 첫번째 방법 : ArrayList 생성 후 addAll 메소드 이용해서 통째로 추가하기

ArrayList <Student> list = new ArrayList<>();
list.addAll(hs2);

 

    ArrayList에 담는 두번째 방법 : ArrayList 생성과 동시에 통째로 추가하기

ArrayList <Student> list2 = new ArrayList<>(hs2);
for(int i = 0; i < list2.size(); i++) {
    System.out.println(list2.get(i));
}

 

근데 이거 왜 쓰는거임 ? 
중복된 데이터가 들어오면 절대 안되는 경우 !!! => 근데 거의 안쓰임...

 

3. Iterator 반복자를 이용해서 순차적을 접근

Iterator<Student> it = hs2.iterator();	// hs2에 담겨있는 객체들을 Iterator(반복자)에 담는 과정
while(it.hasNext()) {
    Student s = it.next();
    System.out.println(s);
}
it.next();	// NoSuchElementException : 더 이상 뽑을 요소 없음

근데 사실 이클립스에서 equals와 hashCode를 자동으로 오버라이딩 할 수 있는 도구가 있다,,,

 

Student Class의 코드창에서 ALT+SHIFT+S

 

Generate

@Override
public int hashCode() {
    return Objects.hash(age, name, score);
}

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    Student other = (Student) obj;
    return age == other.age && Objects.equals(name, other.name) && score == other.score;

}