Jak nie usunę tej lombokomi to chce lobotomii.

Jak nie usunę tej lombokomi to chce lobotomii.

Wątek przeniesiony 2020-12-11 15:16 z Inżynieria oprogramowania przez cerrato.

S9
  • Rejestracja:ponad 4 lata
  • Ostatnio:około 2 lata
  • Lokalizacja:Warszawa
  • Postów:1092
0

@jarekr000000: no generalnie jedyna sensowna rzecz to używanie niemutowalnych obiektów jako klucze w HashMapach i wartości w HashSetach.
Przecież nawet jak hashcode będzie ten sam to pojawia się problem z equals...


AF
  • Rejestracja:prawie 18 lat
  • Ostatnio:12 dni
2
jarekr000000 napisał(a):

Dawno, dawno temu (jak byłem architekte) preferowałem idealny hashCode:
public int hashCode () {return 1;}, bo
taki jest zgodny ze specyfikacją zawsze, pięknie działa w aplikacjach biznesowych, gdzie rzadko mamy hahsMapy większe od 10 elementów, no i w razie czego będzie wiadomo kiedy trzeba zmienić, bo system zacznie klękać.

Przestałem tak robić, bo ludzie jednak nie potrafią zrozumieć, komentować mi się nie chce, a osoby przejmujące system przeważnie nawet nie umieją obsłużyć ani monitoringu ani profilera.

Obecnie skłaniam się ku o wiele lepszemu rozwiązaniu:
public int hashCode () {throw new UnsupportedOperationException();}

Czasami mam wrażenie, że javowcy są jak komuniści, bohatersko walczą z problemami, które sami sobie zrobili. Jakbym zobaczył rzucanie wyjątku w liczeniu hasha, to pewnie bym od razu zmieniał firmę.

Zobacz pozostałe 19 komentarzy
jarekr000000
@Afish: nie ma co tutaj odwoływać się do poglądów. Zrób sobie mutowalną klasę np. Point - z dwoma parametrami xi y - niech komponenty będą intami. Napisz, wygeneruj Wrzuć kilka do HashMapy (jaki klucze) lub HashSet, a potem pomutuj (przechowane gdzieś na boku obiekty). Zobacz jak pięknie. Ale tu akurat naprawianie hashCode, czy jakies inne OOP niewiele by IMO pomogło (ale nie wiem).
jarekr000000
Dlatego klucze raczej powinny być niemutowalne. Albo odrowotnie - obiekty mutowalne powinno się zabezpieczać przed wpadaniem do tablic hashujących. Stąd koncept (niesprawdzony) wyjątku.
AF
@jarekr000000: Jeżeli mieszasz mutowalność z hashowaniem, to właśnie w takie problemy wpadasz, ale to są rozłączne kwestie. Obiekt to dane, zachowanie i tożsamość, jeżeli ignorujesz tożsamość, to nie masz obiektu według paradygmatu OOP, tylko co najwyżej wartość, ale wtedy nic dziwnego, że pojawiają się trudności.
jarekr000000
@Afish: git. chyba w końcu wytłumaczyłem Ci i @WeiXiao dlaczego nie wolno mieszać mutowalności z hashowaniem - i skąd pomysł na to, żeby pewnych obiektów nie dało się stosować w kluczach. Reszty Twoich pouczeń na temat OOP nie rozumiem. Nie wiem co mają do rzeczy w tym miejscu. Jakbym był kobietą to by był klasyczny mansplaining.
AF
@jarekr000000: chyba w końcu wytłumaczyłem Ci :D Dzięki, co ja bym bez Ciebie zrobił.
S9
  • Rejestracja:ponad 4 lata
  • Ostatnio:około 2 lata
  • Lokalizacja:Warszawa
  • Postów:1092
0

@Afish: a czy z hashem i equalsem w C# nie jest tak samo?


KamilAdam
  • Rejestracja:ponad 6 lat
  • Ostatnio:6 dni
  • Lokalizacja:Silesia/Marki
  • Postów:5505
1
Aleksander32 napisał(a):

@Afish: a czy z hashem i equalsem w C# nie jest tak samo?

Why is it important to override GetHashCode when Equals method is overridden? Czy w internecie piszą prawdę?


Mama called me disappointment, Papa called me fat
Każdego eksperta można zastąpić backendowcem który ma się douczyć po godzinach. Tak zostałem ekspertem AI, Neo4j i Nest.js . Przez mianowanie
AK
  • Rejestracja:ponad 6 lat
  • Ostatnio:dzień
  • Postów:3561
1
Aleksander32 napisał(a):

@Afish: a czy z hashem i equalsem w C# nie jest tak samo?

  1. Mam wrażenie, że w ekosystemie to odgrywa mniejsza rolę.
  2. Wg mnie syntax C# nieco sprzyja "psychologicznie" niemutowalnemu stylowi, co oczywiście ułatwia te zagadnienia. (w Javie nie ma to przejawu syntaktycznego - oprócz słowa final - tylko trzeba uczyć nawyków)

Bo C to najlepszy język, każdy uczeń ci to powie
edytowany 1x, ostatnio: AnyKtokolwiek
AF
  • Rejestracja:prawie 18 lat
  • Ostatnio:12 dni
2
Aleksander32 napisał(a):

@Afish: a czy z hashem i equalsem w C# nie jest tak samo?

Wszędzie jest to samo, bo koncept hashowania i hashmapy działa wszędzie jednako. Tylko co to ma do rzeczy? Nie przypominam sobie, żebym kiedykolwiek miał problem z hashami w innym języku, na niejavowych rekrutacjach nigdy mnie o to nie pytali, za to u javowców ten temat często wypływa.

jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 5 godzin
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4707
2

@Afish:

bo koncept hashowania i hashmapy działa wszędzie jednako

I tak i nie. W niektórych językach definicja typu i hashowania w (hashtable/hashmap) jest odspawana.
Dzięki czemu można jeden typ można na wiele sposobów hashować - w zależności od potrzeb.

Na tej samej zasadzie co odspawany jest Comparator. (Byłoby naprawdę nieciekawie jakby w javie było tylko Comparable i jak komuś nie pasuje to sobie wrapper by mógł zrobić...).

Oczywiście (w przypadku hashCode) jest to przydatne może raz na kilka miesięcy, ale pozwala uniknąć tworzenia absurdalnych hashCodów na zapas. Obecnie pisząc hashCode masz wybór:

  • zrobić - prosty, niewydajny, ale że w biznesowej aplikacji nie ma to znaczenia więc - będzie pan zadowolony,
  • zrobić specjalizowany pod przewidywane użycie, ale skończy się pytaniami i ryzykiem, że ktoś użyje w innej "hashTable" niż przewidzieliśmy,

jeden i pół terabajta powinno wystarczyć każdemu
edytowany 1x, ostatnio: jarekr000000
AF
  • Rejestracja:prawie 18 lat
  • Ostatnio:12 dni
1
jarekr000000 napisał(a):

I tak i nie. W niektórych językach definicja typu i hashowania w (hashtable/hashmap) jest odspawana.

Co nie zmienia konceptu działania hashmapy. Nie musisz mi tłumaczyć, że dałoby się tego hasha liczyć lepiej, bo jestem tego świadom i zgadzam się z Tobą, ale jakoś inne języki też robią to "po javowemu" i nie mają takich problemów. Nie wiem, czy problemy w Javie były spowodowane brakiem lambd w języku, przez co trzeba było klepać jakiegoś comparatora czy przeciążać equalsa, żeby łatwiej wyciągać elementy z kolekcji, czy może jakimś fundamentalnym skopaniem JPA, czy może niezrozumieniem LSP, ale jak tylko wyjdziesz poza świat javowy, to tam też są słowniki oparte o hashowanie i getHashCode na Object, i jakoś nic nie wybucha.

edytowany 1x, ostatnio: Afish
S9
  • Rejestracja:ponad 4 lata
  • Ostatnio:około 2 lata
  • Lokalizacja:Warszawa
  • Postów:1092
0

Śmiem wątpić że nikt nie narobił sobie bałaganu w c# z powodu hashcode/equals.


AF
  • Rejestracja:prawie 18 lat
  • Ostatnio:12 dni
1
Aleksander32 napisał(a):

Śmiem wątpić że nikt nie narobił sobie bałaganu w c# z powodu hashcode/equals.

Powiem tak: pracowałem na etacie w dotnecie przez jakieś 3 lata i chyba ani razu nie widziałem nadpisanego equals/hashcode. Być może to kwestia projektów, być może podejścia (zazwyczaj miałem transaction script, a nie DDD), ale nigdy nie miałem takiej potrzeby (i znajomi dookoła też nie), przez co problem ze zmieniającym się hashem nie istniał. Jakby nie java, to pewnie nawet nie byłbym świadomy, że taki problem może wystąpić, bo ani w swoim kodzie się na niego nie nadziałem, ani w cudzym. ani na konferencjach, ani nie kojarzę blogów opisujących to zagadnienie.

Oczywiście to moje wrażenie, każdy może mieć swoje.

W innych technologiach (python, JS, swift, VB.NET) problemu też nie widziałem, ale nie doktoryzowałem się z nich, więc moja opinia jest jeszcze mniej wiarygodna.

edytowany 1x, ostatnio: Afish
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 5 godzin
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4707
2

@Afish:

dziesz poza świat javowy, to tam też są słowniki oparte o hashowanie i getHashCode na Object, i jakoś nic nie wybucha.

Ale są jezyki, gdzie to zrobiono lepiej: rust, haskell, (w scali osobny typeclass Hash się pojawia w różnych bibliotekach, ale standardowa platforma nie kuma tego).
Poza tym, czasem wybucha. Tak jak pisałem dla mnie nie jest problemem tak bardzo jak często rzeczywiście trzeba pisać dwa różne hashCode dla tego samego typu.
Problemem jest wyrabianie u programistów złych intuicji i jałowe dyskusje.


jeden i pół terabajta powinno wystarczyć każdemu
AF
  • Rejestracja:prawie 18 lat
  • Ostatnio:12 dni
2
jarekr000000 napisał(a):

Ale są jezyki, gdzie to zrobiono lepiej

No są, tylko nie o to chodzi. Zgadzam się, ze hasha nie powinno być w obiekcie, ale twierdzę, że javowa konstrukcja hasha nie jest jakaś ułomna, bo jakoś w innych podobnych językach to działa, a tylko w Javie ludzie często się na tym wykładają, bo bez specjalnego przemyślenia nadpisują hashcode (czy to jawnie, czy to lombokiem) i potem mają problemy. Do tego dochodzi niezrozumienie LSP albo nieznajomość dokumentacji (vide twierdzenie, że hash ma być stały dla obiektu, albo dyskusja na forum o tym, czy remove na liście rzucający wyjątek nie łamie LSP).

jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 5 godzin
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4707
1

@Afish:

W zadazie wiadomo dlaczego akurat w javie ten hashCode robił tyle problemów - puty ludzie pisali to ręcznie to działy się naprawdę dziwne rzeczy.

Do tego dochodzi niezrozumienie LSP albo nieznajomość dokumentacji

I tu jest sęk. Programiści nie dzytają dokumentacji i będą jej czytać coraz mniej. The future is now. Dobre api / platforma powinna prowadzić za rączke i ograniczać szkody, a nie polegać na tym, że ktoś będzie czytał wszystkie dokumentacje - bo nie będzie. Kiedyś odsyłałem ludzi do książek i doców, ale zmieniłem zdanie jakiś czas temu - jak zobaczyłem, że jeśli ktoś chciałby zrobić aplikacje w takim springu / javie i nie strzelać sobie w kolano to tak naprawdę musiałby posiedzieć z rok nad książkami. A potem 5 lat ćwiczyć :-)


jeden i pół terabajta powinno wystarczyć każdemu
AF
  • Rejestracja:prawie 18 lat
  • Ostatnio:12 dni
1
jarekr000000 napisał(a):

I tu jest sęk. Programiści nie dzytają dokumentacji i będą jej czytać coraz mniej. The future is now. Dobre api / platforma powinna prowadzić za rączke i ograniczać szkody, a nie polegać na tym, że ktoś będzie czytał wszystkie dokumentacje - bo nie będzie.

Tak, ale tu dochodzisz do innego problemu, jakim jest ogólnie nierozwijanie się. Jeżeli pozwoli się ludziom pisać kod bez myślenia, to potem będą wychodziły potworki, jak w przykładzie @Shalom, gdzie on znał optionale i eithery, a inni nie. Tu jest to samo, albo mamy oczekiwanie, że ludzie nauczą się, albo w praktyce musimy przestać używać "mądrych" rzeczy, bo ludzie zawsze znajdą sposób, żeby strzelić sobie w stopę. Godzina debugowania pozwala zaoszczędzić 5 minut czytania dokumentacji, niezależnie od tego, czy dokumentacja jest o optionalu, czy o kontrakcie hashCode.

jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 5 godzin
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4707
1

@Afish:

Jeżeli pozwoli się ludziom pisać kod bez myślenia, to potem będą wychodziły potworki,

Taka jest rzeczywistość. Ludzie pisali i będą pisać bez myślenia.
Dla mnie cel jest prosty - api i język powinno być tak zrobione, że jak nie przeczytasz potrzebnego minimum to Ci się nie skompiluje. Wszelkie inne metody zwykle zawodzą, są nieekonomiczne.
Ten cel to oczywiście ideał i raczej nie zostanie osiągnięty (bo turing to świnia), ale są platformy bliżej tego celu (Scala) i dalej (java) i bardzo daleko (PHP, Javascript). Co nie oznacza, że każdy system jaki robimy musi być sicher i utrzymywalny (więc jakaś nadzieja/nisza i dla PHP i dla Javy zostaje).


jeden i pół terabajta powinno wystarczyć każdemu
edytowany 1x, ostatnio: jarekr000000
AF
  • Rejestracja:prawie 18 lat
  • Ostatnio:12 dni
0
jarekr000000 napisał(a):

jak nie przeczytasz potrzebnego minimum to Ci się nie skompiluje

No to jeszcze pozostaje ustalić, czym jest to "potrzebne" minimum i znaleźć jakąś magię, żeby programista nie musiał rozkminiać, a żeby błędny kod się nie kompilował, ale póki co na to się nie zanosi (nadzieja w sztucznej inteligencji, ale jak ona to opanuje, to programiści będą zbędni).

jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 5 godzin
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4707
2

Możemy to minmum określić na konkretnych przypadkach.

Już kilka razy podawałem, że dobrym przykładem jak to można było rozwiązać w ok w javie jest przykład Comparable, Comparator.
Mógłby być Hashable i Hash. Wtedy, albo implementujesz interfejs (ewentualnie generujesz implementację z automatu), albo zapodajesz zewnętrznie funkcję Hashującą. Albo nawet masz w kodzie obie opcje.

Jeśli piszesz obiekt, który niegdy w żadnej tablicy hashującej się nie znajdzie, to nawet nie wiesz o problemie. Jeśli chcesz użyć to widzisz co trzeba zaimplementować.
Od razu widać, że hashCode jest związany z tablicą hashującą - przy odrobinie szczęścia programist może nawet zrozumie po co te hashCode są. (fakt, nie ma gwarancji). Jak ktoś zaimplementował dziwnie... to możesz zaimplementować własną funkcję hashującą.

To nie jest rozwiązanie idealne, ale IMO byłoby mniej błędogęnne od obecnego, głównie dlatego, że lepiej byłoby widać powiązanie hashCode z jego użyciem. Obecnie ludzie wierzą w jakieś magie - implementują te hashCode wszędzie (również tam gdzie nie ma sensu, a wręcz nie powinno być).

Jest jeszcze lepiej jak Hash jest zrobiony jako typeclass i masz do tego jakiś derive (ale to już nie w javie i to głownie ergonomia).


jeden i pół terabajta powinno wystarczyć każdemu
edytowany 2x, ostatnio: jarekr000000
AF
  • Rejestracja:prawie 18 lat
  • Ostatnio:12 dni
0

@jarekr000000: No spoko, chyba wszyscy się zgadzają, że Twoja struktura typów jest fajna, tylko że to nie jest meritum problemu. Jak dasz interfejs Hashable, to niewiele zmieni, bo programista zaraz dorzuci adnotację z lomboka, która doda do klasy domyślną implementację FieldBasedHasher i wracamy do punktu wyjścia.

No i w Twojej hierarchii da się doprowadzić do sytuacji, gdy hashe równych obiektów są różne (jak zaimplementuję Comparable ale nie Hashable), więc jak nie przeczytasz potrzebnego minimum to Ci się nie skompiluje nie zachodzi. Jak już naprawiać, to dobrze byłoby, żeby było to bardziej idiotoodporne.

jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 5 godzin
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4707
1

Co do tego, że programiści w końcu wrzucą i tak wszystkie możliwe adnotacje - to mam tego świadomość. Nawet w Scali czy Haskellu na pewno będą (lub już są) tacy goście co wrzucają tony Deriving (Eq, Hashable, Ord, Show, Read...) - tak po prostu, bo kolega tak zrobił i mu działało.

Jeśli chodzi o niespójne hashCode z equals - to akurat Comparable w javie nie jest dobrym przykładem. Przypadkowo - bo akurat Comparable jest do sortowania i co więcej całkiem sprawnie działa (np.w SortedSet/TreeSet -) jeśli jest niespójne z equals/hashCode (co nie znaczy, że warto tak robić), łamany jest kontrakt Set, ale wynikowy DziwnySet działa całkiem sprawnie i dość intuicyjnie dla programisty ( TreeSet instance performs all element comparisons using its compareTo) - dość dobrze jest opisane w javadocu Comparable i SortedSet. Zresztą dlatego warto w miejscach wątpliwych użyć raczej Comparatora. Mniejsze zaskoczenie dla następcy.

Zgadzam się, że gdyby ten hashCode był inaczej zrobiony to nie zabezpieczyłoby całkiem przed problemem. Jak pisałem turing to świnia.
Pisanie API tak, żeby jednak utrudniać popełnianie błędu i kierować użytkownika do dokumentacji tam gdzie to absolutnie konieczne (wykorzystując kompilator) to jednak całkiem użyteczna sztuka.
Akurat w tym miejscu javowy hashCode + equals zawodzi po całości. Jakkolwiek, naprawdę nie wiem czy nawet jest na liście największych problemów javy - ot jeszcze jedna mała zwała, jest więcej miejsc gdzie warto by coś poprawić (a raczej spierniczyć do jezyka, który ma to już poprawione).


jeden i pół terabajta powinno wystarczyć każdemu
edytowany 6x, ostatnio: jarekr000000
somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:dzień
  • Lokalizacja:Wrocław
0
Aleksander32 napisał(a):

Śmiem wątpić że nikt nie narobił sobie bałaganu w c# z powodu hashcode/equals.

Żeby narobić bałaganu, trzeba by to najpierw zaimplementować, a zasadniczo nie ma po co i jest to zjawisko chyba równie rzadkie jak używanie wskaźników.

Afish napisał(a):

No są, tylko nie o to chodzi. Zgadzam się, ze hasha nie powinno być w obiekcie, ale twierdzę, że javowa konstrukcja hasha nie jest jakaś ułomna, bo jakoś w innych podobnych językach to działa, a tylko w Javie ludzie często się na tym wykładają, bo bez specjalnego przemyślenia nadpisują hashcode (czy to jawnie, czy to lombokiem) i potem mają problemy.

No cóż, w komunizmie, tak jak w każdym systemie totalitarnym, szeroko dyskutuje się o problemach zastępczych, mało istotnych, aby tylko przypadkiem nie zwrócić uwagi na ogólną c**** sytuacji.

Shalom
  • Rejestracja:około 21 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
4

tylko w Javie ludzie często się na tym wykładają

Pracuje 10 lat, większość tego czasu w technologiach JMVowych, głównie Java/Kotlin. Raz(!) w życiu widziałem fuckup związany z equals/hashcode i to wynikający z tego ze ktoś je nadpisał ręcznie i zwyczajnie źle zaimplementował. Problemów wynikających z użycia tych wygenerowanych albo tych z lomboka nigdy nie widziałem.
Warto rozumieć po co te metody są i co się z nimi wiąże, ale traktowałbym to na równi z == vs .equals, to taki quirk języka o którym trzeba wiedzieć, ale w prawdziwym życiu nigdy nie będziesz się nad tym zastanawiać.


"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"
somekind
To w końcu jest problem, czy go nie ma? :P
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 5 godzin
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4707
1

Jeden z fajniejszych fackupów z hashCodem to zaliczyli twórcy javy. Historia hashCode dla Stringa jest fascynująca. Walka nadal trwa - choć być może w jdk 15 jest ostateczna/ostateczna łata na "democrats understood clouting".


jeden i pół terabajta powinno wystarczyć każdemu
edytowany 2x, ostatnio: jarekr000000
99xmarcin
Rozumiem że sugestia jest taka że skoro twórcy JDK nie potrafią dobrze napisać hashCode to korporacyjny programista zrobi to lepiej... :trollface:
SW
  • Rejestracja:około 5 lat
  • Ostatnio:3 miesiące
  • Postów:250
0

Przez pół roku miałem okazję programować w Springu i JPA (mało tam było Javy) i czasem zastanawiałem się, czemu nie ma tam czegoś jak EqualityComparer? https://docs.microsoft.com/pl-pl/dotnet/api/system.collections.generic.iequalitycomparer-1?view=net-5.0

Ale kiedy zobaczyłem, że Integer jako PK z bazy wystarcza wszystkim za hashCode, przestałem się zastanawiać

edytowany 1x, ostatnio: SkrzydlatyWąż
S9
  • Rejestracja:ponad 4 lata
  • Ostatnio:około 2 lata
  • Lokalizacja:Warszawa
  • Postów:1092
1

@SkrzydlatyWąż: wystarczy dopóki nie będziesz chciał wsadzić do HashSeta nie zapisanej (nowej) encji ;)


SW
  • Rejestracja:około 5 lat
  • Ostatnio:3 miesiące
  • Postów:250
0
Aleksander32 napisał(a):

@SkrzydlatyWąż: wystarczy dopóki nie będziesz chciał wsadzić do HashSeta nie zapisanej (nowej) encji ;)

Dlatego najpierw insert do Repository i @Transactional wszędzie, żeby było bezpiecznie :)

99xmarcin
Dlatego w .NET'cie nikt się nie szczypie i wszystkie klucze to GUIDy generowane po stronie klienta...
SW
Też się szczypią i bywa że używają intów generowanych przez bazę. Za to w Javie można użyć UUID razem z JPA i też działa. To bardziej kwestia mentalności niż technologii.
99xmarcin
Jak dla kogoś UUID są za wolne to sekwencje z bazy są dobrą alternatywą (nie każda baza wspiera ale można to naprawić prostą stored proc). https://docs.microsoft.com/en-us/sql/t-sql/functions/next-value-for-transact-sql?view=sql-server-ver15
somekind
Dlatego w .NET'cie nikt się nie szczypie i wszystkie klucze to GUIDy generowane po stronie klienta... - w .NET nie ma strony klienta. :P
99xmarcin
  • Rejestracja:prawie 5 lat
  • Ostatnio:4 miesiące
  • Postów:2420
0

Moje 5 groszy:

  • hashCode to operacja niskopoziomowa. Programista który operuje na Order'ach i Invoce'ach nie powinien się nad czymś takim w ogóle zastanawiać.
  • Map<K,V> to interface. Ponieważ niektóre jego implementacje wymagają niskopoziomowej operacji hashCode dla kluczy, to mamy tutaj do czynienia z cieknącą abstrakcją.

Jeżeli chcemy używać obiektów wysokiego poziomu (np. OrderId, PromoCode) z pewnymi impl. mapy to wydaje się rozsądne wygenerowanie tych niskopoziomowych operacji (hashCode). Przez to defacto łatamy problem z cieknącą abstrakcją interfejsu Map.

W alternatywnym podejściu promowanym przez @jarekr000000 sami przy tworzeniu mapy dostarczamy hashCode np. jako implementacje interface'u. OK spox ale załóżmy że mój obiekt ma wszystkie pola private - użytkownik takiego obiektu nie będzie mógł stworzyć własnej impl. hashCode - będzie musiał użyć jednej z dostarczanych przez obiekt (bo nie ma dostępuj do prywatnych pól) [Zakładam generyczny scenariusz że obiekt dla którego chcemy liczyć hashCode żyje w zewnętrznej bibliotece].

Z kolei pisanie własnego hashCode dla typów niskopoziomowych typu String ze względu na perf również mi się nie klei. Nasza implementacja hashCode w języku wysokiego poziomu ma mierne szansę równać się z wyspecjalizowanym kodem assemblera dostarczanym przez twórców JDK. Najprawdopodobniej jakiś PhD z olbrzymią wiedzą o architekturze x86_64 spędził kilka miesięcy optymalizując ten kod.

ERGO, moim zdaniem generowanie kodu hashCode/equals czy to Lomb'okiem czy kompilatorem (Scala, Kotlin) jest najlepszym kompromisem przy obecnych językach programowania.


Holy sh*t, with every month serenityos.org gets better & better...
edytowany 1x, ostatnio: 99xmarcin
stivens
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 3 godziny
0

załóżmy że mój obiekt ma wszystkie pola private

Czy to jest obiekt typu ImperatywnyDoBoluMutujeWszystkoDlaFunu?

Ale po co cos takiego trzymac w mapie?

EDIT: to w sumie hiperbola bo jest sporo de facto czystych typow, ktore i tak maja wszystko private. Ale no raczej np. fabryki jako klucza sie nie uzywa.


λλλ
edytowany 2x, ostatnio: stivens
Zobacz pozostałe 2 komentarze
99xmarcin
Map<OrderId, Order>???
stivens
No ale pytam czy kiedys faktycznie tak zrobiles czy to taki przyklad zeby cos pokazac?
99xmarcin
Nie tylko tak robiłem, obecnie mam wszystkie ID powrapowane w Scalowe case class'y. Jeszcze lepiej to widać na HashSet<V> tam faktycznie mogą być klasy z silną enkapsulacją (w Mapie klucze są zazwyczaj bardzo proste).
stivens
w Mapie klucze są zazwyczaj bardzo proste a no wlasnie ;) Zbiory to chyba lepszy przyklad. Aczkolwiek tam raczej tez zazwyczaj beda proste obiekty bo po co komu np. zbior stringbuilderow? Zbior idkow oczywiscie moze sie zdarzyc
stivens
W ogole to taki calkowicie zaenkapsulowany obiekt moglby po prostu wystawiac jakas publiczna wartosc hasha. I wtedy taka implementacja hasha dla mapy/zbioru/… po prostu delegowalaby to do obiektu. Z jednej strony to troche boilerplate (bo trzeba by pisac jakies Map<T>(T::hash) a z drugiej przynajmniej by bylo wiadomo, ze ten hashcode jest faktycznie zaimplementowany a nie tylko odziedziczony. Tudziez byloby wiadomo, ze tego hasha nie ma i rownie dobrze mozna napisac return 1 ;)
S9
  • Rejestracja:ponad 4 lata
  • Ostatnio:około 2 lata
  • Lokalizacja:Warszawa
  • Postów:1092
0

@stivens: no nie koniecznie. Data class w Kotlinie też de facto mają prywatne pola, tylko są one pokryte lukrem składniowym.


stivens
No dobra ale jak cos ma getter to tak jakby nie bylo prywatnym polem. A w kazdym razie da sie przez getter hash wyliczyc.
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 5 godzin
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4707
0

@0xmarcin:

W alternatywnym podejściu promowanym przez @jarekr000000 sami przy tworzeniu mapy dostarczamy hashCode np. jako implementacje interface'u. OK spox ale załóżmy że mój obiekt ma wszystkie pola private - użytkownik takiego obiektu nie będzie mógł stworzyć własnej impl. hashCode - będzie musiał użyć jednej z dostarczanych przez obiekt (bo nie ma dostępuj do prywatnych pól) [Zakładam generyczny scenariusz że obiekt dla którego chcemy liczyć hashCode żyje w zewnętrznej bibliotece].

Tylko, że to nadal lepsza sytuacja niż to co mamy obecnie. Obecnie jak dostajesz obiekt z zewnątrz to może on:
a) nie mieć w ogóle zrobionego hashCode, a na identityhashcode średnio mozna zwykle polegać,
b) może mieć zrobiony hashCode zupełnie źle (np. wygenerowany na rympał i korzystający z pól non final lub lazy (jpa)),
c) może być nawet relatywnie dobry ...tylko, że nie akurat w twoim przypadku. Tu fajny przykład to hashCode genereowany dla recordu - record Point (int x, int y), który ma defaultowo hashCode dość zły w wielu typowych przypadkach (jaki pech).

Więc na koniec wrzuca się taki obiekt we wrapper i masz dokładnie ten sam problem ( i jeszcze wrapper ).

Co do hashCode i JPA to tu sie poblemy kumulują : https://thorben-janssen.com/lombok-hibernate-how-to-avoid-common-pitfalls/

Z kolei pisanie własnego hashCode dla typów niskopoziomowych typu String ze względu na perf również mi się nie klei.

Generalnie jasne, ale wyobraź sobie, że robisz hashset z kikuset bardzo, bardzo długich stringów ... które przeważnie różnią się od siebie juz na pierwszych kilku znakach. To, że do wyliczenia tego hashCodu jvm obleci wszystkie znaki nie zawsze jest takie fajne (ale to na pewno bardzo specyficzny scenariusz).

A najlepsze podsumowanie o tym jak to nie ma problemów z hashCodem i ile lat trzeba pisać w javie, żeby jakiś zobaczyć jest tutaj:
https://issues.apache.org/jira/browse/HBASE-18447?jql=text%20~%20%22hashCode%22

Wstawiłem, bo jeden z nich kiedyś wywalił mi produkcję ładnie (wydajnościowo). Oczywiście lista wyżej to przelotka po wszystkich projektach z apache, (w tym wielu popularnych), przez naprawdę wiele lat, nie wszystkie tak naprawdę dotycza hashCode.. ale jednak trochę tego jest (a 485 nadal otwartych).

Nie znaczy to też, że ten hash jest w javie aż tak źle zrobiony. Jest średnio. Ale to nie oznacza, że alternatywna implementacja, inny język wszystkie problemy by usunęła. Wszędzie tam, gdzie mamy do czynienia z naprawdę dużą ilością danych trzymaną w hashsecie raczej trzeba ręcznie napisać hashcode, który pasuje do konkretnego rozkładu danych (w tym konkretnym hashsecie). (Potencjalny Automat musiałby wiedzieć jak nasze dane wyglądają na produkcji).


jeden i pół terabajta powinno wystarczyć każdemu
edytowany 13x, ostatnio: jarekr000000
AP
  • Rejestracja:około 6 lat
  • Ostatnio:około 2 miesiące
  • Postów:164
0

Możecie tak w 2 zdaniach wyjaśnić jaki jest problem z hashCode w Javie? Przeczytałem cały wątek (ale jestem w trakcie pierwszej kawy, więć może bez zrozumienia) i nie bardzo rozumiem w czym jest problem, a do tej pory po prostu nie miałem z tym nigdy problemów.

Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)