2020.10.23 고급자바 수업 노트 - SetTest , EqualsHashCodeTest

 equals() 

두 객체의 내용이 같은지 확인하는 Method 입니다.


hashCode() 


두 객체가 같은 객체인지 확인하는 Method 입니다.  



package kr.or.ddit.basic;


import java.util.ArrayList;

import java.util.HashSet;

import java.util.Iterator;

import java.util.Random;


public class SetTest {


public static void main(String[] args) {

       *  - List 와 Set의 차이점 

 *  

 *  1. List : 책꽂이

 *   - 데이터의 순서 (index) 가 있다. 

 *   중복되는 데이터를 저장할 수 있다. 

 *  2. Set : 랜덤주머니 

 *   데이터의 순서(index) 가 없다. 

 *   중복되는 데이터를 저장 할 수 없다

 */

        ** 이부분 설명해 줄 수 있는 시행착오가 있다. (

       random 숫자 뽑기 


        ArrayList<Integer> random = new ArrayList<>(); 

HashSet<Integer> hs = new HashSet<>(); 

        1)

        for ( int i = 0 ; i < 3 ; i++ ) { 

        int num = (int)(Math.random()*9 + 1 ) ; 

            hs.add(num) } 

        

        //hs 라는 HashSet 타입인 hs 에 넣어준다. 

        

        2) 

       while ( hs.size() < 3 ) { 

            hs.add((int)(Math.random()*9 + 1 ) ) ; 

         } 

        

         1) 번과 2)번은 둘다 hs 라는 Set에 넣어주는건데 Set 은 동일한 값을 넣어줄 수 없        다.

        그렇기 때문에 set 의 길이를 확인하지 않고 for 문을 돌려 3번을 실행하는 (1) 번 같이 되면  만약 같은값이 나왔을 때 add로 Set 에 넣어줄 수 없기 때문에 for문 실행한 만큼 set의 길이가 늘어나는 것이 아니라 중복된 값이 나오지 않았을때 set의 길이가 늘어난다. 

그래서 set의 size가 원하는것까지 될 때 까지 라는 while 조건으로 써줘야 원하는 hs 안의 갯수를 얻을 수 있다.

           

즉 Set에 중복되는 데이터를 추가하면 false 를 반환하고 데이터는 추가하지 않는다.  이부분에 해당하는 것 




HashSet hs1 = new HashSet<>(); 

//Set 에 데이터를 추가할 때 add() 메서드를 사용한다. 

//add()메서드의 반환값 : 추가를성공(true) , 추가를 실패 (false) 

hs1.add("DD"); 

hs1.add("AA"); 

hs1.add(2); 

hs1.add("BB"); 

hs1.add("CC");

hs1.add(1); 

hs1.add(3); 

System.out.println("Set 데이터 : " + hs1);

System.out.println("Set의 개수 : " + hs1.size());

//내부적으로 Set은 인덱스가 없어서 넣는순서와 꺼내오는 순서가 달라진다. 

//Set에 중복되는 데이터를 추가하면 false 를 반환하고 데이터는 추가하지 않는다. 

boolean isAdd = hs1.add("FF"); 

System.out.println("중복되지 않을 때 : " + isAdd); true  가 출력 

System.out.println("Set 데이터 : " + hs1);

System.out.println();

isAdd = hs1.add("CC"); 

System.out.println("중복될 때 : " + isAdd); false 가 출력

System.out.println("Set 데이터 : " + hs1);

System.out.println();

//Set의 데이터를 수정하려면 수정하는 명령이 따로 없기 때문에 

// 수정하려는 데이터를 삭제한 후에 새로운 데이터를 추가하는 방법을 사용한다. 

// 삭제하는 메서드 : remove(삭제할 데이터) 

// 반환값 : 삭제를성공(true) , 삭제를실패(false) 

// 전체 자료 삭제 메서드 : clear()

// "FF" 데이터를 "EE"로 변경하기 

hs1.remove("FF"); 

System.out.println("삭제 후 Set 데이터 : " + hs1);

hs1.add("EE"); 

System.out.println(hs1);

/*  hs1.clear(); 

System.out.println("clear 후 데이터 : " + hs1);*/

/*

* Set 의 데이터는 순서(index)가 없기 때문에 List 처럼 index 로 데이터를 하나씩 불러올 수 없다. 

* 그래서 데이터를 하나씩 얻기 위해서는 Iterator 형의 객체로 변환해야 한다.

* -Set 형의 데이터들을 Iterator 형 객체로 변환해 주는 메서드 ==> iterator () 

* Iterator는 출력을 표준화해주는것. 

*/

Iterator it = hs1.iterator(); // Set 데이터를 Iterator 로 변환하기 


//포인터가 존재한다. 1번인덱스 위에 있다. (1번인덱스에 있는게 아니라 ) 

// Iterator 의 hasNext() 메서드 

// ==> Iterator 의 포인터가 가리키는 곳의 다음번째 자리에 데이터가                                         있으면 true , 

// 없으면 false 가 된다. 

// Iterator 의 next() 메서드 

// ==> Iterator 의 포인터를 다음번째 위치로 이동한 후 그곳의 데이                                        터를 반환한다. 

// 처음 포인터는 1번째 인덱스(0번쨰인덱스) 에 있는게 아니라 그 위에 선에 있                       는것임.. * 

while(it.hasNext()) {     //it 은 hs1 을 가지고 Iterator() 의 객체생성을 한것 

System.out.println(it.next());


}


System.out.println("--------------------------------------------------");

//향상된 for문을 사용하면 Iterator 를 사용하지 않고 처리할 수 있다. 

for(Object obj : hs1) { 

System.out.println(obj);

}

System.out.println();

//--------------------------------------------------------------------------

// 번호는 1번부터 25번까지 있고 , 추첨할 인원은 3명이다. 

// 당첨자를 출력하시오. 

HashSet<Integer> testSet = new HashSet<>(); 

while(testSet.size()<3) { 

testSet.add((int)((Math.random()*25) +1 ) ); 

}

                // -> Set의 testSet 에는 3 길이가 되어 있을것

System.out.println("당첨자 번호 : " + testSet);

     

System.out.println();

/*

* 최소값 ~ 최대값 사이의 정수형 난수 만들기 

* (int)(Math.random() * (최대값 - 최소값 ) + 1 + 최소값 )

*/

//Set 유형의 자료를 List 형으로 변환하기 

ArrayList<Integer> testList = new ArrayList<>(testSet); 

System.out.println("List 데이터 출력... ");

for(int i = 0 ; i < testList.size() ; i++) {

System.out.println(testList.get(i));

}

}


}


-----------------------------------------------------------------------------------------------------


package kr.or.ddit.basic;


import java.util.HashSet;


public class EqualsHashCodeTest {


public static void main(String[] args) {

Person p1 = new Person(); 

p1.setId(1);

p1.setName("홍길동"); 

Person p2 = new Person(); 

p2.setId(1); 

p2.setName("홍길동");


Person p3 = p1; 

System.out.println( p1 == p2 );  //false 가 나온다  

System.out.println( p3 == p1 );  //true가 나오고 

//블로그에 call-STACT HEAP 부분 보기 

System.out.println(p1.equals(p2));

// System.out.println( p1 == p2 );  이 의미 

//equals 는 Object 안에 있는 것 . 

//상속을 안하면 자동으로 Object 를 상속하는것 

//person 클래스 는 Object 를 상속받았고 거기들어있는 equals도 쓰임이 같다.

//

//String도 Object 를 상속받았는데  은 equals를 재정의 해놓음 -> 데이터를 가지고 비교하도록 

// 하지만 person 은 아님 그러면 

//person 클래스에 equals 를 재정의 하면 된다. 

HashSet<Person> testSet = new HashSet<>(); 

testSet.add(p1); 

testSet.add(p2); 

System.out.println("set의 크기 : " + testSet.size());

//두개가 같다고 나오게 하려면 해쉬코드도 같게 equals 도 같게 재정의 해줘야한다. 

System.out.println("p1 : " + p1.hashCode());

System.out.println("p2 : " + p2.hashCode());

System.out.println("p3 : " + p3.hashCode());

//실행하면 2개가 나올거다. 

//Set입장에서 봤을때는 값이 같지 않다. 왜냐하면 번지수로 구분하기 때문에 

/* 

*   - equals() 메서드 ==> 두 객체의 내용이 같은지 검사

*   - hashCode() 메서드 ==> 두 객체의 동일성을 검사

*    (hashCode도 Object객체에서 지원을 해줌 ) 

*    

*   - HashSet, Hashtable, HashMap 과 같이 Hash 로 시작하는 컬렉션 객체들은

*     객체의 의미상의 동일성을 비교하기 위해서 hashCode() 메서드를 호출하여 비교한다. 

*     그러므로, 객체가 같은지 여부를 결정하려면 hashCode() 메서드를 재정의 해야 한다.

*     (equals 만 재정의 하면 안되고 ) 

*     

*   - hashCode() 메서드에서 사용하는 '해싱 알고리즘'은 서로 다른 객체들에 대해서 

*    같은 hashcode를 나타낼 수 있다.   

*    

*    평상시에 두개를 같게 검사할때 equals 만 재정의 하는 정도면 되는데 ( 보통 ) 

*    만약 hash 가 들어간 애를 비교해서 같게 만들어주려면 equlas 랑 hashcode 까지 재정의를 해줘야한다.

*    

*/

}


}




class Person{    

private int id; 

private String 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;

}

   // ----- 밑에 주석처리 해준거 처럼.. hashCode 랑 equals 도 재정의해준다.

            //alt shift s 

    ** WHY ? 왜 재정의를 해주냐 ? 에대한 나의 정리

            메서드영역에 Person클래스정보가 저장이 되어 있잖아. 그리고 Person의 p2 p1 등의 객체생성을     

            해주면 Call-Stack 부분에 주소값이 생성이 되고 그 주소값을 따라가는 heap에 p2와 p1이

            그 안에 내용이 저장이 되는데 내가 각각 넣어줬던 id, name 에 내가 넣어준 값                   들이 형식은 Person 클래스로 같지만 그 안에 넣어주는 데이터는 각각 설정해                   준 값에 따라 달라지게 된다. 

            그런데 그 값 두개가 만약 데이터가 같다면 즉 heap 에 저장되어 있는 데이터들이 같다면

            지금 heap 에 저장된 주소값이 다른 상황이잖아. 그렇지만 그럼에도 두개를 같은 애라고 만들어 주기

            위해서 equals 랑 hashCode를 재정의를 해주려고 하는것이야. 일반적으로는 equals로

           비교를 하면 같게 나오지만 Set이런애들은 hashCode가 달라서 동일한 값으로 되지 않아서

            우리가 equals 랑 hasgCode를 재정의 해줌으로서 같게 만들어 주려고 하는것.     

        사실은 Override 로 자동으로 컴퓨터가 해준다...ㅋㅋ

@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())  //getClass() 는 메소드인데, 같은유형의 클래스                                                               인지 비교하는것이다.

return false;


                //밑에는 세부사항 비교해주는 부분

Person other = (Person) obj; ( 비교하려는 애를 Person 타입으로 만들었네..                                                             other 가 비교하기 원하는 새로운 애. ) 

if (id != other.id)

return false;

if (name == null) {

if (other.name != null)

return false;

} else if (!name.equals(other.name))

return false;  //문자열비교 

            //위에 있는거는 비교하기원하는 새로운애가 원래있던애랑 동일하냐 ? 그래서동일            하지 않을 경우를 먼저 전부 써주고 그렇지 않을시에는

            리턴을 아래처럼 true 로 해준다. 이렇게 간단하게 만들어준것. 

return true;

}


/* @Override

public boolean equals(Object obj) {

// TODO Auto-generated method stub

return super.equals(obj);   //원래데이터 // super 는 Object 일테고 ( 부모 )

 

//매개변수는 바꾸면 안되고 안에 내용만 바꿔야 한다. 매개변수는 뭔가 정해져있으면 다른곳에서 저걸 못쓰겠지? 그래서 

//최상위 객체인 Object 로  해놓은것. 

    if(obj == null)

    return false;

    if (this.getClass() != obj.getClass()) //같은 유형의 클래스인지 검사

                     //예시로 들면 Person 클래스같은것. -> 메소드영역에 저장되어있는..

                        걔로 heap 영역에 객체생성해준내용들이 있잖아. 그 Person 같은 유형의

                        클래스인지 검사한다는 것 같다..    

                return false; 

    //getClass() 메소드는 클래스가 같은 Person class인지를 검사하는 것 

    

    if(this == obj) //참조값 (주소값) 이 같은지 검사  ( 주소값은 Call-stack 부분에..) 

    return true; 

    

    

    

    //세부적으로 하나씩 검사하는 부분이다. 

    Person myObj = (Person)obj; //매개변수의 객체(새로운 오른쪽에 나오는애)를 현재             객체 유형으로 형변환 한다.  즉 새로받는 아이 obj 를 Person 과 같은 객체유형으로             만들어줘서 서로 비교가 가능할 수 있도록 한것이다.     

 

           if (this.name == null && myObj.name != null) {

    return false ; 

    }

    //null 인데이터가 왔는지 체크 null이 아니면 진짜 값을 비교(밑에부분) 

    if( this.id == myObj.id && this.name == myObj.name) {

    return true; 

    }   // == 를 사용해서 검사했어 근데 String 타입인데 null 비교하는 거라서 괜찮았                  다. 

        // 만약 id가 숫자고 name 이 null 일때 이게 없으면 equals로 비교까지 안된다. 

    

    if( this.id == myObj.id && this.name.equals(myObj.name) ) {

    return true; 

    }

    

    return false ; 

}*/

}


댓글