공공부하자개발 · 영어 학습 노트
자바
중급객체지향과 코어 라이브러리0/9 완료
  • 01상속, 다형성, 오버라이딩
  • 02추상 클래스 vs 인터페이스
  • 03예외 처리
  • 04java.lang 심화
  • 05컬렉션 프레임워크 딥다이브
  • 06메서드 활용 패턴 (중급)
  • 07Object 메서드와 비교
  • 08java.time 실무 날짜 계산
  • 09HTTP 와 JSON 기초
사이트 소개개인정보처리방침연락처
© 2026 공부하자
홈 › 중급 › 07 / 9

Object 메서드와 비교

equals, hashCode, toString, Comparable
섹션 7진행 0 / 9
1왜 배우는가2핵심 원리3코드 예제4응용 변형 예제5자주 하는 실수 (Tip)6연습 문제7정리‹ 이전다음 ›

3. 코드 예제

소스: java-src/intermediate/07_object_methods (Member, Point, Order, Main).

bash
cd java-src/intermediate/07_object_methods
javac -encoding UTF-8 *.java && java -Dstdout.encoding=UTF-8 Main

예제 1: equals 가 없으면 컬렉션이 "같은 것" 을 모른다

java
static class Plain { final long id; Plain(long id) { this.id = id; } }   // equals 정의 없음

Plain a = new Plain(1), b = new Plain(1);
System.out.println("a == b: " + (a == b) + "  a.equals(b): " + a.equals(b));
System.out.println("list.contains(new Plain(1)): " + List.of(a).contains(new Plain(1)));
System.out.println("HashSet 크기: " + new HashSet<>(List.of(a, b)).size());
text
a == b: false  a.equals(b): false  ← Object.equals 는 == 와 같다
list.contains(new Plain(1)): false  ← contains 는 equals 로 찾는다
HashSet 크기: 2  ← 중복 제거가 안 된다

예제 2: equals 만 있고 hashCode 가 없으면 해시 컬렉션만 깨진다

java
Point p = new Point(1, 2), q = new Point(1, 2);              // Point 는 equals 만 정의
Set<Point> set = new HashSet<>(); set.add(p); set.add(q);
Map<Point, String> map = new HashMap<>(); map.put(p, "A");
System.out.println("map.get(new Point(1,2)): " + map.get(new Point(1, 2)));
text
p.equals(q): true  p.hashCode()==q.hashCode(): false
HashSet 크기: 2  set.contains(new Point(1,2)): false
map.get(new Point(1,2)): null  ← 해시가 다르니 다른 버킷을 뒤진다
List.contains 는 여전히 동작: true  (해시를 안 쓰니까)

equals 는 true 인데 HashSet 에 둘 다 들어가고 map.get 은 null 입니다. 마지막 줄이 이 버그가 늦게 발견되는 이유입니다. List 로 테스트하면 통과합니다.

예제 3: 둘을 함께 정의한 Member 와 record Order

java
Member m1 = new Member(7, "김철수", "kim@a.com"), m2 = new Member(7, "김철수(개명)", "kim@b.com");
Set<Member> set = new HashSet<>(List.of(m1, m2));
Map<Member, Integer> points = new HashMap<>(); points.put(m1, 100); points.merge(m2, 50, Integer::sum);

Order o1 = new Order(1, "kim", new BigDecimal("1000"), LocalDate.of(2026, 9, 9));
Order o3 = new Order(1, "kim", new BigDecimal("1000.0"), LocalDate.of(2026, 9, 9));
text
m1.equals(m2): true  (id 만 같으면 같은 회원)  sameProfile: false
HashSet 크기: 1  contains(new Member(7,...)): true
포인트 합산: {Member{id=7, name=김철수, email=kim@a.com}=150}
m1.equals(null): false  m1.equals("7"): false
record 자동 equals: o1.equals(o2) = true, o1.equals(o3) = false  ← BigDecimal 1000 vs 1000.0 은 equals 가 false
record 자동 toString: Order[no=1, customer=kim, amount=1000, date=2026-09-09]

id 가 같은 두 Member 가 하나로 합쳐져 포인트가 150 이 됐습니다. merge 가 m2 를 키로 넣었는데 m1 과 같다고 판단해 더한 것입니다. record 는 한 줄로 같은 일을 하지만, BigDecimal 스케일 차이까지 다르게 보는 점은 주의해야 합니다.

예제 4: toString

text
기본 toString: Main$Plain@446cdf90  ← 클래스명@해시(16진수). 로그에 찍히면 아무 정보가 없다
오버라이드: Member{id=3, name=이영희, email=lee@a.com}
문자열 결합·printf·로그 프레임워크·디버거 모두 toString 을 부른다: [Member{id=3, name=이영희, email=lee@a.com}]

Main$Plain 의 $ 는 내부 클래스라는 뜻이고, @446cdf90 은 기본 hashCode 의 16진수입니다. 실행할 때마다 달라집니다.

예제 5: 자연 순서와 Comparator 조합, TreeSet 의 중복 판단

java
orders.sort(null);                                                                   // Comparable: 날짜 → 번호
orders.sort(Comparator.comparing(Order::amount).reversed().thenComparing(Order::no));
orders.sort(Comparator.comparing(Order::customer, Comparator.nullsLast(Comparator.naturalOrder())));
TreeSet<Order> byAmount = new TreeSet<>(Comparator.comparing(Order::amount)); byAmount.addAll(orders);
text
자연 순서(날짜→번호): [4, 3, 1, 2]
금액 내림차순, 같으면 번호: [1:12000, 2:5000, 3:5000, 4:700]
고객명 오름차순, null 은 마지막: [kim, lee, park, null]
금액 기준 TreeSet 크기: 3 (주문 4건 중 5000원 두 건이 '같다' 고 판단되어 하나 탈락)

nullsLast 가 없었다면 customer 가 null 인 주문 4 에서 NullPointerException 이 납니다. TreeSet 이 4건 중 3건만 담은 것이 2.8 의 함정입니다.

예제 6: 키 변경, BigDecimal, compareTo 넘침

text
이름 변경 후 contains: true  (id 기준 해시라 안전. 해시에 쓴 필드를 바꿨다면 false 가 된다)
hashCode 없는 Point: contains(자기 자신)=true, contains(같은 값)=false
BigDecimal 1.0 vs 1.00 → equals false, compareTo 0  ← 금액 비교는 compareTo==0 으로
compareTo 를 뺄셈으로 만들면: -2147483639 (양수여야 하는데 음수 → 정렬이 뒤집힌다). Integer.compare = 1

Integer.MAX_VALUE - (-10) 은 수학적으로 양수지만 int 범위를 넘어 음수가 됩니다. 정렬이 "가끔" 틀리는 버그의 전형입니다.

예제 직접 실행

아래 폴더를 JDK 21 로 컴파일하고 실행합니다.

cd java-src\intermediate\07_object_methods
javac -encoding UTF-8 *.java && java Main
코드 예제
  • 예제 1: equals 가 없으면 컬렉션이 "같은 것" 을 모른다
  • 예제 2: equals 만 있고 hashCode 가 없으면 해시 컬렉션만 깨진다
  • 예제 3: 둘을 함께 정의한 Member 와 record Order
  • 예제 4: toString
  • 예제 5: 자연 순서와 Comparator 조합, TreeSet 의 중복 판단
  • 예제 6: 키 변경, BigDecimal, compareTo 넘침
이전 섹션2 핵심 원리3 / 7다음 섹션4 응용 변형 예제