2009-11-03 10 views
5

Muszę przyznać, że posiadam tylko podstawowe wiadomości na temat działania HashTables, chociaż z tego, co wiem, wydaje mi się to dość proste. Moje pytanie brzmi właśnie tak: wydaje się, że konwencjonalna mądrość polega na używaniu prostych, podstawowych typów wartości, takich jak liczby całkowite dla kluczy w HashTable. Ciągi są jednak często używane, chociaż w wielu językach są one implementowane jako typy odniesienia. To, co uważam za ogólnie zalecane, nie jest zalecane przy użyciu złożonych typów referencyjnych; Zgaduję, że to dlatego, że wymagałoby to wolniejszej funkcji skrótu? Ale dlaczego tak powszechnie używane są struny? W końcu nie jest wewnętrznie ciągiem tablicy char [] (znowu w większości języków)?Dopuszczalne typy używane jako klucze w HashTable

W końcu, jakie typy wartości są ogólnie uważane za "najlepsze" (lub nawet po prostu "dopuszczalne") wybory do użycia jako klucze w HashTable? A czy są jakieś powszechnie stosowane wybory, które są faktycznie uważane za "złe" (jak na przykład ciągi)?

Odpowiedz

1

Najlepszym hash keys są te, które

  1. mają dobre (jak w niskich collisions) skrótów (patrz Object.GetHashCode dla .NET Object.hashcode dla Javy)
  2. mieć szybki porównań (bo gdy istnieją kolizje hash) .

Wszystko, co powiedzieliśmy, myślę, że łańcuchy są w większości przypadków dobrymi klawiszami skrótów, ponieważ istnieje wiele doskonałych implementacji skrótów dla ciągów.

3

Tak długo, jak jest zapewniona odpowiednia funkcja skrótu, wszystkie typy będą działały jak klucze. Pamiętaj, że po wszystkim tablica asocjacyjna jest po prostu tablicą liniową. Funkcja hash pobiera klucz określonego typu i oblicza indeks w tablicy tablicy mieszającej (zwanej bucket), w której wartość jest zapisywana (ale występują pewne problemy z kolizjami).

Tak naprawdę trudną częścią jest znalezienie funkcji skrótu. Oczywiście powinien on posiadać pewne właściwości, takie jak proste do obliczenia, chaotyczne (prawie identyczne klucze powinny być odwzorowane na kompletnie różne łyżki tabeli mieszania), deterministyczne (te same klucze oznaczają to samo tablice tablicy mieszającej), jednolitość (wszystkie możliwe klucze są odwzorowywane równomiernie na wiaderka), lub surektywność (wszystkie wiadra tabeli hash powinny być użyte).

Wydaje się, że łatwiej jest zdefiniować taką funkcję dla prostych typów, takich jak liczby całkowite.

+0

źle! prawdziwym problemem jest kluczowa zmienność! – Gyom

+0

To prawda. Jest to jednak określenie, które klucze są uważane za równe, a które nie. – spa

4

Większość implementacji ciągów, chociaż mogą one wyglądać jako typy odniesień w zarządzanych środowiskach, ich implementacja jest zwykle typu niezmiennego.

Funkcja skrótu polega na mapowaniu bardzo dużej liczby stanów na mniejszą liczbę stanów.

Dlatego mieszanie łańcuchów jest dobre do testowania równości łańcuchów. Możesz odwzorować wartość na indeks tablicy i szybko wyszukać informacje o tej wartości. Nie musisz porównywać każdego znaku z każdym innym znakiem w każdym innym ciągu. I możesz powiedzieć dokładnie to samo o wszystkim. Chodzi o zmniejszenie lub pobranie odcisków palców dowolnej liczby bajtów w jakiś użyteczny sposób.

W tym miejscu dyskusja na temat typu klucza używanego w tablicy mieszającej staje się nieważna, ponieważ jest mapowaniem tej wartości na mniejszą przestrzeń stanów i sposobem, w jaki jest ona wykorzystywana wewnętrznie, co czyni ją użyteczną. Liczba całkowita jest zwykle przyjazna sprzętowo, ale 32-bitowe nie jest tak naprawdę dużą przestrzenią, a kolizje są prawdopodobnie w obrębie tej przestrzeni dla dowolnych danych wejściowych.

W końcu, jeśli użyjesz tabeli mieszania, koszt obliczenia wartości skrótu jest nieistotny w porównaniu do czasu, jaki zajęłoby porównanie każdej wartości z każdą inną wartością w każdej innej możliwej pozycji (zakładając, że twój hash tabela zawiera setki pozycji).

+0

Rozumiem, że funkcja skrótu działa przez mapowanie (potencjalnie) dużej wartości na mniejszą przestrzeń, ale czy prędkość funkcji skrótu również nie zależy od rozmiaru jej wejścia? Właśnie dlatego założyłem, że zazwyczaj nie zaleca się używania dużych typów referencji jako kluczy. Jeśli jednak tak nie jest, to zastanawiam się, dlaczego w ogóle by się to zniechęciło (może dlatego, że programista musi wdrożyć swoją własną wydajną funkcję skrótu?). –

+0

Tak jak powiedziałem, wiele zarządzanych środowisk implementuje ciągi jako typy niezmienne. A gdy masz niezmienny typ, kod skrótu nie musi być obliczany za każdym razem, ponieważ wartość jest stała (raz utworzona). Zazwyczaj wystarczy raz zapłacić koszt wytworzenia kodu skrótu dla każdego unikatowego ciągu znaków. na przykład Środowisko wykonawcze .NET utrzymuje wewnętrzną pulę ciągów, aby to osiągnąć. Jednak koszt wytworzenia kodu skrótu z nieznanego ciągu znaków istnieje, ale koszt jest związany z długością łańcucha używanego jako klucz, a nie z rozmiarem kolekcji (lub tablicy mieszającej). –

+0

Jest to dla mnie zupełnie nieintuicyjne. Czy mówisz, że jeśli dodaję element do HashTable, a później pójdę do niego po klucz, środowisko wykonawcze będzie magicznie znało kod skrótu dla tego klucza bez konieczności wykonywania funkcji skrótu? Jak to może być? –

1

Jeśli było użyć typu złożonego jako klucz następnie:

  • byłoby trudne do wdrożenia tabeli hash do pozycji grupy w wiadrach do szybkiego odzyskania; w jaki sposób zdecydować, jak pogrupować zakres skrótów w wiadrze?
  • Tabela mieszania może wymagać dokładnej znajomości typu, aby wybrać wiadro.
  • Istnieje ryzyko zmiany właściwości obiektu, co powoduje, że przedmioty kończą się w niewłaściwych segmentach. Hashy muszą być niezmienne.

Liczby całkowite często używane, ponieważ można je łatwo podzielić na zakresy odpowiadające segmentom, są to typy wartości, a zatem niezmienne i są dość łatwe do wygenerowania.

5

To nie jest kwestia ciągów porównaniu liczb całkowitych, lub wartości kontra odniesienia, ale modyfikowalnych klawiszy porównaniu kluczy niezmiennych. Tak długo, jak klucze są niezmienne (a tym samym ich wartość mieszania nigdy się nie zmienia), są one w stanie indeksować tablicę asocjacyjną. Na przykład ciągi znaków w języku Java są niezmienne i dlatego doskonale nadają się jako klucze hashtable.

Nawiasem mówiąc, jeśli typ danych jest na tyle prosty, aby zawsze był przekazywany przez wartość (np. Skalary), to oczywiście będzie w porządku.

Ale teraz wyobraź sobie, że używasz zmiennego typu; jeśli podasz mi odniesienie do jednego z tych obiektów jako klucza, wyliczę jego wartość hash, a następnie umieścisz ją w jednym z moich zasobników hashtable. Ale kiedy później zmodyfikujesz obiekt, nie będę miał możliwości powiadomienia; i obiekt może teraz znajdować się w niewłaściwym wiadrze (jeśli jego wartość mieszania jest inna).

Mam nadzieję, że to pomoże.

+0

To jest bardzo pomocna odpowiedź; ale wciąż zastanawiam się, czy istnieją pewne typy, które "lepiej" używać jako kluczy niż inne. Na przykład, zakładając, że zdefiniowałem klasę, która jest w rzeczywistości niezmienna i utrzyma się z tym samym kodem mieszania dla całego jej istnienia. Czy używanie klucza jest bardzo dobre, czy też byłoby lepiej używać czegoś w rodzaju liczby całkowitej (ze względu na wydajność)? Wydaje mi się, że pełna, kompleksowa odpowiedź może być kombinacją twoich (klucze muszą być niezmiennymi typami) i spa (typy używane jako klucze powinny mieć wydajne funkcje mieszające) ... –

+0

@Dan: konkretna tabela potrzeb mieszania przechowywać to, czego potrzebuje do przechowywania. Jeśli utrzymujesz pamięć podręczną w Internecie, przechowujesz zawartość dla adresów URL. Klucz musi być adresem URL, nie może być liczbą całkowitą, ponieważ nie sprawdzasz liczb całkowitych. Oczywiście szybszy jest "lepszy", ale "robi to, co chcę powoli" jest zawsze "lepszy" niż "robi coś, co jest naprawdę szybkie, ale całkowicie bezużyteczne" :-) –

+0

Należy również zauważyć, że nie ma nic złego w korzystaniu z modyfikowalnego typu klasy jako klucz tabeli mieszającej, jeśli celem klucza jest * zidentyfikowanie * określonego obiektu. Na przykład, w .net, 'System.Windows.Forms.Form' jest w dużym stopniu zmiennym typem (z atrybutami takimi jak pozycja itp., Które mogą się zmieniać w dowolnym momencie), ale można użyć hashtable do kojarzenia formularzy z czymś innym. Zwróć uwagę, że taka tabela zawsze traktuje dwa odniesienia do różnych formularzy jako nierówne, nawet jeśli wszystkie ich właściwości inne niż ich tożsamość pasują do siebie. – supercat

Powiązane problemy