Newsgroup: it.comp.java
Titolo: Funzioni associate a stringhe

Salve,
volendo eseguire una funzione associata ad una stringa fra tante (es. un 
ActionEvent) esistono soluzioni comode da mantenere, più efficienti di 
una sequenza di if-else ?
Ha senso creare una  TreeMap<String, java.lang.reflect.Method>  ?
-- 
Enos

 
Dal punto di vista delle performance sicuramente no. Se non lo fai tu 
l'if, lo fa il Map quando vai a fare la get, poi ci aggiungi lo stress 
della reflection che non e' mai morbidissimo, passando anche per 
l'appesantimento dei try/catch inevitabili quando invochi il Method.
Anche dal punto di vista del codice, non e' che si snellisca molto, 
dovrai pur sempre riempirlo il tuo Map.

 Se non lo fai tu l'if, lo fa il Map quando vai a fare la get
ma non ho capito questa parte; che intendi che "l'if lo fa il map"?
-- 
By ZeD

 dovrai pur sempre riempirlo il tuo Map.
Lo riempio una volta sola all'avvio del programma, ma lo consulto 
svariate volte durante l'esecuzione.
-- 
Enos

 una sequenza di if-else ?
Ciao,
su stack overflow c'è una discussione che parla proprio di performance
nella reflection:
http://stackoverflow.com/questions/435553/java-reflection-performance
Molto interessante, la reflection è senz'alcun dubbio più lenta che
instanziare in modo tradizionale una classe o un metodo e il motivo è
semplicemente legato ai controlli sui tipi di dato.
Mi viene però in mente una splendida nota letta non ricordo quando su
questo NG, legata all'ottimizzazione del codice:
"Prima si scrive un programma robusto e leggibile, poi, quando servono
performance, si ottimizza.
Se fai il contrario non è detto che tu riesca a rimetterci le mani in
fase successive."
Ho spesso pagato a mie spese l'inosservanza di questo "precetto" che
ormai ritengo fondamentale.
Non conosco il tuo programma, ma può darsi che la "lentezza" della
reflection non influisca in alcun modo sulla tua applicazione.
Te ne accorgi anche dal benchmark che ha fatto YUVAL su stackoverflow:
senza reflaction ci vogliono 15 ms
con la reflection invece 1781.
Ma ha utilizzato 1.000.000 di iterazioni per avere quel numero.
Se fai il conto del gap, per ogni iterazione, il risultato è
assolutamente impercettibile per un essere umano:
(1781-15) / 1000000 = 0,001766
Insomma, ammesso e non concesso che la velocità sia in questo caso
marginale, il mio consiglio è prediligere sempre un buon codice
leggibile.
Se poi risulta lento, trovi una soluzione.
Ciao, Davide.


il 03.09.2010 6.45, Scrive Vito 'ZeD' De Tullio 42166848:

Sottolineavo che risparmiando l'if nella tua classe utilizzando il 
TreeMap in realtà, in termini di performance non ci guadagni, ma anzi ci 
perdi.
Dato un TreeMap<String, Method> quando cerchi il metodo associato alla 
stringa utilizzi il metodo get() del TreeMap (o qualsiasi altra classe 
che implementa Map)
L'HashMap fa cosi':
     public V get(Object key) {
         if (key == null)
             return getForNullKey();
         int hash = hash(key.hashCode());
         for (Entry<K,V> e = table[indexFor(hash, table.length)];
              e != null;
              e = e.next) {
             Object k;
             if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                 return e.value;
         }
         return null;
     }
Il TreeMap e' un po' piu' complesso ma grosso modo si comporta allo 
stesso modo:
     public V get(Object key) {
         Entry<K,V> p = getEntry(key);
         return (p==null ? null : p.value);
     }
     final Entry<K,V> getEntry(Object key) {
         // Offload comparator-based version for sake of performance
         if (comparator != null)
             return getEntryUsingComparator(key);
         if (key == null)
             throw new NullPointerException();
	Comparable<? super K> k = (Comparable<? super K>) key;
         Entry<K,V> p = root;
         while (p != null) {
             int cmp = k.compareTo(p.key);
             if (cmp < 0)
                 p = p.left;
             else if (cmp > 0)
                 p = p.right;
             else
                 return p;
         }
         return null;
     }
Come vedi, puoi effettivamente risparmiare un if/else if/, ma lo fai 
fare alla classe TreeMap e anche di piu'.

 assolutamente impercettibile per un essere umano:
Senza dubbio, sempre che stiamo parlando di un pc con il suo unico bravo 
utente che pesta, se invece si trattasse di un server e gli utenti 
fossero molti, e magari anche laboriosi, la faccenda cambia un po'.
Sono comunque d'accordo che la reflection c'e' e quando merita vada 
anche usata.
(che dio la benedica) :-))

 efficente,
io direi proprio di no. una hashmap garantisce un constant-time per la
get, l'indexOf almeno nelle implementazioni standard non mi risulta.

 fare alla classe TreeMap e anche di piu'.
non direi proprio.
se osservi bene i due metodi della map usano una ricerca indicizzata,
il primo sugli hash e il secondo direttamente sulle key (che sono già
in ordine naturale). il primo in particolare usa gli hashcode interi
che rispetto alle stringhe sono confrontabili in modo molto più
efficiente. è chiaro che se hai poche stringhe da testare le ricerche
indicizzate sono meno adatte, però è altrettanto vero che su pochi
dati non hai problemi di performance.

il 03.09.2010 14.49, Scrive rootkit 65519168:

A parte che un HashMap non garantisce un constant-time, ma e' in grado 
di fornirlo solo se la funzione di hash riesce a distribuire 
uniformemente gli elementi nel suo contenitore, e' ovvio che se togli la 
condizione dell'asserto l'aspetto cambia. La frase che hai quotato 
continuava dicendo che le entries non dovevano essere molte, in quanto, 
essendo l'elenco di metodi di una classe, ho immaginato che dovessero 
essere dell'ordine di qualche decina, non centinaia o migliaia. Inoltre 
constant-time significa che ci mette sempre lo stesso tempo, mentre a 
fare indexOf di un elemento di lista il tempo e' si' proporzionale 
all'indice trovato, ma per il primo indice il tempo impiegato puo' 
essere di molto inferiore al constant-time di cui sopra. Poiche' anche 
per un HashMap il constant-time atteso, e' comunque proporzionale alla 
sua capacita' (lo dice anche la documentazione) e poiche' anche il get 
di un HashMap alla fine fa l'iterazione sul suo array di Entry, e' 
ragionevole pensare che il constant-time sia grosso modo equivalente 
alla media dei tempi di accesso agli elementi di un List di pari 
capacita'. E comunque, per piccoli numeri di elementi, immagino che le 
differenze siano ininfluenti.
Considerando che la lista puo' essere costruita in modo ottimizzato, 
ovvero inserendo per primi gli elementi che il programmatore valuta come 
più probabili, probabilmente un costrutto siffatto sara' ragionevolmente 
piu' efficiente rispetto all'utilizzo di un HashMap in cui gli elementi 
sono messi alla rinfusa.

il 03.09.2010 15.53, Scrive rootkit 65519168:

Beh, direi che questo if, oltre a fare il confronto tra gli hash, fa 
anche il contronto delle key, o no?
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                 return e.value;
Trattandosi che l'OP deve valutare la stringa ricevuta come argomento di 
un qualche metodo, immagino che il confronto (k = e.key) == key sia true 
solo in circostanze molto particolari, (se e.key e key sono la stessa 
istanza), e quindi per ritornare il e.value si debba eseguire anche 
key.equals(k), che e' quello che farebbe nell'if risparmiato di cui si 
parla. O mi sono perso qualche pezzo per la strada?

 sono messi alla rinfusa.
L'unico utilizzo di quella "porcheria" che vedo è quello dove si 
sviluppa un piccolo framework in casa e bisogna configurare facendo 
riferimento a dei metodi generici. In questa situazione significa che ci 
si deve ricordare che i metodi definiti prima (nel XML?) hanno una 
priorità maggiore. Per carità.
Altri utilizzi non ne vedo, anzi non ne voglio vedere ;)

 Se poi risulta lento, trovi una soluzione.
Si, concordo, anche con quello che dice a riguardo Ugo Gagliardelli.
Ciao
-- 
Enos

 key.equals(k) [...]
Lo si esegue solo poche volte, per le poche (magari una sola) chiavi che 
condividono la stessa hash; la sequenza di if...else eseguirebbe equals() 
una media di length/2 volte.
A proposito di strutture di dati... per chi capisce l'inglese parlato, un 
ottimo corso su Java e le strutture di dati avanzate:
http://www.youtube.com/watch?v=QMV45tHCYNI
-- 
Enos

 
http://www.youtube.com/watch?v=QMV45tHCYNI
Spettacolo, ma vi rendete conto in che periodo storico viviamo?
In cui in un università dall'altro lato del pianeta, un uomo spiega
allo stato dell'arte le strutture dati e noi, all'occorrenza,
consultiamo gratuitamente, anche dal cellulare, in metropolitana,
dalla spiaggia...
E' strabiliante.
Scusate l'OT, ma non riuscirò mai ad abituarmici...

 (che dio la benedica) :-))
Assolutamente, se non ci fosse, tutti noi saremmo già passati ad un
altro linguaggio.
Scusate l'enfasi, alle volte mi lascio un po' prendere :-D
Ciao, Davide.

 altro linguaggio.
e non avrebbe mai sfondato proprio in ambito web.

 e non avrebbe mai sfondato proprio in ambito web.
Ciao, Davide

 una sequenza di if-else ?
Ho trovato quello che cercavo.
Nel caso interessi a qualcun altro:
public class TestStringEvaluation {
private enum MyActionEnum {
  AZIONE1, AZIONE2, AZIONE3, NESSUNA_AZIONE;
  public static MyActionEnum parseAction(String actionName) {
    try {
      return MyActionEnum.valueOf(actionName); 
    } catch (IllegalArgumentException e) {
      return NESSUNA_AZIONE; 
    }
  }
}
public static void main(String[] args) {
  String myActionName = "AZIsONE2";
  switch (MyActionEnum.parseAction( myActionName ))	{
    case AZIONE1:
      System.out.println("Eseguita azione 1");
      break;
    case AZIONE2:
      System.out.println("Eseguita azione 2");
      break;
    case AZIONE3:
      System.out.println("Eseguita azione 3");
      break;
    case NESSUNA_AZIONE:
      System.out.println("Azione non catturata");
      break;
  }
}
}
Saluti
-- 
Enos