Kendisini öğe olarak içeren ArrayList'in karma kodu


38

Biz bulabilir hashcodea listkendisini içerdiğini element?

Bunun kötü bir uygulama olduğunu biliyorum, ama görüşmeci bunu sordu.

Aşağıdaki kodu koştu zaman atar StackOverflowError:

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

Şimdi burada iki sorum var:

  1. Neden bir StackOverflowError?
  2. Karma kodunu bu şekilde bulmak mümkün müdür?

7
Çünkü Listeyi kendisine eklersiniz. add ifadesi olmadan a.hashCode () yöntemini deneyin
Jens

Bir nesneyi bir arraylist'e koyduğunuzda, nesneye başvuruyu saklıyorsunuzdur.
Vishwa Ratna


Tamam, neden stackoverflow olduğunu var, bazıları bana 2 numaralı sorunu açıklamak yardımcı olabilir? Bunu nasıl bulunur
Joker

9
Diğerlerinin cevapladığı gibi, bu, Listarayüzün tanımıyla hashCode, bir listenin üyelerine bağlı olduğu mümkün değildir . Listenin kendi üyesi olduğu göz önüne alındığında, karma kodu hashCode, kendisine bağlıdır, bu da ona bağlıdır hashCode... vb StackOverflowError. Şimdi soru şudur: Neden bir listenin kendisini içermesini istersiniz? Böyle bir özyinelemeye gerek kalmadan daha iyi bir şekilde yapmaya çalıştığınız her şeyi başarabileceğinizi garanti edebilirim.
Alexander - Monica'yı

Yanıtlar:


36

Uygun karma kodu Listuygulamalarda arayüz belirtilen edilmiştir :

Bu liste için karma kod değerini döndürür. Bir listenin hash kodu, aşağıdaki hesaplamanın sonucu olarak tanımlanır:

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

Bu olmasını sağlar list1.equals(list2)ima list1.hashCode()==list2.hashCode()herhangi iki liste için, list1ve list2genel sözleşme gereği, Object.hashCode().

Bu, uygulamanın tam olarak böyle görünmesini gerektirmez ( bir akış için karma kodunu bir alternatif için List.hashCode () ile aynı şekilde hesaplama konusuna bakın ), ancak yalnızca kendisini içeren bir liste için doğru karma kodu olması gereken bir sayı x == 31 + xolması true, diğer bir deyişle, uygun bir sayının hesaplanması imkansızdır.


1
@Holger, Eirc hashCode()geri dönmek için tüm fonksiyonun kodunu değiştirmek istiyor 0. Bu x == 31 + x, x'in en az 1 olması gerektiğini teknik olarak çözer, ancak yok sayar.
bxk21

4
@EricDuminil cevabımın anlamı, sözleşme ArrayListkelimenin tam anlamıyla uygulayan ve bir tekrarlamaya yol açan bir mantığı açıklıyor , ancak uygun bir alternatif uygulama da yok. Cevabımı, OP'nin bu özel uygulamanın neden StackOverflowErrordiğer yanıtlarda ele alınan bir a'ya yol açtığını zaten anladığı bir zamanda yayınladığımı unutmayın . Bu nedenle, uygun bir uygulamanın, sınırlı bir sürede tamamlanmış bir değeri ile genel imkansızlığına odaklandım.
Holger

2
@pdem, belirtimin bir algoritmanın, bir formülün, sahte kodun veya gerçek kodun sözcük tanımını kullanması önemli değildir. Yanıtta belirtildiği gibi, spesifikasyonda verilen kod genel olarak alternatif uygulamaları engellemez. Spesifikasyonun formu, analizin olup olmadığı hakkında bir şey söylemez. Arayüz dokümantasyonunun “ Listelerin kendilerini eleman olarak içermesine izin verilse de, son derece dikkatli olunması tavsiye edilir: equals ve hashCode yöntemleri artık böyle bir listede iyi tanımlanmamıştır ”, böyle bir analizin gerçekleştiğini düşündürmektedir.
Holger

2
@ pdem tersine çeviriyorsunuz. Hash kodu nedeniyle listenin eşit olması gerektiğini hiç söylemedim. Listeleri olan tanım olarak eşit olmasıdır. Arrays.asList("foo")eşittir Collections.singletonList("foo"), eşittir List.of("foo")eşittir new ArrayList<>(List.of("foo")). Tüm bu listeler eşittir, nokta. Daha sonra, bu listeler eşit olduğundan, aynı karma koduna sahip olmaları gerekir. Tersi değil. Aynı karma koduna sahip olmaları gerektiğinden, iyi tanımlanmış olmalıdır. Nasıl tanımladıklarına bakılmaksızın (Java 2'de), tüm uygulamalar tarafından kabul edilmesi için iyi tanımlanması gerekir.
Holger

2
@pdem tam ya, uygulamıyor özel bir uygulama Listya ile karşılaştırmak, “sıradan listeleri ile karışmaz” Büyük bir uyarı işareti vardır IdentityHashMapve onun “ Bu sınıftır değil bir genel amaçlı Harita uygulaması! ”Uyarısı. Önceki durumda, uygulama, Collectionancak liste stili dizin tabanlı erişim yöntemlerini eklemede sorun yoktur . Sonra, eşitlik kısıtı yoktur, ancak yine de diğer koleksiyon türleri ile sorunsuz çalışır.
Holger

23

Sınıfta hashCodeyöntemin iskelet uygulamasını kontrol edin AbstractList.

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

Listedeki her öğe için bu çağrılır hashCode. Vaka listenizde tek unsur olduğu gibi vardır. Şimdi bu çağrı asla bitmez. Yöntem özyinelemeli olarak kendini çağırır ve özyineleme, karşılaşana kadar sarmaya devam eder StackOverflowError. Yani hashCodebu yolu bulamıyorsunuz .


Peki cevap karma kodu bu şekilde bulmanın bir yolu yok mu?
Joker

3
Evet, özyinelemeli durum nedeniyle
springe

Sadece bu değil, bu şekilde tanımlanır .
chrylis -on strike-

14

Kendini içeren (patolojik) bir liste tanımladınız.

Neden var StackOverflowError?

Javadoc'lara (yani spesifikasyona) göre, a'nın hash koduList , öğelerinin her birinin hash kodunun bir fonksiyonu olarak tanımlanır. Diyor ki:

Msgstr "Bir listenin hash kodu aşağıdaki hesaplamanın sonucu olarak tanımlandı:"

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

Böylece, hash kodunu hesaplamak için aönce a. Bu sonsuz bir özyinelemeli ve hızlı bir şekilde yığın taşmasına yol açar.

Karma kodunu bu şekilde bulmak mümkün müdür?

Hayır. Yukarıdaki algoritmik spesifikasyonu matematiksel terimlerle ele alırsanız List, kendisini içeren bir hash kodu hesaplanamayan bir fonksiyondur . Bu şekilde (yukarıdaki algoritmayı kullanarak) veya başka bir şekilde hesaplamak mümkün değildir .


OP sorularına gerçekte açıklamalarla cevap verdiğinden, bu cevabın neden diğerlerinden daha düşük olduğunu bilmiyorum.
Neyt

1
@Neyt bazı kullanıcılar cevapları sadece en üstte okuyor.
Holger

8

Hayır, belgelerin bir cevabı var

Liste yapısının dokümantasyonu açıkça belirtmektedir:

Not: Listelerin kendilerini öğe olarak içermesine izin verilse de, son derece dikkatli olunması önerilir: equals ve hashCode yöntemleri artık böyle bir listede iyi tanımlanmamıştır.

Bunun ötesinde söylenecek çok şey yok - Java spesifikasyonuna göre, kendisini içeren bir liste için hashCode'u hesaplayamazsınız; diğer cevaplar neden bu kadar detaylandırılır, ama asıl mesele bilindiği ve kasıtlı olmasıdır.


1
Neden spesifikasyonun dışında olduğunu söylediniz, bu yüzden bir hata olmadığını açıklıyor. Diğer cevaplardan eksik olan kısım buydu.
pdem

3

Ravindra'nın cevabı 1. madde için iyi bir açıklama verir. Soru 2'ye yorum yapmak için:

Karma kodunu bu şekilde bulmak mümkün müdür?

Burada bir şey dairesel. Bu yığın taşması hatası bağlamında, bu 2 koddan en az biri yanlış olmalıdır:

  • listenin karma kodunun elemanlarının kodlarını dikkate alması gerekir
  • bir listenin kendi öğesi olması sorun değil

Şimdi, bir ile uğraştığımız için ArrayList, ilk nokta sabittir. Başka bir deyişle, belki anlamlı bir özyinelemeli listesinin bir karma kodunu hesaplamak mümkün farklı bir uygulama lazım ... Bir genişletmek olabilir ArrayList, gibi bir şey ve elementlerin karma kodları eklemeyi atlamak

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

Bunun yerine böyle bir sınıf kullanarak ArrayList.

İle ArrayList, ikinci nokta yanlış. Yani görüşmeci "Karma kodu bu şekilde bulmak mümkün mü (dizi listesiyle)?" , o zaman cevap hayır, çünkü bu saçma.


1
Hash kodu hesaplama tarafından şart koşulmaktadır sözleşme . Geçerli bir uygulama kendini atlayamaz. Şartname itibaren, bir bulursan o türetebileceği hangi numarayı ise , o zaman geçerli bir kısa kesilmiş ... uygulayabilirsinizListintx == 31 + xtrue
Holger

@Holger'ın ne dediğini tam olarak anlamadım. Ancak çözümle ilgili 2 Sorun var: Birincisi: Bu, sorunu yalnızca bu liste bir öğe olduğunda çözer, bir öğenin kendisinin bir öğesinin (daha özyineleme katmanları) olduğu listede değil İkinci: Liste kendisini atladıysa boş bir listeye eşit olabilir.
Jonas Michel

1
@JonasMichel Tam bir çözüm önermedim. Ben sadece felsefi olarak soru 2 saçmalık etrafında tartışıyorum . Böyle bir liste için bir karma kodu hesaplamak gerekir , o zaman biz bir dizi listesinin kısıtlamasını kaldırmak sürece işe yaramaz (ve Holger Listresmi olarak dikte söyleyerek güçlendirir karma kod mantığına uygulamalarla uyulması)
ernest_k

Benim (sınırlı) anlayışım Listbir karma kod hesaplaması sağlar, ancak bunu geçersiz kılabiliriz. Tek gerçek gereklilik genel hashcode'ların gerekliliğidir: nesneler eşitse, hashcode'lar eşit olmalıdır. Bu uygulama bu şartı izler.
Teepeemm

1
@Teepeemm Listbir arayüzdür. Bu bir tanımlar sözleşme onu yok, değil bir uygulama sağlamaktadır. Tabii ki, sözleşme hem eşitliği hem de karma kodunu kapsamaktadır. Eşitliğin genel sözleşmesi simetrik olması gerektiğinden, bir liste uygulamasını değiştirirseniz, mevcut tüm liste uygulamalarını değiştirmeniz gerekir, aksi takdirde temel sözleşmesini bile bozarsınız java.lang.Object.
Holger

1

Çünkü aynı işlevi aynı işlevle çağırdığınızda, hiç bitmeyen özyineleme koşulu yaratacaktır. Ve bu işlemi önlemek için JAVA geri dönecekjava.lang.StackOverflowError

Aşağıda benzer senaryoyu açıklayan örnek kod verilmiştir:

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);
    }

}   
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.