Discussione:
ottimizzare ciclo in java
(troppo vecchio per rispondere)
andrea__
2008-06-04 09:15:42 UTC
Permalink
sono alle prime armi con java, ma non con la programmazione, ho un dubbio
per cui chiedo a voi che siete più esperti di me...

data una lista di oggetti, in questo caso una lista di stringhe,
supponiamo che io debba ciclare all'interno della lista per stamparne a
video gli elementi, lasciando perdere i generics che non sono il motivo
della domanda, il codice sarebbe questo:

List list=new ArrayList();
list.add("pippo");
list.add("pluto");
list.add("paperino");
for(Iterator itr=list.iterator();itr.hasNext()) {
String str=(String)itr.next();
System.out.println(str);
}

perchè l'oggetto str viene dichiarato e assegnato ad ogni iterazione del
ciclo for? non è meglio dichiaralo esternamente al ciclo in modo che ad
ogni iterazione avvenga solo l'assegnazione dell'oggetto? in tutti gli
esempi che ho visto finora, sto studiando java in vari libri, ho visto
fare come nell'esempio che vi ho riportato... Vengo dal C e questo modo di
iterare nei cicli mi da l'idea di poca ottimizzazione... magari c'è un
motivo valido, chi me lo spiega?
--
questo articolo e` stato inviato via web dal servizio gratuito
http://www.newsland.it/news segnala gli abusi ad ***@newsland.it
Davide Consonni
2008-06-04 09:19:04 UTC
Permalink
Post by andrea__
List list=new ArrayList();
list.add("pippo");
list.add("pluto");
list.add("paperino");
for(Iterator itr=list.iterator();itr.hasNext()) {
String str=(String)itr.next();
System.out.println(str);
}
List list=new ArrayList();
list.add("pippo");
list.add("pluto");
list.add("paperino");
Iterator it = list.iterator();
while (it.hasNext()) {
String str=(String)itr.next();
System.out.println(str);
}
--
Davide Consonni
Davide Consonni
2008-06-04 09:20:38 UTC
Permalink
Davide Consonni wrote:

ehm errata:

      String str=(String)itr.next(); diventa it quindi:

List list=new ArrayList();
list.add("pippo");
list.add("pluto");
list.add("paperino");
Iterator it = list.iterator();
while (it.hasNext()) {
String str=(String)it.next();
System.out.println(str);
}

poi adesso si usano i generics, cosi eviti anche il cast esplicito in
stringa
--
Davide Consonni
andrea__
2008-06-04 09:36:05 UTC
Permalink
Post by andrea__
List list=new ArrayList();
list.add("pippo");
list.add("pluto");
list.add("paperino");
Iterator it = list.iterator();
while (it.hasNext()) {
String str=(String)it.next();
System.out.println(str);
}
poi adesso si usano i generics, cosi eviti anche il cast esplicito in
stringa
forse non mi sono spiegato bene... che il ciclo sia un while piuttosto che
un for o che si usino i generics o meno, la domanda resta: perchè ad ogni
iterazione devo ridichiarare l'oggetto str? non è più ottimizzato
dichiararlo all'esterno del ciclo? perchè sta pratica è diffusa in tutti i
testi che sto leggendo? non è più corretto fare così?

List list=new ArrayList();
list.add("pippo");
list.add("pluto");
list.add("paperino");
Iterator it = list.iterator();
String str;
while (it.hasNext()) {
str=(String)it.next();
System.out.println(str);
}
--
questo articolo e` stato inviato via web dal servizio gratuito
http://www.newsland.it/news segnala gli abusi ad ***@newsland.it
Davide Consonni
2008-06-04 09:42:42 UTC
Permalink
Post by andrea__
List list=new ArrayList();
list.add("pippo");
list.add("pluto");
list.add("paperino");
Iterator it = list.iterator();
String str;
while (it.hasNext()) {
str=(String)it.next();
System.out.println(str);
}
non credo ci sia differenza, visto che le stringhe in java sono immutabili
quindi ad ogni iterazione str conterrà cmq una nuova istanza.
del resto se ne occupa il gc.
--
Davide Consonni
Andrea Francia
2008-06-04 09:56:33 UTC
Permalink
Post by Davide Consonni
Post by andrea__
List list=new ArrayList();
list.add("pippo");
list.add("pluto");
list.add("paperino");
Iterator it = list.iterator();
String str;
while (it.hasNext()) {
str=(String)it.next();
System.out.println(str);
}
non credo ci sia differenza, visto che le stringhe in java sono immutabili
quindi ad ogni iterazione str conterrà cmq una nuova istanza.
No. L'unico che potrebbe allocare la stringa è it.next() ma non lo fa.
Semplicemente ogni volta restituisce un puntatore alla stringa
successiva senza allocare nient'altro.
Post by Davide Consonni
del resto se ne occupa il gc.
--
Andrea Francia
http://www.andreafrancia.it/
Giordano
2008-06-04 09:51:05 UTC
Permalink
Post by andrea__
non è più corretto fare così?
List list=new ArrayList();
list.add("pippo");
list.add("pluto");
list.add("paperino");
Iterator it = list.iterator();
String str;
while (it.hasNext()) {
        str=(String)it.next();
        System.out.println(str);
}
Giusto, correttissimo, ma se hai migliaia, anzi no... di più, migliaia
di migliaia di elementi nella tua list allora <<inizia>> a diventare
un po' pesante perchè....
...[ segue citazione da http://www.mondoinformatico.info/ottimizzare-il-codice-in-java_post-312.html]
<< Siccome gli oggetti String sono immutabili, questo fa si che ogni
volta si crei un nuovo oggetto e si lasci l’altro per il << Garbage
Collector. Questo implica un notevole spreco di tempo, che si può
evitare utilizzando StringBuffer, poiché << questo permette la
modifica degli oggetti rappresentati.

Quindi, per 3, 100 o 1000 elementi nella tua list non ti preoccupare
se fai uso del tipo String; invece se hai molti più elementi allora ti
conviene usare il tipo StringBuffer
andrea__
2008-06-04 09:56:35 UTC
Permalink
Post by Giordano
Giusto, correttissimo, ma se hai migliaia, anzi no... di più, migliaia
di migliaia di elementi nella tua list allora <<inizia>> a diventare
un po' pesante perchè....
...[ segue citazione da
http://www.mondoinformatico.info/ottimizzare-il-codice-in-java_post-312.html]
Post by Giordano
<< Siccome gli oggetti String sono immutabili, questo fa si che ogni
volta si crei un nuovo oggetto e si lasci l’altro per il << Garbage
Collector. Questo implica un notevole spreco di tempo, che si può
evitare utilizzando StringBuffer, poiché << questo permette la
modifica degli oggetti rappresentati.
Quindi, per 3, 100 o 1000 elementi nella tua list non ti preoccupare
se fai uso del tipo String; invece se hai molti più elementi allora ti
conviene usare il tipo StringBuffer
grazie ora è più chiaro, comunque al posto di String poteva esserci
qualsiasi altro oggetto, in tal caso mi sembra di aver capito che è meglio
dichiarare l'oggetto all'esterno del ciclo
--
questo articolo e` stato inviato via web dal servizio gratuito
http://www.newsland.it/news segnala gli abusi ad ***@newsland.it
Andrea Francia
2008-06-04 09:58:54 UTC
Permalink
Post by Giordano
Post by Giordano
Giusto, correttissimo, ma se hai migliaia, anzi no... di più, migliaia
di migliaia di elementi nella tua list allora <<inizia>> a diventare
un po' pesante perchè....
...[ segue citazione da
http://www.mondoinformatico.info/ottimizzare-il-codice-in-java_post-312.html]
Post by Giordano
<< Siccome gli oggetti String sono immutabili, questo fa si che ogni
volta si crei un nuovo oggetto e si lasci l’altro per il << Garbage
Collector. Questo implica un notevole spreco di tempo, che si può
evitare utilizzando StringBuffer, poiché << questo permette la
modifica degli oggetti rappresentati.
Quindi, per 3, 100 o 1000 elementi nella tua list non ti preoccupare
se fai uso del tipo String; invece se hai molti più elementi allora ti
conviene usare il tipo StringBuffer
grazie ora è più chiaro, comunque al posto di String poteva esserci
qualsiasi altro oggetto, in tal caso mi sembra di aver capito che è meglio
dichiarare l'oggetto all'esterno del ciclo
Assolutamente no, vedi il mio post.
Non stai dichiarando l'oggetto. Ma una variabile da 32 bit che contiene
un riferimento all'oggetto.
--
Andrea Francia
http://www.andreafrancia.it/
Andrea Laforgia
2008-06-04 10:50:21 UTC
Permalink
Post by Andrea Francia
Non stai dichiarando l'oggetto. Ma una variabile da 32 bit che contiene
un riferimento all'oggetto.
Non è così. Se è vero che le stringhe sono immutabili, allora stai
istanziando comunque e sempre un nuovo oggetto. Piuttosto, mi viene da
chiedermi se anche spostando fuori dal ciclo la dichiarazione della
variabile, le cose cambino.
La risposta è presumibilmente no, vista proprio l'immutabilità.
--
questo articolo e` stato inviato via web dal servizio gratuito
http://www.newsland.it/news segnala gli abusi ad ***@newsland.it
Andrea Francia
2008-06-04 11:08:54 UTC
Permalink
Post by Andrea Laforgia
Post by Andrea Francia
Non stai dichiarando l'oggetto. Ma una variabile da 32 bit che contiene
un riferimento all'oggetto.
Non è così. Se è vero che le stringhe sono immutabili, allora stai
istanziando comunque e sempre un nuovo oggetto.
Mi stai dicendo che se faccio :
1 String a = "prova";
2 String b = a;

Alla riga 2 sto istanziando un nuovo oggetto? Se è così non sono d'accordo.
Post by Andrea Laforgia
Piuttosto, mi viene da
chiedermi se anche spostando fuori dal ciclo la dichiarazione della
variabile, le cose cambino.
Da che punto di vista cambiano? Velocità o che altro?
Post by Andrea Laforgia
La risposta è presumibilmente no, vista proprio l'immutabilità.
--
Andrea Francia
http://www.andreafrancia.it/
Davide Consonni
2008-06-04 11:10:38 UTC
Permalink
Post by Andrea Francia
Post by Andrea Laforgia
Non è così. Se è vero che le stringhe sono immutabili, allora stai
istanziando comunque e sempre un nuovo oggetto.
1 String a = "prova";
2 String b = a;
Alla riga 2 sto istanziando un nuovo oggetto? Se è così non sono d'accordo.
no alla riga due hai aggiunto un reference ad un'oggetto
--
Davide Consonni
Andrea Francia
2008-06-04 11:14:22 UTC
Permalink
Post by Davide Consonni
Post by Andrea Francia
Post by Andrea Laforgia
Non è così. Se è vero che le stringhe sono immutabili, allora stai
istanziando comunque e sempre un nuovo oggetto.
1 String a = "prova";
2 String b = a;
Alla riga 2 sto istanziando un nuovo oggetto? Se è così non sono d'accordo.
no alla riga due hai aggiunto un reference ad un'oggetto
Allora perché nel tuo post hai detto la cosa seguente?
Post by Davide Consonni
quindi ad ogni iterazione str conterrà cmq una nuova istanza.
del resto se ne occupa il gc.
--
Andrea Francia
http://www.andreafrancia.it/
Davide Consonni
2008-06-04 11:19:10 UTC
Permalink
Post by Andrea Francia
Post by Davide Consonni
Post by Andrea Francia
Post by Andrea Laforgia
Non è così. Se è vero che le stringhe sono immutabili, allora stai
istanziando comunque e sempre un nuovo oggetto.
1 String a = "prova";
2 String b = a;
Alla riga 2 sto istanziando un nuovo oggetto? Se è così non sono d'accordo.
no alla riga due hai aggiunto un reference ad un'oggetto
Allora perché nel tuo post hai detto la cosa seguente?
Post by Davide Consonni
quindi ad ogni iterazione str conterrà cmq una nuova istanza.
del resto se ne occupa il gc.
perchè sei tu fai
1: String a;
2: a = "uno";
3: a = "due";

hai creato due istanze di stringa.
poi entra in gioco il gc a pulire quello che deve pulire. quando arrivi alla
riga 3 la stringa "uno" è eliggibile per il gc
--
Davide Consonni
Andrea Francia
2008-06-04 11:51:09 UTC
Permalink
Post by Davide Consonni
Post by Andrea Francia
Post by Davide Consonni
Post by Andrea Francia
Post by Andrea Laforgia
Non è così. Se è vero che le stringhe sono immutabili, allora stai
istanziando comunque e sempre un nuovo oggetto.
1 String a = "prova";
2 String b = a;
Alla riga 2 sto istanziando un nuovo oggetto? Se è così non sono d'accordo.
no alla riga due hai aggiunto un reference ad un'oggetto
Allora perché nel tuo post hai detto la cosa seguente?
Post by Davide Consonni
quindi ad ogni iterazione str conterrà cmq una nuova istanza.
del resto se ne occupa il gc.
perchè sei tu fai
1: String a;
2: a = "uno";
3: a = "due";
hai creato due istanze di stringa.
poi entra in gioco il gc a pulire quello che deve pulire. quando arrivi alla
riga 3 la stringa "uno" è eliggibile per il gc
Pero' in

List list=new ArrayList();
list.add("pippo");
list.add("pluto");
list.add("paperino");
for(Iterator itr=list.iterator();itr.hasNext()) {
String str=(String)itr.next();
System.out.println(str);
}

Il GC non entra in gioco nel for.
Perché non vengono create nuove istanze (l'istanza restituita da
itr.next() è comunque la stessa che c'e' dentro la lista) e alla fine
del corpo del ciclo cmq le istanze rimangono referenziate almeno dalla
lista.

O no?
--
Andrea Francia
http://www.andreafrancia.it/
Davide Consonni
2008-06-04 12:22:01 UTC
Permalink
Post by Andrea Francia
Pero' in
List list=new ArrayList();
list.add("pippo");
list.add("pluto");
list.add("paperino");
for(Iterator itr=list.iterator();itr.hasNext()) {
String str=(String)itr.next();
System.out.println(str);
}
Il GC non entra in gioco nel for.
Perché non vengono create nuove istanze (l'istanza restituita da
itr.next() è comunque la stessa che c'e' dentro la lista) e alla fine
del corpo del ciclo cmq le istanze rimangono referenziate almeno dalla
lista.
O no?
in questo caso tu hai già le istanze delle stringhe nella lista e nel for
riassegni di vola in volta il reference alla stringa alla variabile str ma
qui non stai creando nuove istanze

infatti se esegui questo codice:

List list = new ArrayList();
list.add("pippo");
list.add("pluto");
list.add("paperino");
for (Iterator itr = list.iterator(); itr.hasNext();) {
Object obj = itr.next();
System.out.println(obj.hashCode());
String str = (String) obj;
System.out.println(str.hashCode());
System.out.println(str.equals(obj));
}

otterrai questo risultato:

106673622
106673622
true
106767924
106767924
true
1118014174
1118014174
true

in questo caso stiamo parlando solo di assegnazione di references. non si
sta creando nessuna nuova stringa.
--
Davide Consonni
Andrea Francia
2008-06-04 12:24:53 UTC
Permalink
Post by Davide Consonni
Post by Andrea Francia
Pero' in
List list=new ArrayList();
list.add("pippo");
list.add("pluto");
list.add("paperino");
for(Iterator itr=list.iterator();itr.hasNext()) {
String str=(String)itr.next();
System.out.println(str);
}
Il GC non entra in gioco nel for.
Perché non vengono create nuove istanze (l'istanza restituita da
itr.next() è comunque la stessa che c'e' dentro la lista) e alla fine
del corpo del ciclo cmq le istanze rimangono referenziate almeno dalla
lista.
O no?
in questo caso tu hai già le istanze delle stringhe nella lista e nel for
riassegni di vola in volta il reference alla stringa alla variabile str ma
qui non stai creando nuove istanze
List list = new ArrayList();
list.add("pippo");
list.add("pluto");
list.add("paperino");
for (Iterator itr = list.iterator(); itr.hasNext();) {
Object obj = itr.next();
System.out.println(obj.hashCode());
String str = (String) obj;
System.out.println(str.hashCode());
System.out.println(str.equals(obj));
}
106673622
106673622
true
106767924
106767924
true
1118014174
1118014174
true
in questo caso stiamo parlando solo di assegnazione di references. non si
sta creando nessuna nuova stringa.
Ok siamo d'accordo.
--
Andrea Francia
http://www.andreafrancia.it/
Davide Consonni
2008-06-04 12:26:07 UTC
Permalink
Davide Consonni wrote:

aggiungo che cambiando il codice in:

List list = new ArrayList();
String s = null;
list.add("pippo");
list.add("pluto");
list.add("paperino");
for (Iterator itr = list.iterator(); itr.hasNext();) {
Object obj = itr.next();
System.out.println(obj.hashCode());
s = (String) obj;
System.out.println(s.hashCode());
System.out.println(s.equals(obj));
}

quindi con la stringa esterna al ciclo ottieni sempre
106673622
106673622
true
106767924
106767924
true
1118014174
1118014174
true

e anche qui non si stanno allocando nuove stringhe, ma si riassegna
semplicemente il reference a "s".
--
Davide Consonni
Lucio Benfante
2008-06-04 12:18:58 UTC
Permalink
Post by Davide Consonni
perchè sei tu fai
1: String a;
2: a = "uno";
3: a = "due";
hai creato due istanze di stringa.
poi entra in gioco il gc a pulire quello che deve pulire. quando arrivi alla
riga 3 la stringa "uno" è eliggibile per il gc
A dirla proprio tutta non è esattamente vero. Di solito i literal String
vengono "riciclati":

String a, b;
a = "uno";
a = "due";
b = "uno";

if (a == b) System.out.println("Sono la stessa istanza");

Quindi alla seconda assegnazione di "uno" non corrisponde la creazione
di un oggetto String...ma probabilmente neanche alla prima, dato che
durante la vita del programma quel literal potrebbe già essere stato
usato e quindi l'oggetto corrispondente essere già stato costruito.
--
Lucio Benfante
JUG Padova http://www.parancoe.org ...have a look at it!
www.jugpadova.it http://www.jugevents.org
Davide Consonni
2008-06-04 12:28:54 UTC
Permalink
Post by Lucio Benfante
A dirla proprio tutta non è esattamente vero. Di solito i literal String
String a, b;
a = "uno";
a = "due";
b = "uno";
if (a == b) System.out.println("Sono la stessa istanza");
Quindi alla seconda assegnazione di "uno" non corrisponde la creazione
di un oggetto String...ma probabilmente neanche alla prima, dato che
durante la vita del programma quel literal potrebbe già essere stato
usato e quindi l'oggetto corrispondente essere già stato costruito.
si ok, qui però stai parlando di caching delle stringhe che è
un'ottimizzazione della jvm. qui però non sono sicuro che *da specifiche*
java ti garantisca sempre lo stesso reference.
--
Davide Consonni
Andrea Francia
2008-06-04 13:09:29 UTC
Permalink
Post by Davide Consonni
Post by Lucio Benfante
A dirla proprio tutta non è esattamente vero. Di solito i literal String
String a, b;
a = "uno";
a = "due";
b = "uno";
if (a == b) System.out.println("Sono la stessa istanza");
Quindi alla seconda assegnazione di "uno" non corrisponde la creazione
di un oggetto String...ma probabilmente neanche alla prima, dato che
durante la vita del programma quel literal potrebbe già essere stato
usato e quindi l'oggetto corrispondente essere già stato costruito.
si ok, qui però stai parlando di caching delle stringhe che è
un'ottimizzazione della jvm.
Questa è un ottimizzazione del compilatore Java -> bytecode, non della JVM.
--
Andrea Francia
http://www.andreafrancia.it/
Davide Consonni
2008-06-04 13:11:36 UTC
Permalink
Post by Andrea Francia
Questa è un ottimizzazione del compilatore Java -> bytecode, non della JVM.
ma è una roba dipendente dall'implementazione o è da specifica ?
--
Davide Consonni
Andrea Francia
2008-06-04 13:18:10 UTC
Permalink
Post by Davide Consonni
Post by Andrea Francia
Questa è un ottimizzazione del compilatore Java -> bytecode, non della JVM.
ma è una roba dipendente dall'implementazione o è da specifica ?
Boh, io non lo so, so solo che se quando sta compilando trova delle
stringhe uguali aggiunge ne scrive solo una nel file .class.
--
Andrea Francia
http://www.andreafrancia.it/
Lucio Benfante
2008-06-04 13:19:34 UTC
Permalink
Post by Davide Consonni
Post by Andrea Francia
Questa è un ottimizzazione del compilatore Java -> bytecode, non della JVM.
ma è una roba dipendente dall'implementazione o è da specifica ?
Basta leggersi le specifiche, no? :)

# Literal strings within the same class (§8) in the same package (§7)
represent references to the same String object (§4.3.1).
# Literal strings within different classes in the same package represent
references to the same String object.
# Literal strings within different classes in different packages
likewise represent references to the same String object.

http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.10.5
--
Lucio Benfante
JUG Padova http://www.parancoe.org ...have a look at it!
www.jugpadova.it http://www.jugevents.org
Andrea Laforgia
2008-06-04 13:29:02 UTC
Permalink
Post by Andrea Francia
1 String a = "prova";
2 String b = a;
Alla riga 2 sto istanziando un nuovo oggetto? Se è così non sono d'accordo.
Non si tratta di "essere d'accordo". Se non è così, dimostramelo :-)
Molto probabilmente hai ragione tu e sto prendendo un abbaglio, anche
visto quello che dice Davide.

Casi come quello che riporti, comunque, non possono essere presi ad
esempio, perché in molti linguaggi (non so se Java si comporti alla stessa
maniera, ma in Delphi ad esempio è così), esiste il concetto di
copy-on-write: i due riferimenti all'inizio puntano alla stessa zona di
memoria e quando uno dei due viene modificato, viene effettuata la copia
del valore originale.

Il concetto è che, se b ha lo stesso valore (come riferimento) di a, nel
momento in cui modifico il contenuto di a che succede a b? dovrei trovar
modificato anche il contenuto di b, il che significa che la semantica è
errata...
--
questo articolo e` stato inviato via web dal servizio gratuito
http://www.newsland.it/news segnala gli abusi ad ***@newsland.it
Andrea Francia
2008-06-04 14:05:41 UTC
Permalink
Post by Andrea Laforgia
Post by Andrea Francia
1 String a = "prova";
2 String b = a;
Casi come quello che riporti, comunque, non possono essere presi ad
esempio, perché in molti linguaggi (non so se Java si comporti alla stessa
maniera, ma in Delphi ad esempio è così), esiste il concetto di
copy-on-write: i due riferimenti all'inizio puntano alla stessa zona di
memoria e quando uno dei due viene modificato, viene effettuata la copia
del valore originale.
In Java questo concetto non c'e'.
Post by Andrea Laforgia
Il concetto è che, se b ha lo stesso valore (come riferimento) di a, nel
momento in cui modifico il contenuto di a che succede a b? dovrei trovar
modificato anche il contenuto di b, il che significa che la semantica è
errata...
Che sia errato o no non è il caso di discuterne. Sta di fatto che Java
fa così. Per esempio:

class Pippo {
int value;
}

Pippo a = new Pippo();
a.value = 6;

Pippo b = a;

System.out.println(b.value); // stampa 6

a.value = 7;

System.out.println(b.value); // stampa 7
--
Andrea Francia
http://www.andreafrancia.it/
Andrea Laforgia
2008-06-04 14:20:03 UTC
Permalink
Post by Andrea Francia
Che sia errato o no non è il caso di discuterne.
Perché, decidi tu di cosa discutere o meno? :-)
In ogni caso...
Post by Andrea Francia
Pippo a = new Pippo();
a.value = 6;
Pippo b = a;
System.out.println(b.value); // stampa 6
a.value = 7;
System.out.println(b.value); // stampa 7
..questo è normale. Stai parlando di istanze di Pippo, non di stringhe.
Il concetto del copy-on-write vale SOLO per le stringhe nei linguaggi che
ho citato, non per i reference in generale (anche in Delphi, data la
classe Pippo, vale il discorso che fai, ma nel caso delle stringhe le cose
cambiano).
--
questo articolo e` stato inviato via web dal servizio gratuito
http://www.newsland.it/news segnala gli abusi ad ***@newsland.it
Andrea Francia
2008-06-04 15:26:54 UTC
Permalink
Post by Andrea Laforgia
Post by Andrea Francia
Che sia errato o no non è il caso di discuterne.
Perché, decidi tu di cosa discutere o meno? :-)
Vabbe' intendevo che secondo me non era il caso. Se qualcuno vuole
discuterne che lo faccia pure ...
Non mi va di discutere se i progettisti Sun hanno fatto bene o meno a
fare una certa cosa.
Post by Andrea Laforgia
In ogni caso...
Post by Andrea Francia
Pippo a = new Pippo();
a.value = 6;
Pippo b = a;
System.out.println(b.value); // stampa 6
a.value = 7;
System.out.println(b.value); // stampa 7
..questo è normale. Stai parlando di istanze di Pippo, non di stringhe.
Il concetto del copy-on-write vale SOLO per le stringhe nei linguaggi che
ho citato, non per i reference in generale (anche in Delphi, data la
classe Pippo, vale il discorso che fai, ma nel caso delle stringhe le cose
cambiano).
Ah ok, adesso ho imparato una cosa su Delphi.
Quindi in Java le stringhe sono immutable, mentre in Delphi sono
copyonwrite.

Due modi un po' diversi per ottenere di non sprecare memoria per le
stringhe duplicate e non sprecare tempo a copiarle se non serve.

Invece mi sembra che il Pascal si copiava tutti i 255 caratteri della
stringa.
--
Andrea Francia
http://www.andreafrancia.it/
Andrea Laforgia
2008-06-04 18:29:40 UTC
Permalink
On Wed, 04 Jun 2008 17:26:54 +0200, Andrea Francia
Post by Andrea Francia
Non mi va di discutere se i progettisti Sun hanno fatto bene o meno a
fare una certa cosa.
Be', i progettisti Sun ne sanno sicuramente molto più di me e di te
messi insieme (almeno per quanto riguarda me non ci vuole poi molto),
ma non sono di certo infallibili. Un design accurato è molto
difficoltoso da ottenere (a volte impossibile). Io credo (banalmente)
che più una libreria sia impostata sul concetto del "bisogna starci
attenti", più qualcosa si è perso nel design.
Post by Andrea Francia
Invece mi sembra che il Pascal si copiava tutti i 255 caratteri della
stringa.
In realtà Delphi supporta due modalità di gestione delle stringhe,
quella compatibile col vecchio Turbo Pascal e quella più simile alle
ASCIIZ del C. In realtà queste ultime, in Delphi, non sono
propriamente delle ASCIIZ, ma la lunghezza è comunque conservata in 4
byte a monte, oltre a informazioni per il reference counting (Delphi
implementa un banale meccanismo di garbage collection per le
stringhe).
Tommy
2008-06-04 19:32:00 UTC
Permalink
Post by Andrea Laforgia
Be', i progettisti Sun ne sanno sicuramente molto più di me e di te
messi insieme (almeno per quanto riguarda me non ci vuole poi molto),
ma non sono di certo infallibili. Un design accurato è molto
difficoltoso da ottenere (a volte impossibile). Io credo (banalmente)
che più una libreria sia impostata sul concetto del "bisogna starci
attenti", più qualcosa si è perso nel design.
Scusa, non capisco cosa ci sarebbe da stare attenti in Java. In Java hai la
garanzia che l'oggetto String non possa cambiare state, e' immutable. Questo
lo rende thread safe, lo rende sicuro, e rende tutto semplice (ma
inefficiente e quindi ecco perche' esiste StringBuilder).
Magari non ho capito cosa intendi.

Tommy
Andrea Laforgia
2008-06-05 08:40:21 UTC
Permalink
Post by Tommy
Scusa, non capisco cosa ci sarebbe da stare attenti in Java.
Mi riferisco alla risposta "bisogna starci attenti" che ho ricevuto.
--
questo articolo e` stato inviato via web dal servizio gratuito
http://www.newsland.it/news segnala gli abusi ad ***@newsland.it
Hole
2008-06-05 11:32:49 UTC
Permalink
Post by Andrea Laforgia
Post by Tommy
Scusa, non capisco cosa ci sarebbe da stare attenti in Java.
Mi riferisco alla risposta "bisogna starci attenti" che ho ricevuto.
Scusami, l'autore della risposta a cui ti riferisci sono io.
Era un modo "simpatico" per dire che un linguaggio, per essere
utilizzato al meglio, deve essere studiato per bene nei suoi concetti
base.

Un concetto basilare in Java è che le variabili sono tutte di tipo
reference (e vengono passate ai metodi come copia).
Se modifico un oggetto accedendo a questo tramite una sua variabile
reference, ogni altro accesso allo stesso oggetto (attraverso
qualsiasi altro reference) vedrà l'oggetto modificato.
Tommy
2008-06-05 21:28:56 UTC
Permalink
"Hole" <***@gmail.com> wrote

[quote]
Scusami, l'autore della risposta a cui ti riferisci sono io.
Era un modo "simpatico" per dire che un linguaggio, per essere
utilizzato al meglio, deve essere studiato per bene nei suoi concetti
base.

Un concetto basilare in Java è che le variabili sono tutte di tipo
reference (e vengono passate ai metodi come copia).
Se modifico un oggetto accedendo a questo tramite una sua variabile
reference, ogni altro accesso allo stesso oggetto (attraverso
qualsiasi altro reference) vedrà l'oggetto modificato.
[/quote]

verissimo ed e' per questo che in Java si tende a creare oggetti immutable,
cosi' questo rischio non accade.

Poi concordo, non piacciono nemmeno a me tante cose di Java. Per tante cose
Java e' comodo, veloce, facile, per altre, beh, rimpiango molto il C++.

Tommy
Hole
2008-06-06 13:55:16 UTC
Permalink
Post by Tommy
verissimo ed e' per questo che in Java si tende a creare oggetti immutable,
cosi' questo rischio non accade.
Poi concordo, non piacciono nemmeno a me tante cose di Java. Per tante cose
Java e' comodo, veloce, facile, per altre, beh, rimpiango molto il C++.
Sì, ci sono alcune cose di Java che, per favorire praticità o
compatibilità all'indietro, vanno a discapito di altre.
Non conosco benissimo C++ ma di sicuro la sua potenza ancora non è
stata raggiunta.
Ad esempio, l'introduzione dei Generics (template c++) sono molto
lontani dall'essere utili per un design riutilizzabile.
Andrea Francia
2008-06-06 14:46:53 UTC
Permalink
Post by Hole
Sì, ci sono alcune cose di Java che, per favorire praticità o
compatibilità all'indietro, vanno a discapito di altre.
Non conosco benissimo C++ ma di sicuro la sua potenza ancora non è
stata raggiunta.
Dipende da che potenza intendi. Potenza espressiva?
In questo caso dietro ci sono anche delle scelte progettuali di chi ha
pensato il linguaggio.

Per esempio alcune cose non le puoi fare in Java perché i progettisti
hanno applicato il principio di "salvare il programmatore da se stesso".

Il principio è discutibile ed è discusso, ma non intendo parlarne nel
merito perché se ne già parlato abbastanza.

L'unica cosa che volevo puntualizzare è il fatto che il Java non ha
raggiunto la potenza espressiva del C++ proprio perché i progettisti non
vogliono che la raggiunga.
Post by Hole
Ad esempio, l'introduzione dei Generics (template c++) sono molto
lontani dall'essere utili per un design riutilizzabile.
A me sembrano molto utili per fare design riutilizzabili invece.
--
Andrea Francia
http://www.andreafrancia.it/
Davide Consonni
2008-06-06 14:54:00 UTC
Permalink
Post by Andrea Francia
Per esempio alcune cose non le puoi fare in Java perché i progettisti
hanno applicato il principio di "salvare il programmatore da se stesso".
vedi ereditarietà multipla :D
--
Davide Consonni
Tommy
2008-06-06 19:04:22 UTC
Permalink
Post by Davide Consonni
Post by Andrea Francia
Per esempio alcune cose non le puoi fare in Java perché i progettisti
hanno applicato il principio di "salvare il programmatore da se stesso".
vedi ereditarietà multipla :D
Si, questo e' un esempio che fanno in molti, ma la decisione di Java e'
forse troppo estrema dall'altra parte... Ovvero un caso abbastanza utile di
ereditarieta' multipla e' quella in cui erediti anche implementazioni di
metodi (ma non variabili e oggetti).

Tommy
Andrea Francia
2008-06-06 19:11:52 UTC
Permalink
Post by Tommy
Post by Davide Consonni
Post by Andrea Francia
Per esempio alcune cose non le puoi fare in Java perché i progettisti
hanno applicato il principio di "salvare il programmatore da se stesso".
vedi ereditarietà multipla :D
Si, questo e' un esempio che fanno in molti, ma la decisione di Java e'
forse troppo estrema dall'altra parte... Ovvero un caso abbastanza utile di
ereditarieta' multipla e' quella in cui erediti anche implementazioni di
metodi (ma non variabili e oggetti).
Anche se non è la stessa cosa puoi sempre ottenere lo stesso effetto con
la composizione.
--
Andrea Francia
http://andreafrancia.blogspot.com/2008/06/macross-frontier.html
Tommy
2008-06-06 19:43:22 UTC
Permalink
"Andrea Francia"
Post by Andrea Francia
Post by Tommy
Si, questo e' un esempio che fanno in molti, ma la decisione di Java e'
forse troppo estrema dall'altra parte... Ovvero un caso abbastanza utile
di ereditarieta' multipla e' quella in cui erediti anche implementazioni
di metodi (ma non variabili e oggetti).
Anche se non è la stessa cosa puoi sempre ottenere lo stesso effetto con
la composizione.
con piu' codice si... per ogni metodo devi scrivere un metodo che chiama il
metodo dell'oggetto che tieni.
Se cambi il numero dei metodi, o il nome, o i parametri. etc.. dell'oggetto.
hai piu' manutenzione da fare, per ogni classe che usa quell'oggetto in
questo modo dei cambiare codice. La composizione e' per certi versi piu'
potente (puoi cambiare il comportamento anche a runtime), ma in alcuni casi,
quando questo non e' richiesto, comporta solo piu' codice e piu'
manutenzione.

Tommy
Andrea Francia
2008-06-06 19:44:12 UTC
Permalink
Post by Tommy
"Andrea Francia"
Post by Andrea Francia
Post by Tommy
Si, questo e' un esempio che fanno in molti, ma la decisione di Java e'
forse troppo estrema dall'altra parte... Ovvero un caso abbastanza utile
di ereditarieta' multipla e' quella in cui erediti anche implementazioni
di metodi (ma non variabili e oggetti).
Anche se non è la stessa cosa puoi sempre ottenere lo stesso effetto con
la composizione.
con piu' codice si... per ogni metodo devi scrivere un metodo che chiama il
metodo dell'oggetto che tieni.
Se cambi il numero dei metodi, o il nome, o i parametri. etc.. dell'oggetto.
hai piu' manutenzione da fare, per ogni classe che usa quell'oggetto in
questo modo dei cambiare codice. La composizione e' per certi versi piu'
potente (puoi cambiare il comportamento anche a runtime), ma in alcuni casi,
quando questo non e' richiesto, comporta solo piu' codice e piu'
manutenzione.
Vero.
--
Andrea Francia
http://andreafrancia.blogspot.com/2008/06/macross-frontier.html
Hole
2008-06-06 15:04:59 UTC
Permalink
Post by Andrea Francia
Post by Hole
Ad esempio, l'introduzione dei Generics (template c++) sono molto
lontani dall'essere utili per un design riutilizzabile.
A me sembrano molto utili per fare design riutilizzabili invece.
Non ti parlo per esperienza diretta. Ma mettiamo tu voglia usare il
polimorfismo e l'overriding di metodi che restituiscono una lista
di ?, da specializzare classe per classe, in modo da restituire quindi
una lista di Oggetto1 o Oggetto2 etc....
Per il poco tempo a disposizione non ho approfondito più di tanto...ma
penso non sia possibile. Mentre qualche ex-guru di C++ mi ha detto che
con i template, quelli veri del c++, si può.

Pensandoci, deve essere per forza così, visto che i generics, dopo la
compilazione, non esistono più e servono, per adesso, semplicemente a
fare controlli a tempo di compilazione (il che è già una cosa di per
sé molto utile). La limitata potenza dei generics è data dal fatto che
i progettisti java hanno preferito tenere la compatibilità
all'indietro con il codice pre-generics.

Poi, se qualcuno ha da condividere storie generics di successo,
sarebbe opportuno che le condividesse qui.
Davide Consonni
2008-06-06 15:08:19 UTC
Permalink
Post by Hole
Poi, se qualcuno ha da condividere storie generics di successo,
sarebbe opportuno che le condividesse qui.
cos'è una storia generics di successo ?
--
Davide Consonni
Hole
2008-06-06 15:41:04 UTC
Permalink
Post by Davide Consonni
Post by Hole
Poi, se qualcuno ha da condividere storie generics di successo,
sarebbe opportuno che le condividesse qui.
cos'è una storia generics di successo ?
:)

Metti che in una classe astratta si voglia definire un metodo astratto
che restituisca una lista di generics. Quando si va a ridefinire il
metodo nelle varie classi concrete, vorresti specializzare il metodo a
ritornare una lista di oggetti specifici.

Mi sembra che non sia possibile.......
Davide Consonni
2008-06-06 15:45:49 UTC
Permalink
Post by Hole
Metti che in una classe astratta si voglia definire un metodo astratto
che restituisca una lista di generics. Quando si va a ridefinire il
metodo nelle varie classi concrete, vorresti specializzare il metodo a
ritornare una lista di oggetti specifici.
Mi sembra che non sia possibile.......
no, i generics non sono covariant.
--
Davide Consonni
Andrea Francia
2008-06-06 16:02:58 UTC
Permalink
Post by Hole
Post by Davide Consonni
Post by Hole
Poi, se qualcuno ha da condividere storie generics di successo,
sarebbe opportuno che le condividesse qui.
cos'è una storia generics di successo ?
:)
Metti che in una classe astratta si voglia definire un metodo astratto
che restituisca una lista di generics. Quando si va a ridefinire il
metodo nelle varie classi concrete, vorresti specializzare il metodo a
ritornare una lista di oggetti specifici.
Mi sembra che non sia possibile.......
E' possibile. Quando ho tempo faccio una prova e posto.
--
Andrea Francia
http://www.andreafrancia.it/
Hole
2008-06-06 16:05:34 UTC
Permalink
Post by Andrea Francia
Post by Hole
Metti che in una classe astratta si voglia definire un metodo astratto
che restituisca una lista di generics. Quando si va a ridefinire il
metodo nelle varie classi concrete, vorresti specializzare il metodo a
ritornare una lista di oggetti specifici.
Mi sembra che non sia possibile.......
E' possibile. Quando ho tempo faccio una prova e posto.
Ok.
Aspettiamo con ansia:)
Andrea Francia
2008-06-06 16:31:32 UTC
Permalink
Post by Hole
Post by Andrea Francia
Post by Hole
Metti che in una classe astratta si voglia definire un metodo astratto
che restituisca una lista di generics. Quando si va a ridefinire il
metodo nelle varie classi concrete, vorresti specializzare il metodo a
ritornare una lista di oggetti specifici.
Mi sembra che non sia possibile.......
E' possibile. Quando ho tempo faccio una prova e posto.
Ok.
Aspettiamo con ansia:)
import java.util.Collections;
import java.util.List;


class CanUomo {}

class Rutto extends CanUomo {}

abstract class CanUomoList {
abstract List<? extends CanUomo> getList();
}

class RuttoList extends CanUomoList{
@Override
List<Rutto> getList() {
return Collections.emptyList();
}
}

E' questo che intendevate?
--
Andrea Francia
http://www.andreafrancia.it/
Tommy
2008-06-06 19:33:33 UTC
Permalink
Post by Andrea Francia
class RuttoList extends CanUomoList{
@Override
List<Rutto> getList() {
return Collections.emptyList();
}
}
il punto e' che getList ritorna una lista EmptyList... non un List<Rutto> o
null.

hai da solo dimostrato quanto siano invece limitati i generics... ora cambio
il tuo codice in:

class RuttoList extends CanUomoList{
@Override
List<Rutto> getList() {
final List list = new ArrayList();
list.add("stringa");
list.add(1);
list.add(new CanUomo());
list.add(true);
return list;
}
}

e tutto funziona!

Sto ritornando una lista che contiene una stringa, un intero, un CanUomo ed
un booleano attraverso il metodo getList che ha una signature List<Rutto>.
Nota che nessun oggetto sulla lista e' un Rutto!!!

Se ottengo la lista cosi' non ho problemi:

final List myList1 = ruttoList.getList();

ora posso iterare i mei oggetti di vari tipi...

for(final Object o : myList1)
{
System.out.println(o);
}

se invece faccio:

final List<Rutto> myList2 = ruttoList.getList();

for(final Rutto o : myList2)
{
System.out.println(o);
}

compila tutto bene, ma ottengo un class cast exception a runtime ( un
pochino tardi, no?!) (*)

A me questo non piace... a mio avviso il compilatore non dovrebbe accettare:

List<Rutto> getList() {
final List list = new ArrayList();
...
return list;
}

perche' non e' sicuro.

e poi non dovrebbe accettare:

final List myList1 = ruttoList.getList();

ma dovrebbe volere per forza:

final List<Rutto> myList2 = ruttoList.getList();

In C++ il compilatore e' molto piu' severo e cmq l'implementazione di
templates non si basa su casts ma su creazione di veri e propri nuovi tipi.
(*) = ottieni una class cast exception in quanto questo in realta' e' tutto
cio' che e' un generic, un cast!

Tommy
Andrea Francia
2008-06-06 19:43:17 UTC
Permalink
Post by Tommy
Post by Andrea Francia
class RuttoList extends CanUomoList{
@Override
List<Rutto> getList() {
return Collections.emptyList();
}
}
il punto e' che getList ritorna una lista EmptyList... non un List<Rutto> o
null.
C'e' c'entra emptyList() ? Funzionava anche con una lista piena.
Post by Tommy
hai da solo dimostrato quanto siano invece limitati i generics...
Non capisco perché?
Post by Tommy
ora cambio
class RuttoList extends CanUomoList{
@Override
List<Rutto> getList() {
final List list = new ArrayList();
list.add("stringa");
list.add(1);
list.add(new CanUomo());
list.add(true);
return list;
}
}
e tutto funziona!
Sto ritornando una lista che contiene una stringa, un intero, un CanUomo ed
un booleano attraverso il metodo getList che ha una signature List<Rutto>.
Nota che nessun oggetto sulla lista e' un Rutto!!!
...
Post by Tommy
final List<Rutto> myList2 = ruttoList.getList();
for(final Rutto o : myList2)
{
System.out.println(o);
}
compila tutto bene, ma ottengo un class cast exception a runtime ( un
pochino tardi, no?!) (*)
Il compilatore ti da un warning, se lo ignori che colpa ha lui.
Nei sorgenti Java 5 non dovresti usare List senza specificare il tipo.
Se lo fai accetti le conseguenze.

Hanno deciso di tenere la compatibilità e hanno introdotto la type
erasure per mantenere la compatibilità binaria con le classi compilate
prima della versione 1.5.

Se scrivi un metodo come se fosse scritto in pre 1.5 è logico che ti
vengono fuori le tipiche ClassCastException che ti potevano venire fuori
con il codice pre 1.5.
Post by Tommy
List<Rutto> getList() {
final List list = new ArrayList();
...
return list;
}
perche' non e' sicuro.
Lo accetta per compatibilità con i sorgenti vecchi. Pero' ti mette un
warning.
Se preferisci un compilatore che ti obbliga a riscrivere i sorgenti
piuttosto che guardare i warning va bene.
--
Andrea Francia
http://andreafrancia.blogspot.com/2008/06/macross-frontier.html
Tommy
2008-06-06 19:58:39 UTC
Permalink
"Andrea Francia"
Post by Andrea Francia
C'e' c'entra emptyList() ? Funzionava anche con una lista piena.
che empty list non e' un list<rutto> ... solo questo...
Post by Andrea Francia
Hanno deciso di tenere la compatibilità e hanno introdotto la type erasure
per mantenere la compatibilità binaria con le classi compilate prima della
versione 1.5.
Non discuto che non abbiano fatto la cosa giusta... forse si, cosi' hanno
mantenuto la compatibilita'... non sono convinto pero' che sia la cosa
migliore.... lo e' solo se consideri la legacy delle versioni precedenti, ma
se si potesse rifare da capo dubito che i generics sarebbero fatti cosi'.
Post by Andrea Francia
Lo accetta per compatibilità con i sorgenti vecchi. Pero' ti mette un
warning.
Se preferisci un compilatore che ti obbliga a riscrivere i sorgenti
piuttosto che guardare i warning va bene.
Sicuramente si, preferisco di gran lunga gli errori. Il fatto che accetti
queste cose per compatibilita' e' una attenuante, non una assoluzione!

Tommy
Andrea Francia
2008-06-06 20:17:08 UTC
Permalink
Post by Tommy
"Andrea Francia"
Post by Andrea Francia
C'e' c'entra emptyList() ? Funzionava anche con una lista piena.
che empty list non e' un list<rutto> ... solo questo...
Ci sono due empty list in Collections:
- public static final <T> List<T> emptyList();
- public static final List EMPTY_LIST;

Mentre EMPTY_LIST è di tipo List, emptyList() prende parametro generico
che inferisce dal contesto.

Infatti la seguente non ti da warning:
List<Rutto> pippo = Collections.emptyList();

La seguente da warning:
List<Rutto> pippo = Collections.EMPTY_LIST;
Post by Tommy
Post by Andrea Francia
Hanno deciso di tenere la compatibilità e hanno introdotto la type erasure
per mantenere la compatibilità binaria con le classi compilate prima della
versione 1.5.
Non discuto che non abbiano fatto la cosa giusta... forse si, cosi' hanno
mantenuto la compatibilita'... non sono convinto pero' che sia la cosa
migliore.... lo e' solo se consideri la legacy delle versioni precedenti, ma
se si potesse rifare da capo dubito che i generics sarebbero fatti cosi'.
D'accordo.
Post by Tommy
Post by Andrea Francia
Lo accetta per compatibilità con i sorgenti vecchi. Pero' ti mette un
warning.
Se preferisci un compilatore che ti obbliga a riscrivere i sorgenti
piuttosto che guardare i warning va bene.
Sicuramente si, preferisco di gran lunga gli errori. Il fatto che accetti
queste cose per compatibilita' e' una attenuante, non una assoluzione!
Chi ha montagne di codice scritto per la 1.4 non credo che sia d'accordo.

D'altra parte anche tu non hai tutti i torti, avrebbero potuto fare che
se compili -source 1.5 ti obbliga a specificare sempre il tipo generico,
mentre con -source 1.4 non ti permette di usare i generics.

Pero' io preferisco la soluzione di mezzo, quella dove ti mettono i warning.
--
Andrea Francia
http://andreafrancia.blogspot.com/2008/06/macross-frontier.html
Tommy
2008-06-06 21:05:24 UTC
Permalink
"Andrea Francia"
Post by Andrea Francia
- public static final <T> List<T> emptyList();
- public static final List EMPTY_LIST;
si, ma quel T non vuole dire nulla, vuole solo dire "un tipo", non e' Rutto
(ma piuttosto Object). Di fatto EMPTY_LIST e quello che emptyList()
restituisce sono la stessa cosa... tanto e' vero che emptyList() non e'
altro che:

public static final <T> List<T> emptyList() {
return (List<T>) EMPTY_LIST;
}

Quindi mi puo' anche dare un warning se uso EMPTY_LIST direttamente ma a me
questo pare tutto molto "sporco".

Io concordo con quello che dici, non pensare il contrario, non so se capisci
cosa sto cercando di dire, probabilmente tu la vedi una discussione
filosofica...

Tommy
Tommy
2008-06-06 22:24:00 UTC
Permalink
"Andrea Francia"
Post by Andrea Francia
Hanno deciso di tenere la compatibilità e hanno introdotto la type erasure
per mantenere la compatibilità binaria con le classi compilate prima della
versione 1.5.
Questo l'ho trovato interessante:

"There is a common misconception that type erasure was nessecary for
backwards compatability in the sense that old code could run on a new JVM
and new code could call old code. However, this was entirely possible
without type erasure (i.e. with reified generics), as has been demonstrated
with the addition of generics to the very similar C# programming language."

Ti posto anche cio' che seguiva in quanto comunque interessante:

...
Type erasure was only nessecary because of a requirement for a temporary
migration compatability; a requirement that the collection classes be the
same classes, so that code which did not insulate itself through use of
interfaces (thus breaking best practices) could co-exist in the same JVM
with new generic style code without the use of wrapping techniques.

Type erasure has several drawbacks:

a.. Typecasting overhead when inserting and removing from a collection and
when calling any other method of a generic type which takes a parameter of a
parameterized type.
b.. Inability to use primitive types as type parameters. Because all types
must be of reference type and must be "erased" to their upper bounds, it was
not possible to allow the use of primitive types. Instead Java 5.0 features
autoboxing which "boxes" primitive type on insertion into collections, thus
incurring extra overhead and making the code more opaque.
c.. What looks like separate classes at development time is really a
single class at runtime. Thus static (class-level) members are shared among
all classes realized from the generic class.
d.. Inability to create new instances or new arrays of types defines
through a type parameter.
e.. Disparity with arrays which means that in general the developer should
never let a method return an array of a parametric type.
f.. The type erasure process may produce clashing method signatures.
g.. Inability to implement different realizations of the same generic
interface.
h.. Inability to use generic exceptions
tratto da wikipedia.

Invece tratto da: http://www.artima.com/intv/genericsP.html

For example, with Java generics, you don't actually get any of the execution
efficiency that I talked about, because when you compile a generic class in
Java, the compiler takes away the type parameter and substitutes Object
everywhere. So the compiled image for List<T> is like a List where you use
the type Object everywhere. Of course, if you now try to make a List<int>,
you get boxing of all the ints. So there's a bunch of overhead there. [
perche' int non deriva da Object... quindi non puo' essere messo in una list
e quindi deve fare boxing a Integer].
Furthermore, to keep the VM happy, the compiler actually has to insert all
of the type casts you didn't write.
...
And really all they're doing in their implementation is automatically
inserting those type casts for you. So you get the syntactic sugar, or some
of it at least, but you don't get any of the execution efficiency. So that's
issue number one I have with Java's solution.
Issue number two, and I think this is probably an even bigger issue, is that
because Java's generics implementation relies on erasure of the type
parameter, when you get to runtime, you don't actually have a faithful
representation of what you had at compile time.

Tommy
Andrea Francia
2008-06-06 22:41:37 UTC
Permalink
Post by Tommy
"Andrea Francia"
Post by Andrea Francia
Hanno deciso di tenere la compatibilità e hanno introdotto la type erasure
per mantenere la compatibilità binaria con le classi compilate prima della
versione 1.5.
"There is a common misconception that type erasure was nessecary for
backwards compatability in the sense that old code could run on a new JVM
and new code could call old code. However, this was entirely possible
without type erasure (i.e. with reified generics), as has been demonstrated
with the addition of generics to the very similar C# programming language."
Qui potrebbero aver ragione, ma la spiegazione non mi convince molto.
Praticamente dicono se ci sono riusciti in C# dovevano riuscirci anche
in Java. Io non so se il C# e Java sono comparabili da questo punto di
vista.

Probabilmente è vero che la type erasure non era necessaria per la
compatibilità binaria pero' se l'argomentavano spiegando tecnicamente
perché è possibile illustrando qualche dettaglio del funzionamento della
JVM mi convincevo meglio.
Post by Tommy
...
Type erasure was only nessecary because of a requirement for a temporary
migration compatability; a requirement that the collection classes be the
same classes, so that code which did not insulate itself through use of
interfaces (thus breaking best practices) could co-exist in the same JVM
with new generic style code without the use of wrapping techniques.
Non ho capito bene questa parte.

Comunque tutto molto interessante.
--
Andrea Francia
http://andreafrancia.blogspot.com/2008/06/macross-frontier.html
Tommy
2008-06-07 09:00:45 UTC
Permalink
"Andrea Francia"
Post by Andrea Francia
Non ho capito bene questa parte.
Comunque tutto molto interessante.
Penso che riassumendo dica semplicemente che ... se per compatibilita' si
intende che vecchio codice (senza generics) puo' girare su nuova JVM (con
generics) e che il nuovo codice (con generics) su nuova VM possa anche
chiamare vecchio codice senza generics, allora la type erasure non era
necessaria.

In Sun avevano pero' un requisito aggiuntivo (non utilissimo) che rendeva la
type erasure necessaria:
Il requisito diceva che la classe con generics e quella senza dovevano
essere la stessa classe cosi' che una istanza di tale classe potesse essere
usata nel nuovo codice con generics e nel vecchio senza, insomma la stessa
istanza usata in due modi diversi.

Ok, i primi requisiti sono importanti in quanto il codice puo' avere una
vita di parecchi anni, e quindi e' importrante che vecchio codice possa
girare su nuova VM. E' importantissimo anche che il nuovo codice possa
chiamare del vecchio codice che non usa generics. Non e' pero'
indispensabile avere la possibilita' di passare una generic collection a del
vecchio codice scritto senza generics. Se usiamo una library vecchia,
potremo semplicemente assicurarci di passare un qualche cosa che la libreria
e' in grado di usare. Quindi se la libreria vuole un List, noi potremmo
semplicemente passare List o ArrayList, ma non un List<Integer> o un
ArrayList<Integer>... ma no, questo non bastava a Sun.

Questo requisito parzialmente utile temporaneamente, durante il periodo di
transazione ha reso i generics meno utili di quanto sarebbero potuti essere.

Se guardi ArrayList.java dice:

public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable

a dimostrazione che ArrayList e ArrayList<E> sono la stessa cosa, la stessa
classe.

Quindi e' ovvio che poi ArrayList.java segue con un bel:

/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer.
*/
private transient Object[] elementData;

ovvero un Object[] e non un E[], ed e' intuibile che debba usare type
erasure.

Quindi torniamo a quando dicevo che :

new ArrayList<Integer>();
new ArrayList<String>();
new ArrayList();

in realta' non sono tre tipi diversi ma lo stesso tipo (una sorta di
ArrayList<Object>).
Post by Andrea Francia
Post by Andrea Francia
Sono due tipi diversi.
Tant'e vero che sia
oggetti = stringhe; // non compila
stringhe = oggetti; // non compila
il fatto che non compili e' semplicemente un controllino che fa il
compilatore per essere carino. Poi sei libero di chiamarli due tipi diversi,
dipende da cosa si intende per tipo, suppongo nessuna definizione sia errata
e nessuna giusta, ma se uno ha usato i templates in C++ fa davvero fatica a
dire che sono due tipi diversi. (in C++ il template viene di fatto usato
come stampino per produrrebbe due classi diverse che venono compilate in
tempi diversi ed hanno nomi diversi e posti diversi nel binary e non hanno
type erasure etc..., non e' l'implementazione piu' elegante, ma sicuramente
questa definisce due tipi diversi!).

Quindi type erasure e' stata fatta per permettere questo uso misto di una
istanza:

// Nuovo codice:
final List<String> names = new ArrayList<String>();
names.add("Tommy");
// ...
// Vecchio codice:
final List str1 = names;

Qui, sarebbe bastato passare un List invece che un List<String> al vecchio
codice, se fossero stati due tipi diversi (due interface diverse).

// Vecchio codice:
final List names = new ArrayList();
names.add("Tommy");
// ...
// Nuovo codice:
final List<String> str1 = names;

Qui, il vecchio codice crea una Lista che contiene solo stringhe, ma che
potrebbe contenere qualsiasi cosa. Nel tuo nuovo codice che chiama questo
vecchio codice pero' non vuoi dover usare List e fare i cast tutte le volte
cosi', cosi' hanno fatto in modo che List e List<String> siano la stessa
cosa e che tu possa usare tranquillamente i generics anche con oggetti che
sono stati creati senza generics (nel vecchio codice).
Questo richiede che i due siano in realta' lo stesso tipo... sarebbe stato
meglio se List e List<String> fossero state due interface diverse, ok,
dovevi continuare ad usare la vecchia senza generics nel nuovo codice o
perdere tempo a passare i dati da List a List<String> a seconda di quanto
siano importanti le prestazioni in quel punto.

Man mano che le librerie vengono scritte usando generics questo requisito
non necessario ma solo "nice to have" e' sempre meno utile, pero' ha
limitato severamente i generics in Java.

Magari la versione 7 risolve il problema ma per farlo ora dovrebbe comunque
creare qualche problema, quindi potevano farlo fin da subito.
Post by Andrea Francia
Post by Andrea Francia
Post by Andrea Francia
IMHO I generics sono troppo comodi.
che siano troppo comodi concordo in pieno, ma sarebbero potuti essere molto
piu' comodi.

Poi l'articolo parlava di boxing... ovvero il fatto che ArrayList tiene
Object in realta' rende impossibile fare ArrayList<int> in quanto int
essendo un primitivo non deriva da Object e quindi non si puo'. Questo rende
necessario quindi fare ArrayList<Integer> che probabilmente comporta tutta
una serie di conversioni da int a Integer e poi da Integer a int.. tutto non
necessario nella maggior parte dei casi.

Poi non fraintendere, uso i generics tutti i giorni e li trovo utile come li
trovi utili tu, ma penso solo potrebbero essere molto meglio.

Tommy
Andrea Francia
2008-06-07 13:09:47 UTC
Permalink
Post by Tommy
"Andrea Francia"
Post by Andrea Francia
Non ho capito bene questa parte.
Penso che riassumendo dica semplicemente che ...
Credo di aver capito, grazie.
Post by Tommy
Poi l'articolo parlava di boxing... ovvero il fatto che ArrayList tiene
Object in realta' rende impossibile fare ArrayList<int> in quanto int
essendo un primitivo non deriva da Object e quindi non si puo'.
Anche i generics come tipo primitivo, come i reified generics sono tra
le cose che la comunità Java chiede. Chissà se e quando saranno aggiunti?

Sui vari siti che parlano di Java 7 se ne parla. Pero' non sono siti
ufficiali Sun quindi contengono un po' quello che gli utenti vorrebbero
piuttosto quello che ci sarà.

Per esempio:
http://tech.puredanger.com/java7#reified
--
Andrea Francia
http://andreafrancia.blogspot.com/2008/06/macross-frontier.html
Andrea Francia
2008-06-06 22:48:57 UTC
Permalink
Post by Tommy
"Andrea Francia"
Post by Andrea Francia
Hanno deciso di tenere la compatibilità e hanno introdotto la type erasure
per mantenere la compatibilità binaria con le classi compilate prima della
versione 1.5.
"There is a common misconception that type erasure was nessecary for
backwards compatability in the sense that old code could run on a new JVM
and new code could call old code. However, this was entirely possible
without type erasure (i.e. with reified generics), as has been demonstrated
with the addition of generics to the very similar C# programming language."
...
Post by Tommy
tratto da wikipedia.
La pagina è http://en.wikipedia.org/wiki/Criticism_of_Java
--
Andrea Francia
http://andreafrancia.blogspot.com/2008/06/macross-frontier.html
Tommy
2008-06-06 19:15:00 UTC
Permalink
Post by Hole
Mi sembra che non sia possibile.......
In C++ i templates definiscono un vero e proprio "tipo", in Java non fanno
affatto questo ma inseriscono un cast.

Ovvero, in C++:

std::list<Person>

e' una lista di persone.

in Java:

List<Person>

e' una lista di oggetti (Object) che in alcuni casi otterra' un cast
automatico a Person, questo perche' in Java Object sta alla base di tutti
gli oggetti, quindi e' come avere "Person extends Object".

L'altra cosa da notare e' che se poi hai:

std::list<Animal>

in C++ ora hai di fatto due tipi diversi: std::list<Animal> and
std::list<Person>. In Java hai sempre ed un solo tipo di lista List<Object>
che a volte fara' un cast a Person ed a volte a Animal.

In C++ anche senza essere un guru, puoi sicuramente fare quello che dici, e'
banale.

Tommy
Andrea Francia
2008-06-06 19:34:10 UTC
Permalink
Post by Tommy
Post by Hole
Mi sembra che non sia possibile.......
In C++ i templates definiscono un vero e proprio "tipo", in Java non fanno
affatto questo ma inseriscono un cast.
std::list<Person>
e' una lista di persone.
List<Person>
e' una lista di oggetti (Object) che in alcuni casi otterra' un cast
automatico a Person, questo perche' in Java Object sta alla base di tutti
gli oggetti, quindi e' come avere "Person extends Object".
Non è esattamente così.
Se hai
List<String> stringhe = null;
List<Object> oggetti = null;

Sono due tipi diversi.
Tant'e vero che sia
oggetti = stringhe; // non compila
stringhe = oggetti; // non compila

Invece si ha che
List<String> estende List
List<Oggetti> estende List

Infatti puoi fare

List list = oggetti; //compila
list = stringhe; //compila

Quindi in Java sono due tipi diversi come in C++.
In piu' hanno un supertipo comune.

Il fatto della type erasure avviene dopo la compilazione.
E' vero che a runtime non ci sono differenze il tipo di 'stringhe' e il
tipo 'oggetti' ma durante la compilazione sono due tipi diversi.

Semplicemente la tipipizzazione dei parametri non è supportata a
runtime. Si vocifera che questa cosa potrebbe essere introdotta nel Java
7 o successivi.

Non sono mai (ancora?) stato persuaso da chi pensa che i Generics Java
siano fatti peggio dei template C++.
--
Andrea Francia
http://andreafrancia.blogspot.com/2008/06/macross-frontier.html
Tommy
2008-06-06 19:51:54 UTC
Permalink
"Andrea Francia"
Post by Andrea Francia
Non è esattamente così.
Se hai
List<String> stringhe = null;
List<Object> oggetti = null;
Sono due tipi diversi.
Non sono affatto due tipi diversi.
Post by Andrea Francia
Tant'e vero che sia
oggetti = stringhe; // non compila
stringhe = oggetti; // non compila
certo che non compila, ma il fatto che non compili non vuole affatto dire
che siano due tipi diversi. Guardati come sono implementati i generics e te
ne renderai conto. Il compilatore semplicemente controlla, ma il tipo e'
sempre lo stesso!
Post by Andrea Francia
Quindi in Java sono due tipi diversi come in C++.
No, non e' cosi... tanto e' vero che hai type erasure e non puoi sapere il
tipo a runtime!
List<Rutto> e' un List normalisso non e' un nuovo tipo List<Rutto> il
compilatore non fa altro che inserire casts quindi quando fai un get sulla
lista il compilatore ti regala il cast: (Rutto). Tutto qua! In piu' ti fa
qualche controllino at compile time, come hai dimostrato.
Ma quando il software gira sono lo stesso tipo: List. Non sono affatto due
tipi diversi!
Post by Andrea Francia
Non sono mai (ancora?) stato persuaso da chi pensa che i Generics Java
siano fatti peggio dei template C++.
Il problema dei templates in C++ sono essenzialmetne due:

- sono complessi da usare (non le cose basi)
- usano piu' spazio e richiedono piu' tempo per compilare.

Tommy
Tommy
2008-06-06 20:08:39 UTC
Permalink
"Tommy" <***@no_mail.no> wrote

Tornando al tuo esempio... o quasi...

final List<Rutto> myList1 = ruttoList.getList();
myList1.toArray(new Rutto[0]);

quel new Rutto[0] e' necessario perche nonostante myList1 sia un List<Rutto>
il compilatore a meno che tu non passi il tipo Rutto[] a toArray() non sa
affatto che tipo creare!!! Tu passi il tpo e lui poi fa un bel .getClass per
sapere che tipo myList1 dovrebbe tenere!!!
Questo rende ovvio il concetto che myList1 e' un semplice ArrayList (nel mio
caso).

se non passi new Rutto[0] non puo' ritornare un Rutto[] ma solo un Object[]
in quanto si e' dimanticato il tipo!

In C++ non e' necessario passare "new Rutto[0]", toArray sarebbe in grado di
creare un array del tipo giusto facendo (javizzando) un "new T[size]" dove T
realmente sa che tipo e' e se era un List<Rutto>, allora T sara' un Rutto.

Tommy
Andrea Francia
2008-06-06 20:26:21 UTC
Permalink
Post by Tommy
Tornando al tuo esempio... o quasi...
final List<Rutto> myList1 = ruttoList.getList();
myList1.toArray(new Rutto[0]);
quel new Rutto[0] e' necessario perche nonostante myList1 sia un List<Rutto>
il compilatore a meno che tu non passi il tipo Rutto[] a toArray() non sa
affatto che tipo creare!!! Tu passi il tpo e lui poi fa un bel .getClass per
sapere che tipo myList1 dovrebbe tenere!!!
Questi tipi esistono solo a compile-time e la reflection non te li dice
a causa della type erasure.

Concordo che questo è un limite dei Generics.
--
Andrea Francia
http://andreafrancia.blogspot.com/2008/06/macross-frontier.html
Andrea Francia
2008-06-06 20:22:22 UTC
Permalink
Post by Tommy
"Andrea Francia"
Post by Andrea Francia
Non è esattamente così.
Se hai
List<String> stringhe = null;
List<Object> oggetti = null;
Sono due tipi diversi.
Non sono affatto due tipi diversi.
Non siamo d'accordo.

Secondo me i tipi sono diversi perché me i tipi sono quelli definiti dal
linguaggio e dal compilatore.

Per te sono uguali perché per te i tipi che contano sono quelli presenti
a runtime.
--
Andrea Francia
http://andreafrancia.blogspot.com/2008/06/macross-frontier.html
Tommy
2008-06-06 21:19:30 UTC
Permalink
"Andrea Francia"
Post by Andrea Francia
Post by Tommy
Non sono affatto due tipi diversi.
Non siamo d'accordo.
Secondo me i tipi sono diversi perché me i tipi sono quelli definiti dal
linguaggio e dal compilatore.
Per te sono uguali perché per te i tipi che contano sono quelli presenti a
runtime.
Forse e' una discussione filosofica anche questa, hai ragione... e' che non
mi convincono del tutto...

final List<Rutto> myList1 = ruttoList.getList();
myList1.toArray(new Rutto[0]);

lascia stare il runtime, concentriamoci sul compile time, Java potrebbe
accettare anche:

final List<Rutto> myList1 = ruttoList.getList();
myList1.toArray();

e, in compile time, capire che myList1 e' un List<Rutto> e quindi passare in
automatico un new Rutto[0] nel bytecode che produce (putroppo qualche cosa
deve passare visto che quando at runtime deve eseguire toArray() a quel
punto davvero non sa il tipo per via del type erasure).
(l'esempio invece ritorna un Object[])

Quindi, quello che voglio dire che e' at runtime non sono due tipi diversi,
su questo concordiamo. E... compile time, beh, non mi convince, il
compilatore genera spesso solo warnings e non errori, e' troppo permissimo e
non fa cose banali (per motivi validi (compatibilita' etc..), non discuto,
ma il risultato non m convince del tutto).

Tommy
Andrea Francia
2008-06-06 21:36:17 UTC
Permalink
Post by Tommy
"Andrea Francia"
Forse e' una discussione filosofica anche questa, hai ragione... e' che non
mi convincono del tutto...
final List<Rutto> myList1 = ruttoList.getList();
myList1.toArray(new Rutto[0]);
lascia stare il runtime, concentriamoci sul compile time, Java potrebbe
final List<Rutto> myList1 = ruttoList.getList();
myList1.toArray();
e, in compile time, capire che myList1 e' un List<Rutto> e quindi passare in
automatico un new Rutto[0] nel bytecode che produce
Deriva dal fatto che per qualche motivo in Java non puoi fare

new T[count]

se T il parametro generic.
Non so perché.

Sapevo che non potevi fare
new T()
e di questo capivo il motivo (type erasure) pero' per quanto riguarda
l'array non me lo spiego.
Post by Tommy
Quindi, quello che voglio dire che e' at runtime non sono due tipi diversi,
su questo concordiamo. E... compile time, beh, non mi convince, il
compilatore genera spesso solo warnings e non errori, e' troppo permissimo e
non fa cose banali (per motivi validi (compatibilita' etc..), non discuto,
ma il risultato non m convince del tutto).
Ok.

Comunque chissà se qualcuno ha colto la citazione Rutto e CanUomo?
--
Andrea Francia
http://andreafrancia.blogspot.com/2008/06/macross-frontier.html
Tommy
2008-06-06 21:48:33 UTC
Permalink
"Andrea Francia"
Post by Andrea Francia
Deriva dal fatto che per qualche motivo in Java non puoi fare
new T[count]
se T il parametro generic.
Non so perché.
Sapevo che non potevi fare
new T()
e di questo capivo il motivo (type erasure) pero' per quanto riguarda
l'array non me lo spiego.
Principalmente per lo stesso motivo, ma io dicevo in realta' un'altra
cosa...
Post by Andrea Francia
Comunque chissà se qualcuno ha colto la citazione Rutto e CanUomo?
Io come al solito no... o usato google ora e suppongo sia da Balle Spaziali,
ma... azzz sara' un secolo che non vedo quel film... :-)

Tommy
Andrea Francia
2008-06-06 21:55:34 UTC
Permalink
Post by Tommy
"Andrea Francia"
Post by Andrea Francia
Deriva dal fatto che per qualche motivo in Java non puoi fare
new T[count]
se T il parametro generic.
Non so perché.
Sapevo che non potevi fare
new T()
e di questo capivo il motivo (type erasure) pero' per quanto riguarda
l'array non me lo spiego.
Principalmente per lo stesso motivo, ma io dicevo in realta' un'altra
cosa...
Allora non ho capito.
--
Andrea Francia
http://andreafrancia.blogspot.com/2008/06/macross-frontier.html
Andrea Francia
2008-06-06 16:01:59 UTC
Permalink
Post by Hole
Post by Andrea Francia
Post by Hole
Ad esempio, l'introduzione dei Generics (template c++) sono molto
lontani dall'essere utili per un design riutilizzabile.
A me sembrano molto utili per fare design riutilizzabili invece.
Non ti parlo per esperienza diretta. Ma mettiamo tu voglia usare il
polimorfismo e l'overriding di metodi che restituiscono una lista
di ?, da specializzare classe per classe, in modo da restituire quindi
una lista di Oggetto1 o Oggetto2 etc....
Per il poco tempo a disposizione non ho approfondito più di tanto...ma
penso non sia possibile. Mentre qualche ex-guru di C++ mi ha detto che
con i template, quelli veri del c++, si può.
Pensandoci, deve essere per forza così, visto che i generics, dopo la
compilazione, non esistono più e servono, per adesso, semplicemente a
fare controlli a tempo di compilazione (il che è già una cosa di per
sé molto utile). La limitata potenza dei generics è data dal fatto che
i progettisti java hanno preferito tenere la compatibilità
all'indietro con il codice pre-generics.
Poi, se qualcuno ha da condividere storie generics di successo,
sarebbe opportuno che le condividesse qui.
IMHO I generics sono troppo comodi.
--
Andrea Francia
http://www.andreafrancia.it/
cicap
2008-06-05 17:59:54 UTC
Permalink
Post by Andrea Laforgia
Post by Tommy
Scusa, non capisco cosa ci sarebbe da stare attenti in Java.
Mi riferisco alla risposta "bisogna starci attenti" che ho ricevuto.
E in definitiva a cosa dovremmo stare attenti, che non si capisce?
Hole
2008-06-04 14:06:05 UTC
Permalink
Post by Andrea Laforgia
Il concetto è che, se b ha lo stesso valore (come riferimento) di a, nel
momento in cui modifico il contenuto di a che succede a b? dovrei trovar
modificato anche il contenuto di b, il che significa che la semantica è
errata...
Infatti, è così.
Bisogna starci attenti :)
Tommy
2008-06-04 19:28:25 UTC
Permalink
Post by Andrea Laforgia
Il concetto è che, se b ha lo stesso valore (come riferimento) di a, nel
momento in cui modifico il contenuto di a che succede a b? dovrei trovar
modificato anche il contenuto di b, il che significa che la semantica è
errata...
non puoi modificare il contenuto di a visto che String e' immutable. puoi
solo far puntare il reference ad un'altra stringa, ma non puoi mutare
l'oggetto.

Tommy
Andrea Francia
2008-06-04 09:57:54 UTC
Permalink
Post by Giordano
Post by andrea__
non è più corretto fare così?
List list=new ArrayList();
list.add("pippo");
list.add("pluto");
list.add("paperino");
Iterator it = list.iterator();
String str;
while (it.hasNext()) {
str=(String)it.next();
System.out.println(str);
}
Giusto, correttissimo, ma se hai migliaia, anzi no... di più, migliaia
di migliaia di elementi nella tua list allora <<inizia>> a diventare
un po' pesante perchè....
...[ segue citazione da http://www.mondoinformatico.info/ottimizzare-il-codice-in-java_post-312.html]
<< Siccome gli oggetti String sono immutabili, questo fa si che ogni
volta si crei un nuovo oggetto e si lasci l’altro per il << Garbage
Collector. Questo implica un notevole spreco di tempo, che si può
evitare utilizzando StringBuffer, poiché << questo permette la
modifica degli oggetti rappresentati.
Quindi, per 3, 100 o 1000 elementi nella tua list non ti preoccupare
se fai uso del tipo String; invece se hai molti più elementi allora ti
conviene usare il tipo StringBuffer
Lo StringBuffer non c'entra niente, qui non si sta modificando delle
stringhe.

Poi piuttosto che usare lo StringBuffer conviene usare lo StringBuilder
che non ha l'overhead dei metodi synchronized.
--
Andrea Francia
http://www.andreafrancia.it/
Lucio Benfante
2008-06-04 13:09:52 UTC
Permalink
Post by andrea__
forse non mi sono spiegato bene... che il ciclo sia un while piuttosto che
un for o che si usino i generics o meno, la domanda resta: perchè ad ogni
iterazione devo ridichiarare l'oggetto str? non è più ottimizzato
dichiararlo all'esterno del ciclo? perchè sta pratica è diffusa in tutti i
testi che sto leggendo? non è più corretto fare così?
List list=new ArrayList();
list.add("pippo");
list.add("pluto");
list.add("paperino");
Iterator it = list.iterator();
String str;
while (it.hasNext()) {
str=(String)it.next();
System.out.println(str);
}
No, non è più corretto, nè più performante, dato che il compilatore
riuscirebbe a fare la stessa cosa, e anzi forse meglio.

Quindi la tua ottimizzazione del codice non produce risultati
differenti, ma anzi probabilmente rende più difficoltose le
ottimizzazioni che il compilatore potrebbe fare.

Giusto per trasformare le chiacchiere in fatti, diamo un'occhiata al
bytecode generato nei due casi.

Compilando la seguente classe:


public class LoopOptimization {

public void notOptimizedLoop(List list) {
Iterator it = list.iterator();
while (it.hasNext()) {
String str = (String) it.next();
System.out.println(str);
}
}

public void optimizedLoop(List list) {
Iterator it = list.iterator();
String str = null;
while (it.hasNext()) {
str = (String) it.next();
System.out.println(str);
}
}

}


Il bytecode generato è il seguente:

public void notOptimizedLoop(java.util.List);
Code:
0: aload_1
1: invokeinterface #2, 1; //InterfaceMethod
java/util/List.iterator:()Ljava/util/Iterator;
6: astore_2
7: aload_2
8: invokeinterface #3, 1; //InterfaceMethod
java/util/Iterator.hasNext:()Z
13: ifeq 36
16: aload_2
17: invokeinterface #4, 1; //InterfaceMethod
java/util/Iterator.next:()Ljava/lang/Object;
22: checkcast #5; //class java/lang/String
25: astore_3
26: getstatic #6; //Field java/lang/System.out:Ljava/io/PrintStream;
29: aload_3
30: invokevirtual #7; //Method
java/io/PrintStream.println:(Ljava/lang/String;)V
33: goto 7
36: return

public void optimizedLoop(java.util.List);
Code:
0: aload_1
1: invokeinterface #2, 1; //InterfaceMethod
java/util/List.iterator:()Ljava/util/Iterator;
6: astore_2
7: aconst_null
8: astore_3
9: aload_2
10: invokeinterface #3, 1; //InterfaceMethod
java/util/Iterator.hasNext:()Z
15: ifeq 38
18: aload_2
19: invokeinterface #4, 1; //InterfaceMethod
java/util/Iterator.next:()Ljava/lang/Object;
24: checkcast #5; //class java/lang/String
27: astore_3
28: getstatic #6; //Field java/lang/System.out:Ljava/io/PrintStream;
31: aload_3
32: invokevirtual #7; //Method
java/io/PrintStream.println:(Ljava/lang/String;)V
35: goto 9
38: return

Come si vede (a parte l'inizializzazione a null fuori dal ciclo) il
bytecode all'interno del ciclo è esattamente lo stesso.

Ciao
Lucio
--
Lucio Benfante
JUG Padova http://www.parancoe.org ...have a look at it!
www.jugpadova.it http://www.jugevents.org
Andrea Francia
2008-06-04 13:20:44 UTC
Permalink
Post by Lucio Benfante
No, non è più corretto, nè più performante, dato che il compilatore
riuscirebbe a fare la stessa cosa, e anzi forse meglio.
Quindi la tua ottimizzazione del codice non produce risultati
differenti, ma anzi probabilmente rende più difficoltose le
ottimizzazioni che il compilatore potrebbe fare.
Giusto per trasformare le chiacchiere in fatti, diamo un'occhiata al
bytecode generato nei due casi.
A corollario di tutto ciò aggiungo che al momento dell'esecuzione ci si
mette pure il JIT che potrebbe effettuare ulteriori ottimizzazioni
durante l'esecuzione del bytecode.
--
Andrea Francia
http://www.andreafrancia.it/
Andrea Laforgia
2008-06-04 09:31:36 UTC
Permalink
Post by andrea__
non è meglio dichiaralo esternamente al ciclo in modo che ad
ogni iterazione avvenga solo l'assegnazione dell'oggetto?
Sì.
Post by andrea__
esempi che ho visto finora, sto studiando java in vari libri, ho visto
fare come nell'esempio che vi ho riportato...
Perché (ahimè) i sistemi con garbage collection sollevano il programmatore
da attenzioni particolari, quali la parsimonia delle risorse. Ovviamente,
è meglio riutilizzare gli oggetti, che non crearne tutte le volte di nuovi.
--
questo articolo e` stato inviato via web dal servizio gratuito
http://www.newsland.it/news segnala gli abusi ad ***@newsland.it
Andrea Francia
2008-06-04 09:55:20 UTC
Permalink
Post by Andrea Laforgia
Post by andrea__
non è meglio dichiaralo esternamente al ciclo in modo che ad
ogni iterazione avvenga solo l'assegnazione dell'oggetto?
Sì.
Non sono d'accordo, vedi il mio post.
Post by Andrea Laforgia
Post by andrea__
esempi che ho visto finora, sto studiando java in vari libri, ho visto
fare come nell'esempio che vi ho riportato...
Perché (ahimè) i sistemi con garbage collection sollevano il programmatore
da attenzioni particolari, quali la parsimonia delle risorse. Ovviamente,
è meglio riutilizzare gli oggetti, che non crearne tutte le volte di nuovi.
In questo caso non si tratta di riutilizzare gli oggetti ma dei
variabili che memorizzano il riferimento all'oggetto.
--
Andrea Francia
http://www.andreafrancia.it/
Andrea Laforgia
2008-06-04 10:51:41 UTC
Permalink
Post by Andrea Francia
Non sono d'accordo, vedi il mio post.
Ti ho risposto.
--
questo articolo e` stato inviato via web dal servizio gratuito
http://www.newsland.it/news segnala gli abusi ad ***@newsland.it
Andrea Francia
2008-06-04 11:03:24 UTC
Permalink
Post by Andrea Laforgia
Post by Andrea Francia
Non sono d'accordo, vedi il mio post.
Ti ho risposto.
Intendevo l'altro post, quello delle 11.52 in risposta al primo
messaggio dell'OP.
--
Andrea Francia
http://www.andreafrancia.it/
Tommy
2008-06-04 19:34:27 UTC
Permalink
Post by Andrea Laforgia
Post by andrea__
non è meglio dichiaralo esternamente al ciclo in modo che ad
ogni iterazione avvenga solo l'assegnazione dell'oggetto?
Sì.
Non credo cambi nulla in Java, quello che volete dichiarare fuori dal ciclo
e' solo un reference che probabilmente viene "optimised-out" cmq.
Post by Andrea Laforgia
Post by andrea__
esempi che ho visto finora, sto studiando java in vari libri, ho visto
fare come nell'esempio che vi ho riportato...
Perché (ahimè) i sistemi con garbage collection sollevano il programmatore
da attenzioni particolari, quali la parsimonia delle risorse. Ovviamente,
è meglio riutilizzare gli oggetti, che non crearne tutte le volte di nuovi.
dichiarandolo fuori o dentro il ciclo non cambi affatto il numero di oggetti
che vengono creati sulla heap.

Tommy
Tommy
2008-06-04 21:26:32 UTC
Permalink
Post by Tommy
Post by andrea__
non è meglio dichiaralo esternamente al ciclo in modo che ad
ogni iterazione avvenga solo l'assegnazione dell'oggetto?
Sì.
Non credo cambi nulla in Java, quello che volete dichiarare fuori dal
ciclo e' solo un reference che probabilmente viene "optimised-out" cmq.
Per cofermare che si dovrebbero lasciare queste piccolezze al bytecode
compiler e hotspot ho digitato il codice originale dell'OP in Netbeans (ho
dovuto aggiungere un ; nel for loop visto che mancava).
Ho compilato in bytecode il main che conteneva il codice ed ho prodotto un
class file. Poi ho decompilato il .class in un .java (compilazion seguita da
decompilazione). Questo e' il risultato della decompilazione:

ArrayList arraylist = new ArrayList();
arraylist.add("pippo");
arraylist.add("pluto");
arraylist.add("paperino");
String s;
for(Iterator iterator = arraylist.iterator(); iterator.hasNext();
System.out.println(s))
s = (String)iterator.next();

come vedete il reference e' gia' fuori dal loop, e questo solo durante il
primo passaggio, hotspot poi potra' fare di meglio.

Non mi sembra caso di scrivere codice come sopra, il seguente codice
compilato e decompilato produce esattamente lo stesso codice qui sopra, ma
e' molto piu' leggibile:

List<String> list = new ArrayList<String>();
list.add("pippo");
list.add("pluto");
list.add("paperino");
for(final String str : list)
{
System.out.println(str);
}

Compilato e decompilato produce esattamente lo stesso codice riportato
sopra. Solo che

for(final String str : list)
{
System.out.println(str);
}

e' piu' leggibile di:

String s;
for(Iterator iterator = arraylist.iterator(); iterator.hasNext();
System.out.println(s))
s = (String)iterator.next();

Io adoro ottimizzare il codice, e' uno dei miei compiti preferiti, ma non si
deve mai perdere tempo a cercare di fare ottimizzazioni inutili.
In generale si dovrebbe cercare di scrivere codice elegante, leggibile e
robusto. Bisognerebbe evitare grosse atrocita' che sprecano prestazioni,
vero, ma evitare di pensare alle piccolezze di cui si puo' occupare il
compilatore (come un reference che va sullo stack). Poi si dovrebbe usare un
profiler per ottimizzare atrocita' che non si sono viste, oppure colli di
bottiglia non previsti, etc...

Un esempio di atrocita':

List list = new ArrayList();
list.add("pippo");
list.add("pluto");
list.add("paperino");
for(final Object o : list.toArray())
{
System.out.println(new String(o.toString()));
}

Qui, creiamo un array che non serve! Inoltre poi creiamo una nuova stringa
ad ogni iterazione. Decompilato e compilato viene:

ArrayList arraylist = new ArrayList();
arraylist.add("pippo");
arraylist.add("pluto");
arraylist.add("paperino");
Object aobj[] = arraylist.toArray();
int i = aobj.length;
for(int j = 0; j < i; j++)
{
Object obj = aobj[j];
System.out.println(new String(obj.toString()));
}

Come si vede in questo caso il compilatore bytecode non ha fatto miracoli,
abbiamo sempre la creazione di un array e ad ogni iterazione la creazione di
una nuova stringa. Questo e' un esempio di una atrocita' che va evitata
subito! Mettere un reference all'interno di un ciclo non e' una atrocita',
anzi rende solo il codice piu' leggibile ed il compilatore e' in grado di
ottimizzare come vuole....


Tommy
Andrea Francia
2008-06-04 21:46:55 UTC
Permalink
Per cofermare che si dovrebbero lasciare queste piccolezze al bytecode ...
Ottimo post.
Concordo in pieno con le conclusioni.

Giusto per curiosità, che cosa hai usato per decompilare?
--
Andrea Francia
http://andreafrancia.blogspot.com/2008/05/schermare-le-remoteexception.html
Tommy
2008-06-04 22:01:05 UTC
Permalink
"Andrea Francia"
Post by Andrea Francia
Giusto per curiosità, che cosa hai usato per decompilare?
Jad: http://www.kpdus.com/jad.html

ma ce ne sono anche altri altrettanto o piu' validi.

Tommy
rootkit
2008-06-04 20:42:44 UTC
Permalink
Post by Andrea Laforgia
Post by andrea__
non è meglio dichiaralo esternamente al ciclo in modo che ad
ogni iterazione avvenga solo l'assegnazione dell'oggetto?
Sì.
imho è indifferente, in termini di ottimizzazione. peggio in termini di
manutenibilità.
Post by Andrea Laforgia
Post by andrea__
esempi che ho visto finora, sto studiando java in vari libri, ho visto
fare come nell'esempio che vi ho riportato...
Perché (ahimè) i sistemi con garbage collection sollevano il programmatore
da attenzioni particolari, quali la parsimonia delle risorse. Ovviamente,
è meglio riutilizzare gli oggetti, che non crearne tutte le volte di nuovi.
ma nel caso di specie non c'è allocazione supplementare di nuovi
oggetti. le istanze le recupera da una lista.
--
 Fino all'anno scorso avevo un solo difetto. Ero presuntuoso.
http://softvalley.blogspot.com
http://www.flickr.com/photos/marko_po/
Andrea Francia
2008-06-04 09:52:06 UTC
Permalink
Post by andrea__
sono alle prime armi con java, ma non con la programmazione, ho un dubbio
per cui chiedo a voi che siete più esperti di me...
data una lista di oggetti, in questo caso una lista di stringhe,
supponiamo che io debba ciclare all'interno della lista per stamparne a
video gli elementi, lasciando perdere i generics che non sono il motivo
List list=new ArrayList();
list.add("pippo");
list.add("pluto");
list.add("paperino");
for(Iterator itr=list.iterator();itr.hasNext()) {
String str=(String)itr.next();
System.out.println(str);
}
perchè l'oggetto str viene dichiarato e assegnato ad ogni iterazione del
ciclo for?
Quando scrivi
String str;
non stai dichiarando un oggetto ma stai dichiarando una variabile
puntatore (o riferimento) ad un oggetto String.

Questo vuol dire che stai allocando solo 32 bit (sulle JVM a 32 bit)
sullo stack.

L'allocazione sullo stack, in Java come in C, non è costosa dal punto di
vista delle prestazioni perché consiste solo nell'aumentare, in questo
casi di 4, un contatore.
Post by andrea__
non è meglio dichiaralo esternamente al ciclo in modo che ad
ogni iterazione avvenga solo l'assegnazione dell'oggetto?
No, poi il JIT dovrebbe abbastanza furbo da elidere anche l'addizione +4
all'entranta dello statement con la sottrazione -4 all'uscita quando fai
i for.

Al contrario dichiarare le variabili il piu' tardi possibile è una
pratica consigliata perché diminuisce la complessità del codice.
Post by andrea__
in tutti gli
esempi che ho visto finora, sto studiando java in vari libri, ho visto
fare come nell'esempio che vi ho riportato... Vengo dal C e questo modo di
iterare nei cicli mi da l'idea di poca ottimizzazione...
Anche nel C è consigliabile dichiarare le variabili il piu' tardi possibile.
--
Andrea Francia
http://www.andreafrancia.it/
Hole
2008-06-04 10:24:38 UTC
Permalink
Post by andrea__
sono alle prime armi con java, ma non con la programmazione, ho un dubbio
per cui chiedo a voi che siete più esperti di me...
data una lista di oggetti, in questo caso una lista di stringhe,
supponiamo che io debba ciclare all'interno della lista per stamparne a
video gli elementi, lasciando perdere i generics che non sono il motivo
List list=new ArrayList();
list.add("pippo");
list.add("pluto");
list.add("paperino");
for(Iterator itr=list.iterator();itr.hasNext()) {
        String str=(String)itr.next();
        System.out.println(str);
}
perchè l'oggetto str viene dichiarato e assegnato ad ogni iterazione del
ciclo for? non è meglio dichiaralo esternamente al ciclo in modo che ad
ogni iterazione avvenga solo l'assegnazione dell'oggetto? in tutti gli
esempi che ho visto finora, sto studiando java in vari libri, ho visto
fare come nell'esempio che vi ho riportato... Vengo dal C e questo modo di
iterare nei cicli mi da l'idea di poca ottimizzazione... magari c'è un
motivo valido, chi me lo spiega?  
E' ovvio che è la creazione degli oggetti ad "ammazzare
l'applicazione".
Per le le stringhe, però, java ha una gestione a "pool", quindi le
classiche considerazioni in questo caso non possono essere fatte. In
realtà, le stringhe le hai già create in maniera letterale ("pippo",
"pluto"...).

Se il tuo obiettivo è stampare la stringa, puoi mettere fuori dal
ciclo la dichiarazione dell'oggetto stringa e ridefinirlo ad ogni
iterazione.

String str = null;
while (it.hasNext()) {
str=(String)itr.next();
System.out.println(str);
}

Questo però può essere una trappola in varie situazioni.

Ad esempio, se devi aggiungere l'oggetto ad una lista, devi dichiarare
un nuovo oggetto ogni volta altrimenti otterrai una lista di elementi
che puntano allo stesso oggetto.

In genere, ci sono varie tecniche di ottimizzazione che evitano la
creazione di nuovi oggetti per migliorare le performance in tempo e
spazio. Alcune tecniche consistono in veri e propri refactoring.
In generale, cmq, meglio ricordare la legge dell'ottimizzazione:

"Fa funzionare il codice e alla fine, se hai tempo, forse, ottimizza".
Nel senso che, a meno che non sia un requisito molto stringente, non
bisogna progettare e sviluppare un software con in testa il pallino
dell'ottimizzazione ma intervenire soltanto alla fine, se il profiling
non è in linea con il requisito, non funzionale, di performance.

Di certo, bisogna evitare comuni strafalcioni in fase di coding.
rootkit
2008-06-04 15:53:08 UTC
Permalink
Post by Hole
E' ovvio che è la creazione degli oggetti ad "ammazzare
l'applicazione".
Per le le stringhe, però, java ha una gestione a "pool", quindi le
classiche considerazioni in questo caso non possono essere fatte.
str=(String)itr.next();
non fa nessuna allocazione e non certo perchè si tratta di stringhe.
anche se fossero sarchiaponi sarebbe la stessa cosa. in quel caso
avviene semplicemente una assegnazione di reference.
--
 Fino all'anno scorso avevo un solo difetto. Ero presuntuoso.
http://softvalley.blogspot.com
http://www.flickr.com/photos/marko_po/
Hole
2008-06-05 11:33:58 UTC
Permalink
Post by rootkit
        str=(String)itr.next();
non fa nessuna allocazione e non certo perchè si tratta di stringhe.
anche se fossero sarchiaponi sarebbe la stessa cosa. in quel caso
avviene semplicemente una assegnazione di reference.
Sì, infatti.

Nella mia risposta avevo deviato la discussione verso altre
considerazioni:)
rootkit
2008-06-04 15:44:44 UTC
Permalink
Post by andrea__
perchè l'oggetto str viene dichiarato e assegnato ad ogni iterazione del
ciclo for? non è meglio dichiaralo esternamente al ciclo in modo che ad
ogni iterazione avvenga solo l'assegnazione dell'oggetto?
è la stessa identica cosa, l'unica cosa che cambia fra mettere sopra o
sotto è solo la visibilità.
le variabili (o per meglio dire le references) in java sono concetti
statici fissati a compiler time. per dire, nel tuo caso non è che
qualora a runtime il programma non entra nel ciclo il java si risparmia
di creare la variabile.
Post by andrea__
in tutti gli
esempi che ho visto finora, sto studiando java in vari libri, ho visto
fare come nell'esempio che vi ho riportato... Vengo dal C e questo modo di
iterare nei cicli mi da l'idea di poca ottimizzazione... magari c'è un
motivo valido, chi me lo spiega?
in c è chiaro, non lo puoi fare.
il motivo per cui spesso si fa in quel modo è per comodità, leggibilità
e manutenibilità del codice. come regola generale è buona norma definire
le variabili nello scope più "stretto" possibile all'interno del quale
sono usate.
--
 Fino all'anno scorso avevo un solo difetto. Ero presuntuoso.
http://softvalley.blogspot.com
http://www.flickr.com/photos/marko_po/
Andrea Francia
2008-06-04 15:56:03 UTC
Permalink
Post by rootkit
Post by andrea__
in tutti gli
esempi che ho visto finora, sto studiando java in vari libri, ho visto
fare come nell'esempio che vi ho riportato... Vengo dal C e questo modo di
iterare nei cicli mi da l'idea di poca ottimizzazione... magari c'è un
motivo valido, chi me lo spiega?
in c è chiaro, non lo puoi fare.
Come no? In C puoi mettere le variabili subito dopo l'apertura dello scope.

I C (e non sto dicendo C++) è perfettamente legale scrivere questo

for(i=0; i< 10; i ++) {
int a = i*2;
printf("%d", a);
}

E pure questo:

int a = 3;
printf("%d\n", a);
{
int b = 8;
printf("%d\n", b);
}

Non puoi fare questo:

int a = 3;
printf("%d\n", a);
int b = 8; <--- errore di compilazione
--
Andrea Francia
http://www.andreafrancia.it/
Tommy
2008-06-04 19:41:48 UTC
Permalink
Post by andrea__
List list=new ArrayList();
list.add("pippo");
list.add("pluto");
list.add("paperino");
for(Iterator itr=list.iterator();itr.hasNext()) {
String str=(String)itr.next();
System.out.println(str);
}
perchè l'oggetto str viene dichiarato e assegnato ad ogni iterazione del
ciclo for? non è meglio dichiaralo esternamente al ciclo in modo che ad
ogni iterazione avvenga solo l'assegnazione dell'oggetto? in tutti gli
esempi che ho visto finora, sto studiando java in vari libri, ho visto
fare come nell'esempio che vi ho riportato... Vengo dal C e questo modo di
iterare nei cicli mi da l'idea di poca ottimizzazione... magari c'è un
motivo valido, chi me lo spiega?
A fire pratico per le prestazioni non cambia nulla... ma dichiarandola fuori
rendi il codice peggiore. Io mi concentrerei a scrivere codice migliore
piuttosto che peggiore senza fare alcuna ottimizzazione:

List<String> list = new ArrayList<String>();
list.add("pippo");
list.add("pluto");
list.add("paperino");
for(final String str : list)
{
System.out.println(str);
}

In questo modo eviti sia il cast che la gestione manuale degli iteratori.
Post by andrea__
String str=(String)itr.next();
Dichiaravi un reference che puntava ad una delle stringhe precedentemente
pre-allocate.

Tommy
Continua a leggere su narkive:
Loading...