• Non ci sono risultati.

Dato 1 Dato 2 Dato 3 Dato 4 Dato 5 Valore 0,146766 0,143082 0,146158 0,143957 0,14

3.4.2 Test sugli iterator

La seconda problematica su cui si è lavorato riguarda i cicli for, in cui potrebbe essere conveniente utilizzare degli iteratori.

Gli iteratori permettono di valutare gli elementi presenti in un contenitore, come un vettore o una lista, indipendentemente dal tipo di oggetto presente. Il meccanismo di iterazione si preoccupa di ricordare quali elementi sono già stati esaminati e quale il prossimo.

Per poter utilizzare un iteratore sono necessari questi passi:

 Implementare l’interfaccia Iterator, che comprende la definizione di alcuni metodi (di solito l’implementazione avviene utilizzando le inner classes, ovvero viene definita all’interno della classe del contenitore):

48

boolean hasnext(), che ritorna true se esistono ancora elementi da visitare;

Object next(), che ritorna l’elemento successivo;

void remove(), che rimuove l’ultimo elemento ritornato dall’iteratore.

 Definire e implementare nel tipo contenitore un metodo che restituisce un iteratore.

Per effettuare i test si è utilizzato lo stesso programma implementato per il test sullo switch, ma chiaramente sostituendo il ciclo for con un iteratore.

Per maggiore chiarezza verranno riportati i pezzi di codice che riguardano l’implementazione dell’iteratore.

Classe POInfo:

public class POInfo {

Vector<POInfoColumn> v = new Vector<POInfoColumn>();

/**

* Costruttore Classe POInfo */

public POInfo () {

for (int i = 0; i < 10; i++) {…}

} /**

* Implementazione dell’interfaccia Iterator */

class VIterator implements Iterator<Object> {

private int i;

VIterator() {

i = 0;

}

public boolean hasNext() {

return i < v.size(); }

49

public Object next() {

if (i < v.size()) {

i++;

return v.get(i - 1);

} else

throw new NoSuchElementException(""); }

public void remove() { }

} /**

* Metodo che restituisce un nuovo iteratore */

public Iterator<?> iterator() {

return new VIterator(); }

}

Metodo load della classe PO:

public void load() {

Iterator<?> it = p_info.v.iterator();

while (it.hasNext()) {…} }

I test si sono svolti su quattro versioni del programma utilizzato per lo switch, provando tutte le combinazioni possibili, ovvero:

Metodo load con ciclo for e all’interno una serie di if concatenati.

Metodo load con ciclo for e all’interno uno switch.

Metodo load con iteratore e all’interno una serie di if concatenati.

50

I primi due sono una copia di quelli utilizzati nei precedenti test, ma sono stati presi in considerazione per effettuare un confronto con le versioni con l’iteratore.

Si è poi aumentato il numero di oggetti presenti nei vettori o array in modo da valutare l’andamento dei tempi di esecuzione in relazione al numero di iterazioni. Per l’analisi dei dati si è seguita la stessa procedura riportata per i test precedenti, ovvero media su cinque valori e controllo della confidenza.

3.4.3 Test sulle stringhe: StringBuilder e StringBuffer

Analizzando il codice del metodo convertStatement presente nella classe Convert si è notato un utilizzo molto intensivo delle stringhe. Le operazioni tipiche che si possono trovare riguardano la creazione di sottostringhe, ricerca di caratteri e concatenamenti. Ogni singola operazione potrebbe non essere così impattante sul tempo di esecuzione, ma dato che le stringhe vengono comunemente usate in quasi tutte le classi, anche un piccolo miglioramento potrebbe portare un contributo significativo in ottica globale.

Viene ora riportata una parte del codice del metodo convertStatement con la relativa analisi. 1 2 3 4 5 6 7 8 9 10 11 12

protected ArrayList<String> convertStatement(String sqlStatement) {

int found_next_function =

sqlStatement.toUpperCase(). indexOf("NEXTIDFUNC(");

if (found_next_function <= 0) found_next_function

= sqlStatement.toUpperCase().indexOf("NEXTID(");

if (found_next_function > 0) {

boolean SYSTEM_NATIVE_SEQUENCE = true;

boolean adempiereSys = false;

if (SYSTEM_NATIVE_SEQUENCE && !adempiereSys) {

String function_before = sqlStatement.substring(0,

51 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 found_next_function);

String function_start = sqlStatement.substring( found_next_function);

String function_after = function_start.substring

(function_start.indexOf(")") + 1);

String sequence = function_start.substring(

function_start.indexOf("(") + 1,

function_start.indexOf(","));

int separator = function_start.indexOf("'") +1;

String next = function_start.substring( separator); String system = next.substring(0,next.indexOf("'")); } } … } 1 2

Il metodo riceve una stringa sqlStatement come parametro di ingresso.

3 4

Si ottiene la versione con tutti i caratteri maiuscoli della stringa e si cerca all’interno di essa la sottostringa “NEXTIDFUNC(“

5 6

Se non viene trovata si cerca la sottostringa “NEXTID(“

7 8 9

Se l’intero found_next_function è positivo (ovvero è stata trovata una delle due sottostringhe precedenti) si setta la variabile booleana SYSTEM_NATIVE_SEQUENCE al valore true, e la booleana adempiereSys a false.

10 Si verifica che le variabili booleane siano state settate correttamente. 11

12 13 14 15

Si estrae una sottostringa da sqlStatement dal primo carattere fino all’intero

found_next_function calcolato precedentemente, ovvero l’indice che segna l’inizio di

“NEXTID(“ o “NEXTIDFUNC(“. La nuova stringa viene chiamata function_before. Si estrae una sottostringa da sqlStatement dal carattere con indice uguale all’intero

52 16

17 18

Viene estratta una sottostringa da function_start, che inizia il carattere seguente la prima chiusura di parentesi tonda, fino alla fine. La nuova stringa viene chiamata

function_after.

19 20 21

Viene estratta una sottostringa da function_start, che inizia il carattere seguente la prima apertura di parentesi tonda, fino alla prima virgola trovata. La nuova stringa viene chiamata sequence.

22 23

Si cerca nella stringa function_start la posizione in cui è presente il primo apice. Al risultato viene sommato 1 e il valore salvato nell’intero separator.

24 25

Viene estratta una sottostringa da function_start, che inizia con il carattere indicato dall’intero separator, fino alla fine. La nuova stringa viene chiamata next.

26 27

Viene estratta una sottostringa da next, che inizia con il primo carattere e termina con il primo apice trovato. La nuova stringa viene chiamata system.

Per comprendere meglio il funzionamento di questo metodo viene riportato un esempio di esecuzione con i valori assunti dalle singole stringhe.

Ipotizziamo che il parametro sqlStatement in ingresso venga inizializzato in questo modo:

String sqlStatement = “wxyz NEXTIDFUNC(parametro 1, parametro 2) xxx ' yyy ' zzz”

int found_next_function =

sqlStatement.toUpperCase().indexOf(

"NEXTIDFUNC(");

if (SYSTEM_NATIVE_SEQUENCE && !adempiereSys)

{ String function_before = sqlStatement.substring (0,found_next_function); String function_start = found_next_function: 5 function_before: wxyz function_start:

53 sqlStatement.substring (found_next_function); String function_after = function_start.substring (function_start.indexOf(")") + 1); String sequence = function_start.substring (function_start.indexOf("(") + 1, function_start.indexOf(",")); int separator = function_start.indexOf("'") + 1; String next = function_start.substring(separator); String system = next.substring

(0,next.indexOf("'")); } NEXTIDFUNC(parame tro 1, parametro 2) xxx ' yyy ' zzz function_after: xxx ' yyy ' zzz sequence: parametro 1 separator: 42 next: yyy ' zzz system: yyy

Dato che le stringhe vengono ampiamente utilizzate e manipolate, si è cercato un modo per renderle più efficienti. Si è pensato di sostituire le stringhe con degli oggetti molto simili, ovvero gli oggetti StringBuilder e StringBuffer.

La differenza sostanziale è che le stringhe sono oggetti non modificabili; ciò comporta che se si avesse la necessità di concatenare dei caratteri, o ottenere una parte di stringa, si dovrebbe necessariamente creare una nuova stringa.

Come si vede nel codice di convertStatement, quando si ha bisogno di una sottostringa si deve necessariamente creare una nuova stringa.

54

Gli oggetti di tipo StringBuilder e StringBuffer invece possono essere modificati e mettono a disposizione alcuni metodi in grado di farlo, come il metodo append che permette di aggiungere caratteri in fondo alla stringa, oppure delete che permette la cancellazione di uno o più caratteri all’interno della stringa.

Vista la necessità di manipolare numerose stringhe si è pensato che avere a che fare con un oggetto più flessibile potesse migliorare le prestazioni.

La differenza tra i due tipi sta nel fatto che il tipo StringBuffer è thread-safe, ovvero garantisce affidabilità in presenza di più thread che potrebbero agire sull’oggetto. Per effettuare i test sono stati implementati 4 metodi convertStatement:

1. Convert1: il metodo originale visto in precedenza

2. Convert2: viene utilizzato il tipo StringBuilder al posto di string.

static String convert2(String sqlStatement) {

int found_next_function =

sqlStatement.toUpperCase().indexOf("NEXTIDFUNC("

);

StringBuilder function_before = new

StringBuilder (sqlStatement);

StringBuilder function_start = new

StringBuilder( sqlStatement);

function_before = function_before.delete

(found_next_function, function_before.length()); function_start = function_start.delete(0,

found_next_function);

55

StringBuilder system = new

StringBuilder(function_start);

function_start = function_start.delete(0,

function_start.indexOf(")") + 1);

system = system.delete(0, separator);

system = system.delete(system.indexOf("'"),

system.length());

return system.toString(); }

Le operazioni di substring sono state sostituite con il metodo delete, per eliminare i caratteri in eccesso.

3. Convert3: viene utilizzato il tipo StringBuffer al posto di string. Il codice e le operazioni sono le stesse di Convert2.

4. Convert4: viene utilizzato il tipo StringBuffer al posto di string ed inoltre gli oggetti di tipo StringBuffer vengono inizializzati con la preallocazione dello spazio necessario.

static String convert4(String sqlStatement) {

int found_next_function =

sqlStatement.toUpperCase().indexOf(

"NEXTIDFUNC(");

StringBuffer function_before = new StringBuffer

(sqlStatement.length()).append(sqlStatement);

56 (sqlStatement.length()).append(sqlStatement); function_before = function_before.delete (found_next_function, function_before.length()); function_start = function_start.delete(0, found_next_function);

int separator = function_start.indexOf("'") + 1;

StringBuffer system = new StringBuffer

(function_start.length()).append(function_start) ;

function_start = function_start.delete

(0,function_start.indexOf(")") + 1);

system = system.delete(0, separator);

system = system.delete(system.indexOf("'"),

system.length());

return system.toString(); }

Da notare che nel metodo originario vengono create le stringhe ricalcando la gerarchia di Figura 3.7. Le stringhe segnate con uno sfondo blu sono quelle che realmente vengono utilizzate, mentre le altre sono solo come passaggio per ulteriori manipolazioni. Per questo motivo le stringhe inutili non sono state create negli altri metodi Convert.

57

Figura 3.7 - Gerarchia stringhe utilizzate nel metodo convert

I test effettuati hanno ricalcato la stessa procedura descritta in precedenza, quindi 5 prove per ogni metodo, con controllo della confidenza.

Documenti correlati