2011-09-30 15 views
9

Ho una Hashmap che può contenere caratteri jolly (*) nella stringa.Restituzione di un elenco di corrispondenze di caratteri jolly da una HashMap in java

Per esempio,

HashMap<String, Student> students_; 

può avere John * come una chiave. Voglio sapere se JohnSmith corrisponde a qualsiasi elemento negli studenti_. Potrebbero esserci più corrispondenze per la mia stringa (John *, Jo * Smith, ecc.). C'è un modo per ottenere una lista di queste partite dalla mia HashMap?

C'è un altro oggetto che potrei usare che non mi richiede di scorrere tutti gli elementi della mia collezione, o devo succhiarlo e usare un oggetto List?

FYI, la mia raccolta conterrà meno di 200 elementi e alla fine voglio trovare la coppia che corrisponde alla minor quantità di caratteri jolly.

+2

funzioni di hashing, in generale, sono costruiti in modo che le modifiche minori (ad es: 'John SmitH' a' John Smith') producono hash totalmente diversi. – NullUserException

+0

Perché non vuoi iterare? Non è poi così male (specialmente con meno di 200 elementi), e in definitiva qualsiasi altra soluzione probabilmente implicherà qualcosa di simile in termini di prestazioni. – Guillaume

+0

A meno di 200 elementi, basta eseguire la ricerca lineare su 'entrySet()' e valutare il carattere jolly per ogni chiave.Se fosse stato molto di più, avrei suggerito un database (incorporato) e una query 'LIKE'. –

risposta

1

Non è possibile ottenere con un hasmap, a causa della funzione di hashing. Dovrebbe assegnare l'hash di "John*" e l'hash di "John Smith" e altri. lo stesso valore.

si potrebbe fare con un TreeMap, se si scrive la propria classe personalizzata WildcardString avvolgimento String, e implementare compareTo in modo tale che "John*".compareTo("John Smith") restituisce 0. Si potrebbe fare questo con le espressioni regolari come other answers hanno già fatto notare.

Visto che si desidera che l'elenco di corrispondenze widlcard sia sempre possibile rimuovere le voci così come le si trova e iterare TreeMap.get(). Ricorda di rimettere le chiavi una volta terminato con un nome.

Questo è solo un modo possibile per raggiungerlo. Con meno di 200 elementi starai bene iterando.

AGGIORNAMENTO: per imporre l'ordine correttamente sul TreeSet, si poteva distinguere il caso di confronto di due WildcardString s (che significa che è una comparazione tra i tasti) e confrontando un WildcardString ad un String (a confronto di una chiave con un valore di ricerca) .

+0

Grazie, Xavi. Per una lista di 200 pensi che ci saranno dei benefici in termini di prestazioni nell'utilizzo di un TreeSet? – Sarah

+2

La creazione di un metodo compareTo (stringa) nella classe WildCardString interromperà il contratto del metodo compareTo perché: 'wildCardString.compareTo (stringa)' potrebbe non essere il segno opposto o 'string.compareTo (wildCardString)'. Inoltre, si raccomanda che compareTo sia coerente con gli uguali. – Jim

+0

@jim Grazie per l'intuizione, buon punto. –

3

È possibile utilizzare espressioni regolari per abbinare, ma è necessario prima attivare "John*" nella regex equivalente "John.*", anche se è possibile farlo on-the-fly.

Ecco po 'di codice che funziona:

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

Ha detto in particolare che non voleva ripetere tutte le voci ... – Guillaume

+0

@Guillaume no ha fatto specificamente ** NON ** dillo. In particolare, ha detto: * C'è un altro oggetto che potrei usare che non mi richiede di scorrere tutti gli elementi della mia collezione, ** o devo succhiarlo e usare un oggetto List **? *. Ho risposto alla domanda confermando la parte * OR *. – Bohemian

+1

Bohemian, tu sofista :-) Sono d'accordo con la tua risposta comunque. Non vedo perché non voglia iterare. – Guillaume

0

Si può solo scorrere la vostra mappa senza convertirlo in un elenco, e utilizzare la funzione di stringa corrisponde, wih usa un'espressione regolare.

Se si vuole evitare il loop, è possibile utilizzare guava come questo

@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")); 

}

Problemi correlati