Confronto stringhe performante

di il
10 risposte

Confronto stringhe performante

Ciao ragazzi avrei bisogno di un consiglio (eventualmente accetto anche la soluzione), su un confronto tra due stringhe.

In pratica leggo un file situato all’interno della mia applicazione inserisco quel che mi serve in una lista e poi passo delle stringhe ad un metodo che itera sulla lista specificata per un confronto.
Il problema è che provando l’applicazione su un dispositivo non troppo performante, la schermata diventa nera per circa 10 secondi. Quindi vorrei capire come migliorare il codice.

Il file non è enorme, sono circa 350 righe, quindi è evidente che il mio sistema non va bene. Posto alcuni pezzi di codice…

...
 try {
          leggiDati();
        } catch (IOException e) {
            e.printStackTrace();
        }

private void leggiDati() throws IOException {

        FileInputStream in = getApplicationContext().openFileInput(String.valueOf(filePath));
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));

        String line;
        try {
            while ((line = reader.readLine()) != null) {
                String[] ruote = line.split(",");
                for(int rt = ruotaScelta-1; rt < ruote.length; rt += 11) {
                    switch (ruotaScelta) {
                        case 1:
                            listaFinale.add(ruote[rt]);
                            break;
                        case 2:
                            listaFinale.add(ruote[rt]);
                            break;
                            ...
                       }
                   }  
            in.close();
            reader.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
 

Passo le stringhe al metodo per iterare con l'ArrayList..

 for (int n = 1; n <= 90; n++) {
            for (int n2 = 1; n2 <= 90; n2++) {
                if (n != n2 && n < 10 && n2 >= 10) {
                    singolaRuota(ruotaScelta, "0" + Integer.toString(n), Integer.toString(n2));
                } else if (n != n2 && n2 < 10 && n >= 10) {
                    singolaRuota(ruotaScelta, Integer.toString(n), "0" + Integer.toString(n2));
                } else if (n != n2 && n < 10 && n2 < 10) {
                    singolaRuota(ruotaScelta, "0" + Integer.toString(n), "0" + Integer.toString(n2));
                } else if (n != n2 && n >= 10 && n2 >= 10) {
                    singolaRuota(ruotaScelta, Integer.toString(n), Integer.toString(n2));
                }
            }

        }

Questo è il metodo che confronta le stringhe...
In pratica ho bisogno di contare quante volte è presente un determinato ambo, per poi stamparlo sullo schermo.

 private void singolaRuota(int r, String n1, String n2) {

        switch (r) {
            case 1:
                int ps = 0;
                for (String s : getListaFinale()) {
                    if((s.contains(n1) && s.contains(n2))) {
                        if (!n1.equals(n2)) {
                            ps++;
                        }
                    }
                }
                if(ps > totale) {
                    tmp.add(n1 + "-" + n2);
                    tmp.add(Integer.toString(ps));
                }

                Iterator<String> it = tmp.iterator();
                while (it.hasNext()) {
                    String s = it.next();
                    if(s.equals(n2 + "-" + n1)) {
                        listaIterata.add(n1 + " - " + n2);
                        listaIterata.add(Integer.toString(ps));
                        it.remove();
                    }
                }
                break;
                ...                
Infine chiamo il metodo che si occupa di popolare la ListView

private void mostraFrequenze() {      
        for(int i = 0, v = 1; i < listaIterata.size(); i+=2, v+=2) {
            frequenza.add((new RuotaSelezionata(listaIterata.get(i), listaIterata.get(v))));           
        }
    }
Vorrei cortesemente capire se List<String> è la scelta giusta e, poi magari anche un consiglio su come migliorare questo codice. Questa, così come lo è un'altra classe dell'applicazione che sto sviluppando, non sono per nulla performanti.

Grazie in anticipo a chiunque voglia darmi una mano.

10 Risposte

  • Re: Confronto stringhe performante

    Provo a riproporre il mio problema.

    Devo fare un confronto tra due liste così da poter scartare le presenze troppo basse e i doppioni:
    Es:
    01 – 44 <= 1 presenza -> scarto
    05 – 34 >= 3 presenze ? inserisco nella lista finale
    05 – 34 si ripeterà con 34 – 05 quindi dovrò scartare una delle due coppie.

    Per fare questo lavoro non avendo una cultura da programmatore ho pensato di utilizzare/iterare due liste, ho letto che StringBuffer o ancora meglio StringBuilder sono più performanti, ma nel mio caso non saprei come implementarli, l’iterazione mi sembra l’opzione più corretta.

    Ora, nel mio caso ciclo tutti e 90 numeri (essendo stringhe dall’uno al nove mi tocca inserire “0” solo per la stampa finale), per questo motivo si vede questo “pasticcio”:
    for (int n = 1; n <= 90; n++) {
        for (int n2 = 1; n2 <= 90; n2++) {
            if (n != n2 && n < 10 && n2 >= 10) {
                singolaRuota(ruotaScelta, "0" + Integer.toString(n), Integer.toString(n2));
            } else if (n != n2 && n2 < 10 && n >= 10) {
                singolaRuota(ruotaScelta, Integer.toString(n), "0" + Integer.toString(n2));
            } else if (n != n2 && n < 10 && n2 < 10) {
                singolaRuota(ruotaScelta, "0" + Integer.toString(n), "0" + Integer.toString(n2));
            } else if (n != n2 && n >= 10 && n2 >= 10) {
                singolaRuota(ruotaScelta, Integer.toString(n), Integer.toString(n2));
            }
        }
    }
    Probabilmente (sicuramente) avrei potuto migliorare quel codice, ad ogni modo questa è stata la mia scelta.
    Questi parametri;
    singolaRuota(ruotaScelta, Integer.toString(n), Integer.toString(n2));
    Li ritrovo qui;
    private void singolaRuota(int r, String n1, String n2) {
        switch (r) {
    	    case 1: // sarà Bari
              int ps = 0; // presenze
              for (String s : getListaFinale()) { //ciclo la lista
                 if((s.contains(n1) && s.contains(n2))) {
                    if (!n1.equals(n2)) {
                       ps++; // incremento presenza
                    }
                }
             }
    
    // se le presenze sono maggiori >= 3 a seconda delle mie impostazioni
    if(ps > totale) {
        // aggiungo alla lista temporanea l'ambo
        tmp.add(n1 + "-" + n2);
        tmp.add(Integer.toString(ps));
    }
    // itero la lista temporanea per escludere i doppioni
    // quindi inserisco gli ambi che mi interessano
    // in una lista finale 
    Iterator<String> it = tmp.iterator();
    while (it.hasNext()) {
        String s = it.next();
        if(s.equals(n2 + "-" + n1)) {
            listaIterata.add(n1 + " - " + n2);
            listaIterata.add(Integer.toString(ps));
            it.remove();
        }
    }
    E questo è tutto il codice utile per queste operazioni. Adesso che, spero sia più chiaro, qualcuno può “gentilmente” consigliarmi come migliorare questo codice?

    Queste poche righe di codice mi impallano l’applicazione, impiega troppo tempo.
  • Re: Confronto stringhe performante

    Ragazzi per favore non rispondete tutti insieme altrimenti rischio di far confusione...
  • Re: Confronto stringhe performante

    Ciao, provo ad aiutarti ma non sono sicuro di aver ben compreso il tuo codice.
    Se ho ben capito tu hai un file contenente un certo numero di vecchie estrazioni del lotto e vuoi tirare fuori tutti gli ambi che sono usciti con una certa frequenza. E' corretto?

    Comunque, in generale gli errori più evidenti che ho trovato sono i seguent:
    • La tua applicazione tratta dei numeri, ma tu lavori con delle stringhe, che ti costringono a fare cose strane (vedi la logica per aggiungere lo zero) e il cui confronto è decisamente meno performante rispetto al confronto di 2 interi
    • Di solito si lavora con delle liste se ti interessa mantenere un determinato ordinamento. Se ho ben capito qual è il tuo obiettivo, allora non ti serve mantenere l'ordinamento, per cui potresti lavorare con strutture dati diverse, in cui la ricerca è più performante (es: HashSet)
  • Re: Confronto stringhe performante

    E' ovvio che SE il tuo dato e' numerico e ci devi fare confronti e op matematiche, usare delle stringhe e' quantomento inefficiente. Diciamo pure sciocco.
    SE ti serve avere una collezione di oggetti e controllare se dentro questa collezione c'e' uno specifico oggetto, la LISTA (in cui la ricerca e' sequenziale) NON E' ASSOLUTAMENTE la struttura ideale.
    Ti serve un SET, una struttura dati PENSATA appositamente per questo tipo di op.

    ESATTAMENTEcquanto ha scritto @della

    Questi problemi sono legati ad un errore concettuale di fondo:
    NON SI STUDIA spulciando Internet, ma SUI LIBRI.
  • Re: Confronto stringhe performante

    Della ha scritto:


    Ciao, provo ad aiutarti ma non sono sicuro di aver ben compreso il tuo codice.
    Se ho ben capito tu hai un file contenente un certo numero di vecchie estrazioni del lotto e vuoi tirare fuori tutti gli ambi che sono usciti con una certa frequenza. E' corretto?

    Comunque, in generale gli errori più evidenti che ho trovato sono i seguent:
    • La tua applicazione tratta dei numeri, ma tu lavori con delle stringhe, che ti costringono a fare cose strane (vedi la logica per aggiungere lo zero) e il cui confronto è decisamente meno performante rispetto al confronto di 2 interi
    • Di solito si lavora con delle liste se ti interessa mantenere un determinato ordinamento. Se ho ben capito qual è il tuo obiettivo, allora non ti serve mantenere l'ordinamento, per cui potresti lavorare con strutture dati diverse, in cui la ricerca è più performante (es: HashSet)
    Allora, grazie ai consigli di una generosa persona (non se la tira come qualche sapientone), ho migliorato di molto le prestazioni con due piccole modifiche.

    Lettura della lista una sola volta, prima di invocare il metodo:
    //Leggo la lista una volta per tutte
            List<String> listaConfronto = getListaFinale();
    
            for (int n = 1; n <= 90; n++) {
                for (int n2 = n+1; n2 <= 90; n2++) {
                    String ns1 = String.format("%02d", n);
                    String ns2 = String.format("%02d", n2);
                    singolaRuota(listaConfronto, ruotaScelta, ns1, ns2);
                }
    
            }
    Quindi ho modificato il metodo in questo modo:
     private void singolaRuota(List<String> lista, int r, String n1, String n2) {
    
            switch (r) {
    
                case 1: // sarà Bari
                    int ps = 0; // presenze
                    for (String s : lista) { //ciclo la lista
                            if((s.contains(n1) && s.contains(n2))) {
                                ps++; // incremento presenza
                            }
                        }
                    // se le presenze sono maggiori >= 3 a seconda delle mie impostazioni
                    if(ps > totale) {
                        // aggiungo alla lista temporanea l'ambo
                        tmp.add(n1 + "-" + n2);
                        tmp.add(Integer.toString(ps));
                    }
    
                    // itero la lista temporanea per escudere i doppioni
                    // quindi inserire gli ambi che mi interessano
                    // in una lista finale
                    Iterator<String> it = tmp.iterator();
                    while (it.hasNext()) {
                        String s = it.next();
                        if(s.equals(n1 + "-" + n2)) {
                            listaIterata.add(n1 + " - " + n2);
                            listaIterata.add(Integer.toString(ps));
                            it.remove();
                        }
                    }
                    break;
    Però mi è stato anche sconsigliato, come tu stesso hai detto di utilizzare delle stringhe. Un tizio mi ha parlato di array numerici, di inserire tutto su una matrice 90x90. Ma onestamente non è stato molto chiaro quindi ho passato.

    Adesso tu mi stai dicendo di utilizzare HashSet, ma i Set non accettano doppioni o sbaglio?

    Mettiamo il caso di avere 25 – 74 e 25 -67, il 25 non viene scartato?

    Mi potresti fare un piccolo esempio? Hai voglia? Non sono più un ragazzino quindi il tempo non è dalla mia parte.

    In pratica ho dei file csv (che si trovano nella memoria del dispositivo) da cui leggo i dati.

    1) L’utente seleziona il periodo e la ruota
    2) Leggo i dati e popolo una lista provvisoria
    3) Ciclo i 90 numeri a coppie (n1, n2)
    4) All’interno del metodo, in base al numero delle estrazioni, ho una variabile totale il cui valore cambia da 3 a 9.
    5) Quando un ambo è >= a totale, la variabile presenze += 1 e aggiungo l’ambo ad una lista tmp.
    6) Itero la lista tmp per eliminare i doppioni (es: 33 – 44, 44 – 33), e inserisco questi ambi all’interno di una lista definitiva.
    7) Passo il tutto ad una classe Adapter per popolare una ListView.

    E questo è tutto.

    Cronometrando il tempo, risulta che per popolare la lista definitiva (passo 6) trascorrono per smartphone con API 19 (KitKat) fino a 4 secondi, prima di questa variazione ne trascorrevano anche 17. Quindi per me è già un’ottima cosa, se però mi suggerite un metodo alternativo, io ascolto volentieri, e ringrazio per la pazienza.

    migliorabile ha scritto:


    E' ovvio che SE il tuo dato e' numerico e ci devi fare confronti e op matematiche, usare delle stringhe e' quantomento inefficiente. Diciamo pure sciocco.
    SE ti serve avere una collezione di oggetti e controllare se dentro questa collezione c'e' uno specifico oggetto, la LISTA (in cui la ricerca e' sequenziale) NON E' ASSOLUTAMENTE la struttura ideale.
    Ti serve un SET, una struttura dati PENSATA appositamente per questo tipo di op.

    ESATTAMENTEcquanto ha scritto @della

    Questi problemi sono legati ad un errore concettuale di fondo:
    NON SI STUDIA spulciando Internet, ma SUI LIBRI.
    Ho letto libri e svolto corsi online, ma c’è tanta roba da imparare, e questo dovresti saperlo Mr. Ingegnere. Tra l’altro, tempo fa ho anche seguito alcune lezioni online (insomma faccio quel che posso “permettermi”) di alcune università (ma non spiegano nulla di veramente utile). Ad ogni modo se personalmente decido di dare un contributo (aiutare qualcuno) lo faccio perché ne ho voglia, non per mania di egocentrismo, tanto meno ridicolizzo la persona a cui lo dedico. Poi tutta sta confidenza, ma chi ti conosce?

    Guarda che non hai a che fare con un ragazzino, quindi stai rilassato, nessuno ti obbliga a far nulla.
  • Re: Confronto stringhe performante

    Per la questione confronto fra numeri e confronto fra stringhe, considera che 2 numeri possono essere confrontati in maniera "immediata", con 1-2 istruzioni in linguaggio macchina. Le stringhe, invece, vengono confrontate andando a fare un confronto per ogni singolo carattere che le compone, quindi puoi facilmente intuire che ciò è più lento, anche se la stringa è corta. Ad esempio, per confrontare 80 e 81, col confronto di numeri ottieni subito la risposta, mentre con le stringhe confronti prima "8" e "8", e poi "0" e "1". Visto che questi confronti vengono ripetuti molte volte, puoi avere un calo di performance non trascurabile.

    Per tornare di nuovo al tuo problema, non mi è chiaro se ciò che vuoi ottenere sono gli ambi più frequenti di una determinata ruota oppure i numeri più frequenti di una determinata ruota. La differenza fra i 2 casi può sembrare piccola, ma in realtà richiede soluzioni decisamente diverse.
    Supponi ad esempio di avere le seguenti estrazioni per la stessa ruota:

    1, 2, 3, 4, 5
    2, 4, 7, 8, 9
    1, 10, 11, 12, 13
    1, 14, 15, 16, 17
    2, 18, 19, 20, 21

    A questo punto, l'ambo più frequente è (2, 4), uscito nella prima e seconda estrazione, mentre la coppia di numeri più frequente è (1, 2), in quanto 1 è presente nella prima, terza e quarta estrazione e il 2 è presente nella prima, seconda e quinta estrazione.
    Tu quale dei 2 vuoi calcolare?
  • Re: Confronto stringhe performante

    Ho letto solo adesso, mi servono gli ambi più frequenti, l'utente visualizzerà sullo schermo una lista di tutti gli ambi più frequenti di una determinata ruota.

    Per gli estratti (singoli numeri) più frequenti sono già a posto.

    Il fatto è che avendo un file.csv di questo aspetto;
    01.33.90.15.05
    55.61.34.67.41
    87.09.31.59.88
    pensavo che la cosa più comoda fosse l'utilizzo di List<String>, ad ogni modo qual'è il tuo consiglio? List<Integer>? HashSet non accetta doppioni...

    Va beh! Rispondi con calma, è pur sempre domenica.
  • Re: Confronto stringhe performante

    Ok, ora il problema è più chiaro.
    Visto che ti interessano gli ambi, allora una cosa importante da fare (che non sono sicuro di aver visto nel codice che hai postato sopra) è di estrarre tutti i possibili ambi da un'estrazione (dati 5 numeri avrai 10 ambi, ovvero 10 combinazioni di 2 numeri).

    Una volta fatto ciò, puoi salvare gli ambi in un oggetto che ti permetta di salvare una coppia di valori (es: la classe Pair della libreria Apache commons-lang, oppure puoi facilmente creare una classe analoga, è estremamente facile). Nota che come hai già detto prima, la coppia (A, B) è uguale alla coppia (B, A), per cui devi trovare un modo per far sì che ciò sia vero anche nel tuo programma (una soluzione banale consiste nel creare una coppia in cui il primo elemento è il più piccolo, ad esempio, oppure ridefinisci il metodo "equals").

    Infine, puoi salvare le varie frequenze in una struttura dati come la HashMap, dove la chiave è l'ambo e il valore è la frequenza di tale ambo. Alla fine poi andrai ad iterare la tua mappa escludendo gli ambi la cui frequenza è minore di un certo numero.
  • Re: Confronto stringhe performante

    Della, grazie del suggerimento, ci ragionerò sopra e se la cosa risulterà fattibile (per me), la metterò in pratica modificando la classe responsabile, altrimenti ne farò comunque tesoro per un progetto futuro.

    Più che altro, così di primo acchito mi mette in crisi il tuo primo consiglio:

    Della ha scritto:


    Visto che ti interessano gli ambi, allora una cosa importante da fare (che non sono sicuro di aver visto nel codice che hai postato sopra) è di estrarre tutti i possibili ambi da un'estrazione (dati 5 numeri avrai 10 ambi, ovvero 10 combinazioni di 2 numeri).
    .
    Per il resto dovrei essere in grado, in questo progetto utilizzo già HashMap in alcune occasioni. È la prima volta che lavoro su file (tanta teoria inutile e poca pratica), e il fatto di aver salvato tutti i numeri nel formato 01.02.03 (per convenienza) mi ha portato a procedere utilizzando le stringhe, se i numeri erano nel formato 1.2.3.4 era un'altra storia.

    Ed è per questo che passo al metodo che utilizzo per iterare, il valore "0"+Integer.toString(1) ? nella versione migliorata [String n = String.format("%02d", numero)], perché confronto i valori scritti nel file.csv...

    Ad ogni modo, ripeto vedrò cosa riuscirò a concludere, in ogni caso adesso provo a testare qualcosa su Eclipse e ti ringrazio di tutto.
  • Re: Confronto stringhe performante

Devi accedere o registrarti per scrivere nel forum
10 risposte