2011-09-30 13 views
9

Mam mapę Hashmap, która może zawierać znaki wieloznaczne (*) w ciągu znaków.Zwracanie listy dopasowań symboli wieloznacznych z HashMap w java

Na przykład

HashMap<String, Student> students_; 

może mieć John * jako jeden klucz. Chcę wiedzieć, czy JohnSmith pasuje do jakichkolwiek elementów w studentach. Może być kilka dopasowań dla mojego ciągu (John *, Jo * Smith, itp.). Czy istnieje sposób, aby uzyskać listę tych meczów z mojego HashMap?

Czy istnieje inny obiekt, który mógłbym użyć, który nie wymaga od mnie przechodzenia przez każdy element w mojej kolekcji, czy muszę go ssać i używać obiektu List?

FYI, moja kolekcja będzie zawierała mniej niż 200 elementów, a docelowo będę chciał znaleźć parę pasującą do najmniejszej liczby symboli wieloznacznych.

+2

funkcje mieszaja w ogóle są skonstruowane w taki sposób, że drobne zmiany (np: '' John SmitH' do John Smith') dają całkowicie różne hasze. – NullUserException

+0

Dlaczego nie chcesz iterować? To nie jest takie złe (szczególnie w przypadku mniej niż 200 elementów), a ostatecznie każde inne rozwiązanie będzie prawdopodobnie obejmować coś podobnego pod względem wydajności. – Guillaume

+0

Przy mniej niż 200 elementach, po prostu wykonaj liniowe przeszukiwanie przez 'entrySet()' i oceń swój symbol wieloznaczny na każdy klucz.Gdyby to było znacznie więcej, zasugerowałbym (wbudowaną) bazę danych i zapytanie "LIKE". –

Odpowiedz

1

Nie można osiągnąć dzięki hasmapie, ze względu na funkcję skrótu. Musiałby przypisać wartość mieszającą z "John*" i hash z "John Smith" i in. ta sama wartość.

Można zrobić to z TreeMap, jeśli piszesz własny zwyczaj klasa WildcardString owijania String i wdrożyć compareTo w taki sposób, że "John*".compareTo("John Smith") zwraca 0. Można to zrobić z wyrażeń regularnych jak other answers już zauważył.

Widząc, że chcesz listę widlcard matchings możesz zawsze usunąć wpisy, jak je znaleźć, i iterować TreeMap.get(). Pamiętaj, aby odłożyć klucze, gdy skończysz z nazwą.

To tylko jeden z możliwych sposobów, aby to osiągnąć. Z mniej niż 200 elementów będzie w porządku iteracji.

UPDATE: Aby narzucić porządek poprawnie na TreeSet, można odróżnić przypadek porównywania dwóch WildcardString s (co oznacza, że ​​jest to komparacji między klawiszami) i porównując WildcardString do String (porównując klucz o wartości wyszukiwania) .

+0

Dziękuję, Xavi. Czy uważasz, że lista 200 osób przyniesie korzyści związane z korzystaniem z TreeSet? – Sarah

+2

Utworzenie metody compareTo (string) w klasie WildCardString złamałoby umowę metody compareTo, ponieważ: 'wildCardString.compareTo (string)' może nie być przeciwnym znakiem lub 'string.compareTo (wildCardString)'. Dodatkowo zaleca się, by compareTo było zgodne z równymi. – Jim

+0

@jim Dzięki za twój wgląd, dobry punkt. –

3

Możesz użyć wyrażenia regularnego, aby dopasować, ale musisz najpierw przekształcić "John*" w odpowiednik regex "John.*", chociaż możesz to robić w locie.

Oto niektóre kodu, który będzie działać:

String name = "John Smith"; // For example 
Map<String, Student> students_ = new HashMap<String, Sandbox.Student>(); 

for (Map.Entry<String, Student> entry : students_.entrySet()) { 
    // If the entry key is "John*", this code will match if name = "John Smith" 
    if (name.matches("^.*" + entry.getKey().replace("*", ".*") + ".*$")) { 
     // do something with the matching map entry 
     System.out.println("Student " + entry.getValue() + " matched " + entry.getKey()); 
    } 
} 
+1

Powiedział specjalnie, że nie chce iterować nad wszystkimi wpisami ... – Guillaume

+0

@Guillaume nie on specjalnie zrobił ** NIE ** tak mówi. Mówiąc konkretnie, powiedział: * Czy istnieje inny obiekt, który mógłbym użyć, który nie wymaga od mnie przechodzenia przez każdy element w mojej kolekcji, ** czy muszę go ssać i używać obiektu List **? *. Odpowiedziałem na pytanie, potwierdzając część * OR *. – Bohemian

+1

Czeski, ty sofista :-) Zgadzam się z twoją odpowiedzią. Nie rozumiem, dlaczego nie chce iterować. – Guillaume

0

Można po prostu iteracyjne mapy bez konwertowania go na liście, a następnie użyj funkcji String mecze, wih używa wyrażenia regularnego.

Jeśli chcesz uniknąć pętli, można użyć guawa tak

@Test 
public void hashsetContainsWithWildcards() throws Exception { 
Set<String> students = new HashSet<String>(); 
students.add("John*"); 
students.add("Jo*Smith"); 
students.add("Bill"); 

Set<String> filteredStudents = Sets.filter(students, new Predicate<String>() { 
    public boolean apply(String string) { 
    return "JohnSmith".matches(string.replace("*", ".*")); 
    } 
}); 

assertEquals(2, filteredStudents.size()); 
assertTrue(filteredStudents.contains("John*")); 
assertTrue(filteredStudents.contains("Jo*Smith")); 

}

Powiązane problemy