2012-03-01 5 views

risposta

47

TL; DR Per semplici stringhe è la cosa migliore, ma per solo le parole intere che corrispondono alle espressioni regolari sono probabilmente migliori.

Il modo migliore per vedere quale metodo è più efficiente è testarlo.

È possibile utilizzare String.contains() anziché String.indexOf() per semplificare il codice non regexp.

Per cercare parole diverse l'espressione regolare si presenta così:

apple|orange|pear|banana|kiwi 

I | lavora come OR nelle espressioni regolari.

Il mio codice di prova molto semplice assomiglia a questo:

public class TestContains { 

    private static String containsWord(Set<String> words,String sentence) { 
    for (String word : words) { 
     if (sentence.contains(word)) { 
     return word; 
     } 
    } 

    return null; 
    } 

    private static String matchesPattern(Pattern p,String sentence) { 
    Matcher m = p.matcher(sentence); 

    if (m.find()) { 
     return m.group(); 
    } 

    return null; 
    } 

    public static void main(String[] args) { 
    Set<String> words = new HashSet<String>(); 
    words.add("apple"); 
    words.add("orange"); 
    words.add("pear"); 
    words.add("banana"); 
    words.add("kiwi"); 

    Pattern p = Pattern.compile("apple|orange|pear|banana|kiwi"); 

    String noMatch = "The quick brown fox jumps over the lazy dog."; 
    String startMatch = "An apple is nice"; 
    String endMatch = "This is a longer sentence with the match for our fruit at the end: kiwi"; 

    long start = System.currentTimeMillis(); 
    int iterations = 10000000; 

    for (int i = 0; i < iterations; i++) { 
     containsWord(words, noMatch); 
     containsWord(words, startMatch); 
     containsWord(words, endMatch); 
    } 

    System.out.println("Contains took " + (System.currentTimeMillis() - start) + "ms"); 
    start = System.currentTimeMillis(); 

    for (int i = 0; i < iterations; i++) { 
     matchesPattern(p,noMatch); 
     matchesPattern(p,startMatch); 
     matchesPattern(p,endMatch); 
    } 

    System.out.println("Regular Expression took " + (System.currentTimeMillis() - start) + "ms"); 
    } 
} 

I risultati che ho ottenuto sono stati i seguenti:

Contains took 5962ms 
Regular Expression took 63475ms 

Ovviamente tempi variano a seconda del numero di parole da ricercare e la Le stringhe vengono ricercate, ma l' sembra essere ~ 10 volte più veloce delle espressioni regolari per una ricerca semplice come questa.

Utilizzando le espressioni regolari per cercare stringhe all'interno di un'altra stringa, si sta utilizzando una mazza per rompere un dado, quindi suppongo che non dovremmo essere sorpresi dal fatto che sia più lento. Salva le espressioni regolari per quando i modelli che vuoi trovare sono più complessi.

Un caso in cui si consiglia di utilizzare le espressioni regolari è se indexOf() e non farà il lavoro perché desideri solo per abbinare parole intere e non solo stringhe, per esempio vuoi corrispondere a pear ma non a spears. Le espressioni regolari gestiscono bene questo caso poiché hanno il concetto di word boundaries.

In questo caso ci piacerebbe cambiare il nostro modello a:

\b(apple|orange|pear|banana|kiwi)\b 

La \b dice per abbinare solo l'inizio o la fine di una parola e il gruppo staffe OR espressioni insieme.

nota, al momento di definire questo modello nel codice è necessario sfuggire alla backslash con un altro backslash:

Pattern p = Pattern.compile("\\b(apple|orange|pear|banana|kiwi)\\b"); 
7

Non credo che un'espressione regolare farà un lavoro migliore in termini di prestazioni, ma è possibile utilizzarlo come segue:

Pattern p = Pattern.compile("(apple|orange|pear)"); 
Matcher m = p.matcher(inputString); 
while (m.find()) { 
    String matched = m.group(1); 
    // Do something 
} 
+5

Non puoi leggere? Non ho mai detto che fosse efficiente. –

+1

Le prestazioni dipendono dalla lunghezza dell'espressione regolare. Se è inferiore a 1000 caratteri, vai avanti. Se è più lungo hai bisogno di un'altra soluzione. Ad esempio dividere il testo per separare le parole e confrontarle con la tabella hash predefinita/set di parole "conosciute". – AlexR

+2

@deporter lo scopo della risposta è dare un buon suggerimento su come risolvere la domanda per non fornire una soluzione perfetta, brillante, di livello mondiale. Può essere facilmente migliorato e, come per la leggibilità, se hai 200 stringhe (un motivo in più per non usare regexp per quello), puoi usare un ciclo for e concatenare in un 'StringBuilder'. Penso che la mia risposta fornisca abbastanza sapore. –

2

Qui è la soluzione più semplice che ho trovato (la corrispondenza con caratteri jolly):

boolean a = str.matches(".*\\b(wordA|wordB|wordC|wordD|wordE)\\b.*");