자신을 요소로 포함하는 ArrayList의 해시 코드


38

자체를 포함 hashcode하는의 list를 찾을 수 있습니까 element?

나는 이것이 나쁜 습관이라는 것을 알고 있지만 이것은 면접관이 요구 한 것입니다.

다음 코드를 실행하면 다음이 발생합니다 StackOverflowError.

public class Main {
    public static void main(String args[]) {
        ArrayList<ArrayList> a = new ArrayList();
        a.add(a);
        a.hashCode();
    }
}

이제 두 가지 질문이 있습니다.

  1. StackOverflowError있습니까?
  2. 이런 식으로 해시 코드를 찾을 수 있습니까?

7
List 자체를 추가하기 때문입니다. add 문없이 a.hashCode ()를 시도하십시오
Jens

객체를 arraylist에 넣을 때 객체에 대한 참조를 저장하는 것입니다. 귀하의 경우에는 ArrayList 마녀를 넣는 것이 자체입니다.
Vishwa Ratna


좋아, 왜 stackoverflow가 있는지, 어떤 사람이 문제 번호를 설명하는 데 도움이 될 수 있습니까 2- 이것을 찾는 방법
Joker

9
다른 사람들이 대답했듯이 List인터페이스의 정의에 hashCode따라 목록의 구성원에 따라 목록이 달라집니다. 목록이 자체 멤버 인 경우 해시 코드는에 의존하며 hashCode, 그에 따라 hashCode... 등에 의존 하여 무한 재귀를 발생시키고 StackOverflowError사용자는 실행됩니다. 이제 문제는 : 자신을 포함하기 위해 목록이 필요한가요? 나는 이와 같은 재귀 멤버쉽을 요구하지 않고 더 나은 방법으로 당신이하려는 일을 성취 할 수 있음을 보장 할 수 있습니다.
알렉산더-복원 모니카

답변:


36

적합한 List구현 위한 해시 코드 가 인터페이스에 지정되었습니다 .

이리스트의 해시 코드 값을 리턴합니다. 목록의 해시 코드는 다음 계산 결과로 정의됩니다.

 int hashCode = 1;
 for (E e : list)
     hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());

이를 통해 두 목록에 대한 및 의 일반 계약에서 요구하는 사항 을 list1.equals(list2)암시합니다 .list1.hashCode()==list2.hashCode()list1list2Object.hashCode()

이것은 구현이 정확히 그렇게 보일 필요는 없지만 ( 대안의 경우 List.hashCode ()와 같은 방식으로 스트림의 해시 코드를 계산하는 방법 참조 ) 자체를 포함하는 목록의 올바른 해시 코드는 즉, 일치하는 숫자를 계산할 수없는 숫자 x == 31 + x여야합니다 true.


1
@Holger는 Eirc 전체 함수의 코드를 대체하고자 hashCode()반환에 0. 이는 x == 31 + xx가 1 이상이어야한다는 요구 사항을 기술적으로 해결 하지만 무시합니다.
bxk21

4
@EricDuminil 내 대답의 요점은 계약에 ArrayList문자 그대로 구현되어 재귀를 유발 하는 논리를 설명 하지만 일치하는 대체 구현도 없습니다. OP 가이 특정 구현으로 StackOverflowError인해 다른 답변으로 해결 된 이유를 이미 이해했을 때 내 답변을 게시했습니다 . 그래서 나는 한정된 시간 안에 완성 된 준수 구현의 일반적인 불가능성에 초점을 두었습니다.
Holger

2
@pdem 사양이 알고리즘, 수식, 의사 코드 또는 실제 코드에 대한 간단한 설명을 사용하는지 여부는 중요하지 않습니다. 답변에서 말했듯이 사양에 제공된 코드는 대체 구현을 배제하지 않습니다. 사양의 형태는 분석이 발생했는지 여부에 대해 아무 것도 말하지 않습니다. 인터페이스 문서의 문장“ 목록이 자신을 요소로 포함하는 것이 허용되지만 극도의주의가 필요하다. equals 및 hashCode 메소드는 더 이상 그러한 목록에 잘 정의되어 있지 않다 ”고 분석했다.
Holger

2
@ pdem 당신은 그것을 반전하고 있습니다. 해시 코드로 인해 목록이 동일해야한다고 말한 적이 없습니다. 목록 정의상 동일합니다. Arrays.asList("foo")같은지 Collections.singletonList("foo")에이 동일한 List.of("foo")동일하다 new ArrayList<>(List.of("foo")). 이 모든 목록은 동일합니다. 그런 다음이 목록은 동일하므로 해시 코드가 동일해야합니다. 다른 길은 아닙니다. 그것들은 동일한 해시 코드를 가져야하므로 잘 정의되어 있어야합니다. 그들이 어떻게 그것을 정의했는지에 관계없이 (Java 2에서 다시) 모든 구현에서 동의하도록 잘 정의되어야합니다.
Holger

2
@pdem List은 "일반 목록과 혼합하지 마십시오"를 구현하지 않거나 큰 경고 표시가 있는 사용자 정의 구현 IdentityHashMap과 비교 하여 " 이 클래스는 범용 Map 구현 이 아닙니다 ! ”경고. 전자의 경우 이미 Collection목록 스타일 색인 기반 액세스 방법 을 구현 하지만 추가하는 것이 좋습니다. 그런 다음 등식 제약 조건은 없지만 다른 컬렉션 유형과 함께 원활하게 작동합니다.
Holger

23

클래스 에서 hashCode메소드 의 골격 구현을 확인하십시오 AbstractList.

public int hashCode() {
    int hashCode = 1;
    for (E e : this)
        hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
    return hashCode;
}

목록의 각 요소에 대해을 호출합니다 hashCode. 귀하의 경우 목록은 유일한 요소입니다. 이제이 전화는 끝나지 않습니다. 이 메소드는 재귀 적으로 호출되며 재귀는를 만날 때까지 계속 감습니다 StackOverflowError. 따라서이 hashCode방법을 찾을 수 없습니다 .


따라서 해시 코드를 이런 식으로 찾을 수있는 방법이 없습니까?
조커

3
예, 때문에 재귀 조건

뿐만 아니라 이런 식으로 정의됩니다 .
chrylis

14

자체가 포함 된 (병리학 적) 목록을 정의했습니다.

StackOverflowError있습니까?

javadocs (즉, 스펙) 에 따르면 , Lista의 해시 코드는 각 요소의 해시 코드 함수에 정의됩니다. 그것은 말한다 :

"목록의 해시 코드는 다음 계산 결과로 정의됩니다."

int hashCode = 1;
    for (E e : list)
         hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());

의 해시 코드를 계산 a하려면 먼저의 해시 코드를 계산합니다 a. 그것은 무한히 재귀 적이며 스택 오버플로로 빠르게 이어집니다.

이 방법으로 해시 코드를 찾을 수 있습니까?

아닙니다 . 위의 알고리즘 사양을 수학 용어로 고려하면 List자체를 포함 하는의 해시 코드는 계산할 수없는 함수 입니다. 이 방법으로 (위의 알고리즘을 사용하여) 또는 다른 방법으로 는 계산할 수 없습니다 .


나는 왜이 대답이 설명과 함께 OP 질문에 응답하기 때문에 다른 두 가지보다 낮은 지 모르겠습니다.
Neyt

1
@Neyt 일부 사용자는 상단의 답변 만 읽습니다.
Holger

8

아니요, 설명서에 답이 있습니다

List 구조문서는 다음과 같이 명시 적으로 설명되어 있습니다.

참고 :리스트가 자신을 요소로 포함하는 것이 허용되지만 극도의주의가 필요합니다. equals 및 hashCode 메소드는 더 이상 이러한리스트에서 잘 정의되지 않습니다.

Java 사양에 따르면 자신을 포함하는 목록에 대해 hashCode를 계산할 수 없습니다. 다른 답변은 그 이유에 대해 자세히 설명하지만 요점은 그것이 알려지고 의도적이라는 것입니다.


1
왜 그것이 사양을 벗어난 지 알려 주었으므로 버그가 아니라고 설명합니다. 그것은 다른 답변에서 누락 된 부분이었습니다.
pdem

3

Ravindra의 답변은 포인트 1에 대한 좋은 설명입니다. 질문 2 :

이런 식으로 해시 코드를 찾을 수 있습니까?

여기에 원형이 있습니다. 이 스택 오버플로 오류와 관련하여 다음 중 하나 이상이 잘못되어야합니다.

  • 목록의 해시 코드는 요소의 해시 코드를 고려해야합니다
  • 리스트가 자신의 요소가되는 것은 괜찮습니다.

이제 우리는를 다루기 때문에 ArrayList첫 번째 요점이 고정되었습니다. 다시 말해, 재귀 목록의 해시 코드를 의미있게 계산하려면 다른 구현이 필요할 수 있습니다. ArrayList요소의 해시 코드 추가를 확장 하거나 건너 뛸 수 있습니다.

for (E e : this)
  if(e == this) continue; //contrived rules
  hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());

대신 이러한 클래스를 사용 ArrayList하면 가능합니다.

ArrayList두 번째 요점이 잘못되었습니다. 인터뷰자가 "이러한 방식으로 (배열 목록으로) 해시 코드를 찾을 수 있습니까?" 그 말은 터무니없는 대답입니다.


1
해시 코드의 계산은 명령에 따른 것입니다 계약 . 유효한 구현은 스스로 건너 뛸 수 없습니다. 사양에서, 당신은 당신이 찾아내는 경우에 있음을 유도 할 수 있는 숫자 입니다 , 당신은 유효한 짧은 컷 ... 구현할 수 있습니다Listintx == 31 + xtrue
홀거

@Holger의 말을 잘 이해하지 못했습니다. 그러나 해결책에 대한 두 가지 문제가 있습니다. 첫 번째 :이 목록이 자체 항목 인 경우에만 문제를 해결하고 자체 항목 항목이있는 목록 (더 깊은 재귀 계층) 두 번째 : 목록이 자체적으로 건너 뛴 경우 빈 목록과 같을 수 있습니다.
Jonas Michel

1
@JonasMichel 나는 해결책을 제안하지 않았다. 나는 질문 2의 부조리에 대해 철학적으로 토론하고 있습니다. 해당 목록에 대한 해시 코드를 계산 해야하는 경우 배열 목록의 제약 조건을 제거하지 않으면 작동하지 않습니다 (그리고 Holger는 List공식적으로 지시 한다고 말함으로써 그것을 강화 합니다 구현에 의해 준수되는 해시 코드 로직)
ernest_k

내 (제한된) 이해는 List해시 코드 계산 을 제공하지만 재정의 할 수 있다는 것입니다. 유일한 실제 요구 사항은 일반 해시 코드입니다. 객체가 같으면 해시 코드가 같아야합니다. 이 구현은 해당 요구 사항을 따릅니다.
Teepeemm

1
@Teepeemm List은 인터페이스입니다. 계약을 정의하고 구현을 제공 하지 않습니다 . 물론 계약에는 평등과 해시 코드가 모두 포함됩니다. 평등의 일반적인 계약은 대칭이어야한다는 것이므로 하나의 목록 구현을 변경하면 기존의 모든 목록 구현을 변경해야합니다. 그렇지 않으면의 기본 계약을 위반할 수도 java.lang.Object있습니다.
Holger

1

같은 함수에서 같은 함수를 호출하면 결코 끝나지 않는 재귀 조건을 생성하기 때문입니다. 그리고이 작업을 방지하기 위해 JAVA는java.lang.StackOverflowError

다음은 유사한 시나리오를 설명하는 예제 코드입니다.

public class RefTest {

    public static void main(String... strings) {
        RefTest rf = new RefTest();
        rf.message(message("test"));
    }

    public static String message2(String s){
        return message(s);
    }

    public static String message(String s){
        return message2(s);
    }

}   
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.