Newsgroup: it.comp.java
Titolo: [Fwd: Chi mi aiuta per un semplice "if"?]

Un saluto a tutto in NG.
Scusate se domanda e' un po' banale, ma sono un po' niubbo.
Sto uscendo pazzo per un semplice "if". Quello che vorrei fare è leggere
una stringa da un file, e cercare nella stringa letta un carattere
predefinito, per esempio ";". Ho scritto una routine, ma, pur sapendo
che nella stringa ci sono vari ";", non ne trovava nemmeno uno.
Allora ho semplificato tutto al massimo.
Ho salvato un file di testo con un unico carattere (appunto il ";"), ed
ho scritto una routine che leggesse quel carattere e lo confrontasse con
";". Stampando il carattere letto, veniva visualizzato ";", ma quando lo
comparavo con ";", il risultato era sempre che era diverso.
Allora ho letto due volte lo stesso carattere dallo stesso file,
assegnandolo a due variabili, ma quando le confronto, continuano ad
essere diverse.
Chi mi puo' spiegare cosa succede?
Puo' dipendere dall'ambiente di sviluppo? Io uso un programma free
trovato in rete:
JCreator 4.00 LE della XINOX software, version 4.00.028
link: 
http://www.jcreator.com
Allego la routine che ho scritto, per completezza.
----------------------
/**
  * @(#)ProvaIf.java
  *
  * ProvaIf application
  *
  * @author
  * @version 1.00 2007/10/16
  */
import java.io.*;
import java.lang.*;
public class ProvaIf {
public static void main(String[] args) {
 
   String App = ";";
   String AppoChar = " ";
   String Appo2Char = " ";
   BufferedReader AppoIn = null;
   try
   {
      FileReader Appo = new java.io.FileReader("NomeFile");			
AppoIn = new BufferedReader(Appo);
      String Linea = AppoIn.readLine();
      AppoChar = String.valueOf(Linea.charAt(0));
      System.out.println("AppoChar: "+AppoChar);
   }
   catch (Exception e)
   {   System.err.println("File input error");
   }
   BufferedReader Appo2In = null;
   try
   {
      FileReader Appo2 = new java.io.FileReader("NomeFile");
      Appo2In = new BufferedReader(Appo2);
      String Linea2 = Appo2In.readLine();					     Appo2Char =
String.valueOf(Linea2.charAt(0));
      System.out.println("Appo2Char: "+Appo2Char);
   }
   catch (Exception e)
   {   System.err.println("File input error");
   }
 
   if (AppoChar == Appo2Char)
     	System.out.println("Uguale");
	else System.out.println("Diverso");
   }
}
--------------------------------------------------------------------
Eseguendo questo programma, il risultato dell'ultima stampa e' sempre
"Diverso". Qualcuno mi sa dire cosa succede?
Ciao
Nullius


Ti ho evidenziato il punto sbagliato col carattere'†'.

 
Scusami, ma non riesco proprio a trovare dove hai inserito il carattere 
per segnalare l'errore.
Nullius

    if (AppoChar == Appo2Char)
     if (AppoChar.equals(Appo2Char))


 Chi mi puo' spiegare cosa succede?
le stringhe si confrontano con il metodo equals(), non con l'operatore 
== che *notoriamente* confronta i reference e non gli oggetti.

rootkit wrote:

Falso.
Le stringhe sono oggetti immutabili, quindi possono confrontate con
l'operatore ==, ma la cosa dipende dalla JVM. Nelle JVM recenti i
riferimenti (diversi) a stringhe uguali si risolvono come riferimenti alla
stessa stringa in memoria.
Ad esempio:
    public static void main(String[] args) {
        String s1 = "ciao";
        String s2 = "ciao";
 
        String s3 = "c" + "i" + "a" + "o";
 
        if( s1.equals(s2) )
            System.out.println("Equals dice <uguali>");
 
        if( s1 == s2 )
            System.out.println("== dice <uguali>");
 
        if( s1.equals(s3) )
            System.out.println("Equals dice <uguali>");
 
        if( s1 == s3 )
            System.out.println("== dice <uguali>");
    }
su una jvm 6 stampa
Equals dice <uguali>
== dice <uguali>
Equals dice <uguali>
== dice <uguali>
Tuttavia, usare equals è in generale più corretto perché
1) non si sa su quale jvm si eseguirà il codice
2) il confronto fra oggetti è un'operazione che deve sottostare al contratto
degli oggetti stessi, e quindi ad un loro metodo.
Luca

> oggetti.
LF> Falso.  Le stringhe sono oggetti immutabili, quindi possono
LF> confrontate con l'operatore ==, ma la cosa dipende dalla
LF> JVM. Nelle JVM recenti i riferimenti (diversi) a stringhe uguali
LF> si risolvono come riferimenti alla stessa stringa in memoria.
Falso, devi ripassare!!!! :> 
Le stringhe sono oggetti immutabili: vero.
Le stringhe si  possono confrontare con ==: falso.  Il fatto che siano
immutabili non c'entra per nulla con la semantica di ==.
== confronta i riferimenti, non lo stato degli oggetti.
Per la  cronaca ho verificato  con *questo* codice  che la JVM  1.6 si
comporta come la 1.5 che si comporta come la 1.4 ...
/**
 * Classe che dimostra che qualcuno deve ripassare....
 *
 * @author <a href="mailto:
sa...@spammer.impiccati.it
">Gian Uberto Lauri</a>
 * @version $Revision$
 */
public class Foo {
    /**
     * <code>main</code>
     *
     * @param args a <code>String</code>
     */
    public static final void main(final String[] args) {
       String s1 = "ciao";
       String s2 = "ciao";
       String s3 = "c" + "i" + "a" + "o";
       if( s1.equals(s2) )
           System.out.println("Equals dice <uguali>");
       if( s1 == s2 )
           System.out.println("== dice <uguali>");
       if( s1.equals(s3) )
           System.out.println("Equals dice <uguali>");
       if( s1 == s3 )
           System.out.println("== dice <uguali>");
       s1 = s1 + "";
       if( s1 == s3 )
           System.out.println("== dice <uguali>");
       else
           System.out.println("Qualcuno dovrebbe ripassare");
    }
}
Cosa succede ?
In fase di compilazione il compilatore trasforma "c" + "i" + "a" + "o"
in "ciao"  (quell'espressione può essere valutata una  volta per tutte
in quanto compaiono solo costanti).
Quindi quando la classe viene  caricata in memoria e vengono calcolati
gli hash  dei literal di tipo  stringa viene fuori che  gli hash delle
stringhe assegnate a s1, s2 ed  s3 coincidono, quindi a tutte e tre le
variabili  viene assegnato  lo  stesso riferimento  ed  in memoria  si
conserva una sola stringa.
Quando poi si arriva allo statement s1 = s1 + "" il compilatore non si
azzarda a fare alcuna ottimizzazione(*). 
Ergo,  a  runtime  viene  allocato uno  StringBuffer  (presumibilmente
contenente il  contenuto di s1) e  a quello viene  accodata la stringa
vuota,  infine  si  richiama  il  toString()  sullo  StringBuffer  per
estrarre il risultato.
Dato che  a runtime la JVM non  si rimette a calcolare  l'hash di ogni
nuova  stringa che  crea (proponilo  a Gosling  e  molto probabilmente
commenterà con un  "Are you nuts?")  ci saranno  due oggetti String il
cui contenuto è  "ciao" nello heap. Ed a questo  punto s1 punterà alla
nuova stringa  (punterà, dato che  a naso in TUTTE  le implementazioni
della  JVM  di questo  pianeta  i  riferimenti  sono implementati  con
puntatori), mentre s3 rimane col vecchio puntatore. E quindi == dice
"Qualcuno dovrebbe ripassare".
(*)la  può fare  facilmente  un  umano, per  quello  che riguarda  una
macchina direi che  la spesa di complicare il  compilatore non vale il
guadagno in prestazioni.
-- 
 /\           ___                                    Ubuntu: ancient
/___/\_|_|\_|__|___Gian Uberto Lauri_____               African word
  //--\| | \|  |   Integralista GNUslamico            meaning "I can
\/                   e coltivatore diretto               not install
                               di software                   Debian"
a Cesare avrei detto di scrivermi | I would have told Caesar to write
a 
fn...@rat.vg
                    | me at 
fn...@rat.vg

 Falso, devi ripassare!!!! :>
In soldoni:
String a = "pippo";
String b = "pippo";
a==b --> true perchè la JVM va a pescare dalla stessa piscina delle
stringhe.
String a = new String("pippo");
String b = new String("pippo");
a==b --> false perchè i reference sono diversi.
Che le stringhe siano immutabili è vero ma non c'entra nulla.
Stringa immutabile vuol dire che se io ho
String a = new String("pippo");
a = a + "pluto";
la mia nuova a è frutto di una nuova istanza e che quindi nella
piscina suddetta ho ancora un "pippo" che ci sguazza dentro...:)

 stessa stringa in memoria.
stai facendo una confusione di concetti da paura :)
intanto dovresti deciderti: o si può fare o non si può fare, una
regola semantica non dipende dalla jvm.
inoltre ribadisco che fai confusione fra stringhe costanti (che la jvm
giustamente può ottimizzare) e il concetto di immutabilità degli
oggetti string. immutabile vuol dire che la classe string non ha
metodi per modificare il valore di una istanza. il concetto a cui fai
riferimento invece è il seguente:
String s1 = "Prova";
String s2 = "Prova";
in questo caso può essere vero (ma non è certo) che s1 == s2 grazie al
compilatore "intelligente" che per ottimizzare mappa le stringhe
costanti uguali nella stessa locazione di memoria. se però banalmente
fai:
String s1 = "Prova";
String s2 = new String("Prova");
ecco che s1 != s2 anche se le stringhe sono equivalenti perchè
contengono la stessa sequenza di caratteri.


invece di == usa .compareTo()
vedrai che viene!!!!!!!

 writes:
s> invece di == usa .compareTo() vedrai che viene!!!!!!!
compareTo funziona benissimo salvo che non è la scelta migliore:
          hai bisogno  di un ulteriore  test (comparazione con  0) per
          stabilire se sono uguali.
          non tutti gli oggetti sono Comparable (gli String si :) )


 Sto uscendo pazzo per un semplice "if".
[CUT]
Grazie a tutti, ho risolto usando "equals".
Ve l'avevo detto che ero niubbo.
Tra l'altro, ho visto che usando variabili di tipo char avrebbe 
funziona, comparando la variabile il carattere con ';' ed usando ==.
Solo che preferisco usare variabili String, visto che dovrò cercare 
anche stringhe di caratteri.
Grazie ancora per l'aiuto.
Nullius

 Un saluto a tutto in NG.
[CUT]
Gia' che ci sono, approfitto ancora un po' di voi.
Anche se probabilmente e' gia' stato detto, mi segnalate quakche buon 
libro per imparare Java come si deve, partendo dalle basi?
Ancora grazie.
Nullius

 Luca
Luca Ferrari, questo nome non mi e' nuovo.
Sei per caso Capitan Novara?
Nullius

> sono un po' niubbo.  Sto uscendo pazzo per un semplice "if".
N> [CUT]
N> Grazie a tutti, ho risolto usando "equals".  Ve l'avevo detto che
N> ero niubbo.  Tra l'altro, ho visto che usando variabili di tipo
N> char avrebbe funziona
Le variabili di tipo char non sono oggetti ma tipi "primitivi".
I tipi primitivi sono essenzialmente numerici (byte, short, int, long,
float e  double) cui si aggiungono i  due tipi boolean e  char che, se
per un  umano non lo  sono, per la  macchina lo rimangono in  quanto i
valori "logici" che  noi gli attribuiamo (vero, falso,  'A', '@' etc.)
vengono  fisicamente rappresentati  con numeri  interi. Per  i  char è
anche  possibile  fare  un'aritmetica  
		char a='A';
		char b = (char)(a + '\u0001');
		System.out.println(b);
in  quanto  si sa  quanto  sono  "grossi", cosa  che  non  vale per  i
boolean. Questi  ultimi contengono un  solo bit di informazione  ma la
dimensione non è definita, come indicato in:
http://java.sun.com/docs/books/tutorial/java/nutsandbolts/datatypes.html
Libri: se  non hai grossi problemi  con l'inglese ci  sono i documenti
sul sito  web di Sun, il  Gosling e Core  Java di Cay S.   Horstmann e
Gary Cornell.  Non so se ci sono edizioni italiane.
Ho sentito parlare bene di "Thinking  in java". Ho dato una occhiata a
"Thinking in C++" e non era quello che mi serviva al momento (un libro
sul  lingaggio) ma  può  essere utile  come  libro su  "come usare  il
linguaggio".


 
Il compilatore non può fare ottimizzazioni certe su valori che dipendono dal
run-time. 
Luca

Nullius wrote:

No.

> 
LF> Il compilatore non può fare ottimizzazioni certe su valori che dipendono dal
LF> run-time.
Non è dipendente dal runtime
String pippo = pluto + "" + paperino + 1;
Al momento  della compilazione il  compilatore sa se pluto  o paperino
sono String o meno.
Supponiamo che  paperino sia String  e pluto Tipo.  E  volendo sarebbe
possibile  scartare  la stringa  vuota  dato  che l'albero  sintattico
passerebbe da
     +
    / \
   /   \
pluto   + (il valore del risultato è il contenuto di paperino,
       / \   tipo String riconoscibile dal compilatore)
      /   \
     ""   paperino
a
    +
   / \
  /   \
pluto  paoerino (sempte tipo String, il valore non cambia)
solo  che il  compilatore  dovrebbe essere  complicato.  Troppo per  i
vantaggi ottenibili. E quindi non si fa.


 String pippo = pluto + "" + paperino + 1;
Dipende si dal run-time, non sempre è possibile stabilire il tipo e il
valore di un riferimento a tempo di compilazione. Se fosse così il
compilatore potrebbe rilevare un sacco di cose, dall'ArrayIndexOutOfBound
ai permessi....
Luca

> Non hanno voluto farla e quindi hanno fatto la specifica.
LF> Cioè prima non fanno le cose e poi ci fanno le specifiche attorno?
No, ci hanno pensato a come sarebbe stato farla e poi hanno fatto
la specifica. Si chiama progettazione, che sappia :)
LF> Dipende si dal run-time, non sempre è possibile stabilire il tipo
LF> e il valore di un riferimento a tempo di compilazione.
Ma con le String si. Non puoi estendere String, è final.
