Enrera
Mòdul 3
Iniciació a la programació en Java
  Pràctica
1
2
3
4
   
Exercicis
Exercicis
 
 
  Vectors, arraylists, conversió (casting)...
   
  Més sofisticat: vectors i arraylists
   
Atenció !

Una matriu (un array) és, en realitat, un constructe molt primitiu,fins i tot massa. Manipular una matriu més enllà de posar-hi elements i fer-hi referència pot exigir un munt de codi, no sempre prou eficient i segur si el programador no és un excel·lent coneixedor de tots els racons i del funcionament de la Màquina Virtual.

Com a solució a això el paquet (package) java.util proporciona dues classes que contenen una bona col·lecció de mètodes que, en essència, no són més que manipulacions sofisticades de matrius unidimensionals. Les dues classes són:

  • java.util.Vector

  • java.util.ArrayList
  Vegem què aporten aquestes dues classes:
  • Ambdues classes guarden els objectes a la variable Object[] elementData, és a dir, en una matriu d'objectes de la classe Object. Però aquesta variable és protected,no accessible a mètodes d'altres classes i, per tant, invisible al programador.

  • El fet que elementData sigui del tipus Object (la mare de totes les classes!) implica que, en un vector o en un arraylist, hi podem enmagatzemar objectes de tipus diferents els uns dels altres; tots ells hi queden guardats com a Object.

  • A diferència de les matrius (arrays) que, a l' inicialitzar-les, cal establir ja quina serà la seva longitud, la seva capacitat, la qual restarà inamovible, tant els vectors com els arraylists tenen capacitat variable: això vol dir que si requerim guardar-hi un nou objecte quan la matriu elementData ja es plena, la pròpia classe, sense intervenció de codi extern, redimensiona la matriu elementData per tal de rebre aquest nou objecte.

  • Ambdues classes contenen el mètode int size(), el qual ens retorna, no pas la longitud de la matriu elementData, sinó el nombre d'objectes efectius que guarda.

  • Ambdues classes contenen mètodes per a insertar-hi objectes, per poder saber si determinat objecte hi és o no, per buidar-les, etc. Consulteu la documentació de les classes java.util.Vector i java.util.ArrayList.

  • El comportament de vectors i arraylists és diferent quan han d'incrementar la seva capacitat: per defecte, un vector que ha de augmentar la seva capacitat, la duplica. En canvi, un arraylist que hagi d'augmentar la seva capacitat només la fa créixer en un 50%. D'altra banda, en un dels mètodes constructors de la classe dels vectors, public Vector(int initialCapacity,int capacityIncrement) hom pot fixar un increment de capacitat diferent del 100% de defecte. Aquesta possibilitat no hi és en els arraylists. Com ja es pot comprendre, l'operació d'augmentar la capacitat d'un vector o d'un arraylist consumeix temps, cosa que el programador ha de tenir en compte.

  • La diferència fonamental entre vectors i arraylists és que tots els mètodes capaços de canviar el contingut d'un vector, de referir-se a la quantitat d'elements o a la seva ordenació, són synchronized mentre que els corresponents dels arraylists no ho són. Això vol dir que els mètodes de la classe java.util.Vector són segurs quant a "atacs" d'altres fils (threads, vegeu les pràctiques 1 i 2 del mòdul 7): cap altre fil (thread) pot actuar sobre el vector mentre s'està executant algun d'aquests mètodes! En canvi, la manipulació directa de matrius o l'execució de mètodes de la classe java.util.ArrayList són, en aquest sentit, ben insegurs. Però es paga un preu: els mètodes synchronized són un pèl més lents que els que no ho són.
Pràctica El programa següent (Projecte Vectors "Empty Project", fitxer Vectors.java) il·lustra l'ús de vectors:
   
 
import java.util.*; // Importació del paquet java.util, on hi ha
                    // la classe Vector

class Vectors {

    Vector vector; // Declaració d'un objecte de la classe
                   // java.util.Vector

    public static void main (String[] args) {
        Vectors mainApp=new Vectors(); // Es crea l'objecte mainApp
                                       // fill de la classe Vectors.
                                       // Això es fa així per tal
                                       // de poder cridar variables
                                       // i mètodes sense la
                                       // clàusula "static".

        mainApp.vector=new Vector(); // Construcció efectiva de
                                     // l'objecte vector, cosa que
                                     // vol dir reservar-li l'espai
                                     // corresponent a la memòria

        mainApp.omple(); // Crida al mètode void omple()
        mainApp.mostraElVector(); // Crida al mètode
                                  // void mostraElVector()

    }

    void omple () { // Addició d'objectes diversos al vector
        vector.add(new Tipus_1("u"));
        vector.add(new Tipus_1("dos"));
        vector.add(new Tipus_2("tres",null));
        vector.add(new Tipus_1("quatre"));
        String[] paraules={"Pedra","Tisora","Paper"};
        vector.add(new Tipus_2("cinc",paraules));
        vector.add(new Tipus_1("sis"));
        vector.add(new Double(Math.PI));
    }

    void mostraElVector () { // "Publicació" a la sortida stàndard
                             // del sistema del contingut del vector

        System.out.println("El vector conte "+ // Nombre d'elements
                           vector.size()+      // que conté el
                           " elements");       // vector
        System.out.println("El vector te "+    // Nombre d'elements
                           "capacitat per a "+ // que pot contenir
                           vector.capacity()+  // el vector. Si la
                           " elements");       // capacitat se
                                               // sobrepassa, el
                                               // vector augmenta
                                               // la seva capacitat
                                               // automàticament.

        int ind=0;
            for(Enumeration enum=vector.elements(); // Obtenció
                enum.hasMoreElements();) {          // d'un objecte
                                            // java.util.Enumeration
                                            // que conté la llista
                                            // dels objectes que hi
                                            // ha al vector

                Object obj=enum.nextElement(); // Referència a un
                                               // objecte determinat
                                               // com a instància de
                                               // java.lang.Object

                System.out.print((ind++)+". ");
                    if (obj instanceof Tipus_1) { // Selecció del
                                                  // tipus d'objecte
                                                  // mitjançant
                                                  // l'operador
                                                  // "instanceof"

                        Tipus_1 t_1=(Tipus_1)obj; // Casting
                                                  // imprescindible!

                        t_1.explicoQuiSoc();
                    } else if (obj instanceof Tipus_2) {
                        Tipus_2 t_2=(Tipus_2)obj; // Casting
                                                  // imprescindible!

                        t_2.explicoQuiSoc();
                    } else if (obj instanceof Double) {
                        Double db=(Double)obj; // Casting
                                               // imprescindible!

                        System.out.println(db.doubleValue());
                    }
            }
    }

}

class Tipus_1 {

    String nom; // Variable de classe

    public Tipus_1 (String nm) { // Constructor
        nom=nm;
    }

    public void explicoQuiSoc () {
        System.out.println("Soc "+nom+
                           ", un objecte fill de la classe Tipus_1"
                          );
    }

}

class Tipus_2 {

    String nom; // Variable de classe
    String[] paraules; // Variable de classe

    public Tipus_2 (String nm,String[] prls) { // Constructor
        nom=nm;
        paraules=prls;
    }

    public void explicoQuiSoc () {
        System.out.println("Soc "+nom+
                           ", un objecte fill de la classe Tipus_2"
                          );
            if (paraules!=null) {
                int quantes=paraules.length; // Quantes paraules
                                             // guardo?

                    for (int i=0;i<quantes;i++) {
                        System.out.println(paraules[i]);
                    }
            } else {
                System.out.println("No guardo cap paraula...");
            }
    }

}

   
Atenció !

Ara cal fixar-se en aquests punts:

  • Com que farem servir instàncies de les classes java.util.Vector i java.util.Enumeration, cal importar el paquet (package) java.util.

  • Els objectes que posem a dintre del vector mitjançant el mètode de la classe java.util.Vector, public boolean add(Object obj) (no fem cas del resultat true o false) hi entren com a instàncies de la classe java.lang.Object, que és la mare de totes les classes. Quan els haguem de recuperar, amb algun dels mètodes de la classe java.util.Vector,

    • public Object elementAt(int index)

    • public Enumeration elements()

    • public Object firstElement()

    • public Object get(int index)

    • public Object lastElement()

    • public Object remove(int index)

    • public Object[] toArray()

    els obtindrem també com a instàncies de la classe java.lang.Object, la qual no conté pas el mètode void explicoQuiSoc()! que, a l'exemple, cridem tot just a continuació. Observeu com, mitjançant l'ús de l'operador instanceof

    if (obj instanceof Tipus_1) { ...

    ens assegurem que obj és una instància d'una certa classe i com, després, obtenim l'objecte t_1 com a instància efectiva d'aquesta classe per tal de poder cridar ja als seus mètodes:

    Tipus_1 t_1=(Tipus_1)obj;

    La conversió anterior (casting) és d'us comú en el maneig de matrius, vectors i arraylists.

    Naturalment, ja podeu pensar que la conversió (casting) només funciona si a la sentència:

    Tipus_1 t_1=(Tipus_1)obj;

    l'objecte obj és, efectivament, una instància de la classe Tipus_1. Llavors, t_1 és l'objecte obj, i Java el considera com a instància de la classe Tipus_1. La conversió (casting) inversa, com és lògic, és innecessària: si la classe Tipus_1 deriva de la classe Tipus_2 (filla, neta, besneta, etc...), llavors tots els objectes que siguin instàncies de Tipus_1 són, automàticament, instàncies de Tipus_2.

  • Resulta molt convenient recuperar les referències als objectes continguts en un vector com a una instància de la classe java.util.Enumeration. Això es fa mitjançant el mètode de la classe java.util.Vector, public Enumeration elements(). Amb el mètode de la classe java.util.Enumeration, public boolean hasMoreElements(), podem arar recuperant-ne les referències successivament, tal com es fa en l'estructura for (... de l'exemple:

    for(Enumeration enum=vector.elements();enum.hasMoreElements();)
    {
        Object obj=enum.nextElement();
        ...
        ...
        ...
    }


   
 

 

 
Amunt