MokaByte 72- Marzo 2003 
Corso di programmazione Java
XI parte: Classi Standard
di
Andrea Gini
Dopo aver introdotto l'uso degli oggetti nel linguaggio Java, è giunto il momento di studiare un piccolo insieme di oggetti di uso molto comune. Questi semplici oggetti costituiscono il viatico fondamentale per poter iniziare a realizzare i primi programmi Java veramente interessanti

Stringhe
Negli esempi visti fino ad ora, abbiamo sempre lavorato con variabili numeriche. Le variabili di tipo char permettono di memorizzare un singolo carattere alla volta, ma è chiaro che per applicazioni di manipolazione testi è necessario disporre di uno strumento che permetta di lavorare su parole o su intere frasi: la Stringa. In molti linguaggi di programmazione, tra i quali C e Pascal, si utilizzano come stringhe array di caratteri; nel linguaggio Java, in coerenza con la sua impostazione Object Oriented, le stringhe vengono implementate come oggetti.
L'oggetto String contiene una sequenza di caratteri di lunghezza arbitraria che può essere utilizzata per memorizzare parole, frasi o testi di qualunque dimensione. Il contenuto di un oggetto String viene deciso al momento della sua creazione, e non può essere modificato in seguito:

String s = new String("Questo è un esempio di stringa di testo");

I metodi di String permettono di ispezionare il contenuto della stringa, di estrarne dei frammenti (sottostringhe), di verificare l'uguaglianza con un'altra stringa ed altre interessanti operazioni. Nei prossimi paragrafi analizzeremo a fondo caratteristiche e di questo oggetto fondamentale.
Le Stringhe e il linguaggio Java: creazione, concatenazione ed uguaglianza
Le stringhe sono uno strumento così importante nella programmazione, che i progettisti di Java hanno deciso di introdurre dei costrutti nel linguaggio per semplificarne l'uso.
Creazione
In fase di creazione, invece di ricorrere all'operatore new, possiamo utilizzare direttamente un letterale stringa, ossia una qualsiasi sequenza di caratteri racchiusa tra doppi apici, come nel seguente esempio:

String s = "Ciao"; // è eqivalente a String s = new String("Ciao");

Ogni letterale stringa viene considerato automaticamente come un oggetto String, pertanto è possibile applicare la chiamata a metodo direttamente su di esso. La seguente riga di codice:

System.out.println("Ciao".length());

stampa a schermo la lunghezza della stringa, ossia il risultato della chiamata del metodo length() sulla stringa "Ciao", non il suo contenuto.
Concatenazione
Java prevede l'uso di + come operatore di concatenazione in alternativa al metodo concat(String s). Grazie all'operatore +, un'istruzione come

String s = "Ecco ".concat("una").concat"Stringa ".concat("Concatenata");

può essere sostituita dalla più sintetica ed intuitiva

String s = "Ecco " + "una " + "Stringa " + "Concatenata";

L'operatore di concatenazione è particolarmente utile quando si devono creare stringhe così lunghe da occupare più di una riga. Dal momento che i letterali stringa non possono essere interrotti con un ritorno a capo, è possibile concatenare più letterali stringa da una riga ricorrendo all'operatore +:

String s = "La vispa Teresa, " +
"avea tra l'erbetta " +
"a volo sorpresa " +
"gentil farfalletta";

L'operatore + effettua in modo automatico e trasparente la conversione dei tipi primitivi in stringa:

int risultato = 12 * a;
System.out.println("Il contenuto della variabile risultato è: " + risultato);


Operatore di uguaglianza e metodo Equals
Quando si lavora su oggetti o vettori, bisogna prestare molta attenzione all'uso dell'operatore di uguaglianza "==" che, a differenza di quanto ci si potrebbe aspettare, non serve a testare l'uguaglianza di contenuto, ma solo l'uguaglianza di riferimento. In Figura 1 vediamo due variabili s1 e s2 che puntano a due distinti oggetti stringa. Nonostante entrambe le strighe contengano la parola "Ciao!", il test con l'operatore "==" restituirà false, dal momento che gli oggetti puntati dalle due variabili sono due oggetti differenti, localizzati in due zone di memoria distinte. Per verificare se due stringhe hanno lo stesso contenuto, come in questo caso, è necessario ricorrere al metodo equals(String s).

In Figura 2, invece, le variabili s1 ed s2 puntano allo stesso oggetto stringa: in questo caso il test con l'operatore "==" restituirà true. Abbiamo uno scenario di questo tipo ogni volta che assegniamo lo stesso oggetto a più di una variabile:

s1 = "Ciao!";
s2 = s1; // s1 ed s2 puntano allo stesso oggetto stringa

 


Figura 1
- Le variabili s1 ed s2 puntano due oggetti stringa con lo stesso contenuto

 


Figura 2 -
Le variabili s1 ed s2 puntano allo stesso oggetto stringa

 

Operazioni fondamentali
L'oggetto String comprende più di 50 metodi; quelli veramente fondamentali comunque sono appena 6. Vediamoli qui di seguito:

  • String substring(int beginIndex , int endIndex ): Restituisce una sottostringa che contiene il testo incluso tra beginIndex e endIndex-1.
  • char charAt(int i): restituisce l'i-esimo carattere della stringa.
  • int length(): restituisce la lunghezza della stringa
  • int compareTo(String anotherString): effettua una comparazione con un'altra stringa, e restituisce 0 se l'argomento è una stringa uguale, un valore inferiore a 0 se l'argomento è una stringa più avanti rispetto all'ordine alfabetico, un valore superiore a 0 in caso contrario. Se si desidera che nella comparazione non venga considerata la differenza tra maiuscole e minuscole, si può utilizzare il metodo compareToIgnoreCase(String s).
  • String concat(String str): Effettua la concatenazione con la stringa str. Come già visto è equivalente all'operatore +.
  • boolean equals(Object o): Restituisce true se la stringa risulta uguale all'oggetto passato come parametro. Se si desidera che venga ignorato l'ordine tra maiuscole e minuscole, si può utilizzare il metodo equalsIgnoreCase(String s).

Si noti che i metodi concat e substring non modificano l'oggetto Stringa su cui vengono invocati, ma creano un nuovo oggetto che ha per valore il risultato dell'operazione. In Figura 3 vediamo una rappresentazione della memoria dopo l'esecuzione delle seguenti istruzioni:

String s1 = "Rosso di sera, bel tempo si spera";
String s2 = s1.substring(9,1);
String s3 = s2.concat("ta");


Figura 3
- Rappresentazione della memoria del computer
dopo l'esecuzione di una serie di operazioni su stringhe


Altri metodi utili
L'oggetto String prevede diversi altri metodi utili. Ne vediamo solo alcuni. Ecco un gruppo di metodi utili per l'interrogazione

  • boolean startsWith(String prefix): restituisce true se la stringa inizia con il prefisso specificato dal parametro.
  • boolean endsWith(String suffix): restituisce true se la stringa finisce con il suffisso specificato dal parametro.
  • int indexOf(String str): scandisce la stringa dall'inizio alla fine, e verifica se contiene o meno la stringa specificata come parametro. Se la trova, restituisce l'indice del primo carattere della sotto stringa cercata, altrimenti restituisce -1.

Un esempio tipico di uso di questi metodi è il controllo di validità dell'estensione di un file:

import javax.swing.*;

public class ProvaEndsWith {

public static void main(String argv[]) {
String file = JOptionPane.showInputDialog(null, "Inserisci il nome di un file java valido");
if( file.endsWith(".java") )
JOptionPane.showMessageDialog(null,"Il nome del file è valido");
else
JOptionPane.showMessageDialog(null,"Il nome del file non termina con .java");
}
}

Un altro gruppo di metodi fornisce alcune funzionalità utili in fase di manipolazione:

  • String replace(char oldChar , char newChar): restituisce una stringa in cui il carattere specificato dal primo parametro è stato sostituito da quello indicato dal secondo.
  • String toLowerCase(): restitituisce una stringa in cui tutti I caratteri sono minuscoli.
  • String toUpperCase():restitituisce una stringa in cui tutti I caratteri sono maiuscoli.
  • String trim(): restituisce una stringa dalla quale sono stati rimossi gli eventuali spazi all'inizio e alla fine.

Come esempio riepilogativo, vediamo un programma che inverte le frasi:

import javax.swing.*;

public class Invertitore {

  public static void main(String argv[]) {
    String s = JOptionPane.showInputDialog(null,
                           "Inserisci una frase");
    s = s.trim();
    String inversa = "";

    for( int i = s.length() -1 ; i >= 0 ; i-- )
      inversa = inversa + s.charAt(i);

    JOptionPane.showMessageDialog(null,inversa);
  }
}

Il programma chiede all'utente di inserire una frase, quindi la scandisce lettera per lettera dall'ultima alla prima e le aggiunge alla stringa "inversa", creando in tal modo una frase speculare rispetto a quella inserita dall'operatore.

Nota: in questi e nei prossimi esempi si farà uso di piccole finestre di dialogo per l'input e l'output dei dati sullo schermo. La forma base dell'istruzione di input è:

String s = JOptionPane.showInputDialog(null, messaggio);

Questa istruzione mostra all'utente una finestra di dialogo con il messaggio specificato dal parametro, e restituisce la stringa inserita dall'operatore. Per creare invece una finestra di output che visualizzi a schermo un messaggio si ricorre alla seguente istruzione:

JOptionPane.showMessageDialog(null,messaggio);

Se si desidera utilizzare queste istruzioni nei propri programmi, è necessario inserire la direttiva

import javax.swing.*;

all'inizio del programma. Una descrizione completa dei controlli grafici e del loro utilizzo è rimandata all'apposito capitolo.

 

Vettori dinamici
Nel Capitolo 2 abbiamo imparato ad usare gli array per lavorare su insiemi di variabili di lunghezza prefissata. Il principale difetto degli array è la loro natura statica: la dimensione viene stabilita al momento della creazione, e non può cambiare in alcun modo. Questa limitazione viene superata dai vettori dinamici, entità software simili agli array, ma senza il limite di una dimensione prefissata. Le librerie Java offrono un buon numero di vettori dinamici, simili tra loro nell'interfaccia di programmazione ma diversi nell'implementazione interna: il più usato di questi è senza dubbio Vector.

 

Uso di Vector
Il Vector è un oggetto concettualmente simile ad un array, ma a differenza di quest'ultimo prevede un utilizzo esclusivamente Object Oriented. Esso va in primo luogo creato allo stesso modo di qualunque altro oggetto Java:

Vector v = new Vector();

Successivamente si può riempire il Vector utilizzando il metodo add(Object o), che aggiunge in coda alla lista l'oggetto passato come parametro:

String nome = "Un nome";
v.add(nome);

A differenza dell'array, che viene crato in modo da contenere valori di un certo tipo, il Vector memorizza oggetti di tipo generico: per questa ragione, al momento del prelievo, è necessario ricorrere all'operatore di casting per riconvertire l'elemento al suo tipo di origine:

String n = (String)v.get(1);

L'approccio completamente OO permette di incorporare un certo numero di funzionalità all'intero di Vector e di renderle disponibili sotto forma di metodi. Ad esempio, se si desidera conoscere la posizione di un elemento all'interno della lista, è sufficiente chiamare l'apposito metodo di ricerca:

int position = v.indexOf("Un nome");

Se invece si desidera creare una copia del vettore, si può utilizzare il metodo clone():

Vector nuovoVector = (Vector)v.clone() ;

Nota: Per usare il Vector all'interno di un programma è necessario aggiungere in testa al programma la direttiva

import java.util.*;

Metodi Fondamentali

  • boolean add(Object o): aggiunge l'elemento in coda alla lista.
  • void add(int index , Object element): aggiunge un elemento nella posizione specificata; gli elementi successivi vengono spostati in avanti di uno.
  • void clear(): svuota completamente la lista.
  • boolean isEmpty(): restituisce true se la lista è vuota
  • Object get(int i): restituisce l'i-esimo elemento della lista
  • int indexOf(Object o): restituisce l'indice dell'elemento passato come parametro, o -1 se l'elemento non è presente nella lista.
  • Object remove(index i): rimuove l'I-esimo elemento della lista, e sposta l'indice di tutti gli elementi successivi in avanti di un valore. Il metodo restituisce l'elemento appena rimosso.
  • Object set(int i , Object element): mette l'elemento specificato in I-esima posizione, in sostituzione dell'elemento preesistente. Il metodo restituisce l'elemento appena rimosso.
    ·int size(): restituisce la dimensione della lista.

Iterator
Per effettuare una determinata operazione su tutti gli elementi di un vettore dinamico, è possibile ricorrere ad un ciclo for come nel caso di un array:

for(int i=0;i<v.size();i++)
System.out.println((String)v.get(i));

Tuttavia, per semplificare questo tipo di operazione e per aggiungere un tocco di eleganza, è disponibile una soluzione OO che prevede il ricorso ad un apposito oggetto denominato Iterator, che può essere prelevato dal Vector usando un apposito metodo:

Iterator i = nomi.iterator();

L'Iterator è un oggetto molto semplice ed intuitivo che permette unicamente di scandire la lista dal primo all'ultimo elemento allo scopo di effettuare una determinata operazione su ciascuno dei suoi elementi. L'utilizzo canonico di un Iterator ha la forma:

while(i.hasNext()) // verifica se c'è un ulteriore elemento
System.out.println((String)i.next()); // preleva l'elemento e lo utilizza

Conversione in array
Il Vector dispone anche di un metodo che permette di trasferire il suo contenuto in un array:

Object[] toArray(Object[] a)

L'array fornito come parametro viene ridimensionato, riempito con gli elementi del Vector e restituito all'utente. Dal momento il Vector memorizza gli elementi con il tipo generico Object, è necessario passare come parametro un array del tipo giusto ed effettuare il casting sul valore di ritorno:

String[] lista = (String[])v.toArray(new String[0]);

 

Un esempio riepilogativo
Un piccolo esempio permetterà di illustrare un uso tipico del Vector. All'utente viene richiesto di inserire un elenco di nomi attraverso una serie di finestre di dialogo; non appena l'utente avrà segnalato, premendo il pulsante annulla, il termine della fase di inserimento, il vettore verrà scandito con un Iterator, e i suoi valori verranno stampati a schermo:

import java.util.*;
import javax.swing.*;

public class ListaNomi {

  public static void main(String argv[]) {
    Vector v = new Vector();

    while(true) {
      String nome = JOptionPane.showInputDialog(null,
                                "Inserisci un nome");
      if(nome == null || nome.equals(""))
        break;
      else
        v.add(nome);
    }

    Iterator i = v.iterator();

    while(i.hasNext()) {
      System.out.println((String)i.next());
    }
  }
}

 

Mappe Hash
La mappa Hash è un contenitore di oggetti che mappa chiavi con valori, senza mantenere informazioni posizionali. In parole semplici, una mappa Hash è un contenitore di oggetti in cui gli elementi vengono associati ad oggetti chiave anziché a valori numerici. La mappa hash più comunemente utilizzata è Hashtable:

Hashtable h = new Hashtable();

Per depositare elementi nella mappa, bisogna utilizzare il metodo put(Object chiave,Object valore), che richiede come chiave un valore univoco (tipicamente una stringa di testo) e come valore l'oggetto da memorizzare:

h.put("nome","Mario");
h.put("cognome","Rossi");
h.put("età","25 anni");
h.put("lavoro","insegnante");

La mappa può ovviamente contenere elementi duplicati; non può invece associare più di un elemento alla stessa chiave. Per recuperare l'elemento, sarà sufficiente fornire la chiave attraverso il metodo get:

String s = (String)h.get("nome");

Anche in questo caso, in fase di recupero è necessario ricorrere all'operatore di casting.
Metodi Principali

  • Object put(Object key , Object value): inserisce un nuovo oggetto nella mappa, associandolo alla chiave specificata.
  • Object get(Object key): preleva dalla mappa l'oggetto associato con la chiave specificata.
  • Object remove(Object key): rimuove dalla mappa l'oggetto associato alla chiave specificata.
  • void clear(): svuota la mappa.
  • int size(): restituisce il numero di coppie chiave-valore contenute nella mappa.
  • boolean isEmpty(): restituisce true se la mappa è vuota.
  • boolean containsKey(Object key): verifica se la mappa contiene la chiave specificata.
  • boolean containsValue(Object value): verifica se la mappa contiene il valore specificato.

Estrazione dell'insieme di chiavi o valori
Per ottenere l'insieme delle chiavi o dei valori, esiste un'apposita coppia di metodi:

Set keySet(): restituisce l'insieme delle chiavi.
Collection values(): restituisce la lista dei valori.

Questi metodi restituiscono oggetti di tipo Set e Collection; entrambi questi oggetti hanno metodi comuni a Vector, in particolare il metodo iterator() che rende possibile la scansione degli elementi:

Iterator keyIterator = h.keySet().iterator();
System.out.println("L'insieme delle chiavi è:");
while(keyIterator.hasNext())
System.out.println((String)keyIterator.next());

Iterator elementIterator = h.values().iterator();
System.out.println("L'insieme degli elementi è:");
while(elementIterator.hasNext())
System.out.println((String)elementIterator.next());


Oggetti Involucro
Vector e Hashtable possono memorizzare solamente Oggetti: se si desidera memorizzare al loro interno valori primitivi è necessario racchiuderli in appositi oggetti involucro. Il linguaggio Java prevede un oggetto Involucro per ogni tipo primitivo: Boolean, Byte, Short, Integer, Long, Float e Double. Il loro uso è abbastanza semplice: in fase di creazione è sufficiente specificare nel costruttore il valore da inglobare:

Integer i = new Integer(15);

E' disponibile anche un costruttore che accetta valori sotto forma di String, una funzionalità estremamente utile in fase di inserimento dati:

String numeroStringa = JOptionPane.showInputDialog(null, "Inserisci un numero intero");
Integer numeroInteger = new Integer(numeroStringa);
int numero = numeroInteger.intValue();

Per recuperare il valore nel suo formato naturale, ogni oggetto Involucro dispone di un apposito metodo:

boolean BooleanValue() su oggetti di tipo Boolean
byte byteValue() su oggetti di tipo Byte
short shortValue() su oggetti di tipo Short
int intValue() su oggetti di tipo Integer
long longValue() su oggetti di tipo Long
float floatValue() su oggetti di tipo Float
double doubleValue() su oggetti di tipo Double


Conclusioni
Questo mese abbiamo imparato ad utilizzare alcuni oggetti molto comuni nei programmi Java: essi ci hanno permesso di realizzare alcuni esempi che chiariscono l'uso degli oggetti all'interno delle nostre applicazioni. Il mese prossimo impareremo a progettare oggetti partendo da zero, e a realizzare con essi sofisticate strutture dati.

MokaByte® è un marchio registrato da MokaByte s.r.l. 
Java®, Jini® e tutti i nomi derivati sono marchi registrati da Sun Microsystems.
Tutti i diritti riservati. E' vietata la riproduzione anche parziale.
Per comunicazioni inviare una mail a info@mokabyte.it