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 ;
}*/
}
댓글
댓글 쓰기