Arreglos H. Tejeda Marzo 2016
´Indice 1. Declaraci´ on de arreglos
2
2. Inicializaci´ on de arreglos
4
3. Uso de sub´ındices variables en un arreglo
5
4. Declaraci´ on y uso de arreglos de objetos
8
5. B´ usqueda y uso de arreglos paralelos
11
6. Paso y devoluci´ on de arreglos en m´ etodos
16
Para guardar un valor y usarlo se usan variables. Cuando se usan ciclos las variables se “reciclan” porque son usadas varias veces; ya que despu´es de haber creado una variable, asignarle un valor, usar ´este, y luego, en iteraciones sucesivas del ciclo se modifica la variable. Al reusar la variable da la apariencia de que esta guarda valores diferentes. En ocasiones se tienen situaciones en las cuales guardar un solo valor a la vez en memoria no es suficiente. Por ejemplo, un supervisor de ventas que vigila 20 empleados podr´ıa querer determinar si cada empleado ha hecho ventas por encima, o por debajo, del promedio. Con el valor de las ventas del primer empleado ingresado en una aplicaci´on, no se puede determinar si est´a por encima, o por debajo, porque todav´ıa no se sabe el promedio hasta que se tengan los 20 valores. Desafortunadamente, si se intenta asignar 20 valores de venta a la misma variable, cuando se asigne el valor para el segundo empleado, esta reemplaza el valor del primer empleado. Una soluci´on posible es crear 20 variables de ventas de empleados separadas, cada una con un nombre u ´nico, para poder guardar todas las ventas hasta que se pueda calcular el promedio. 1
Una desventaja es que se ocupan 20 nombres de variables diferentes para asignar los valores y 20 sentencias de asignaci´on diferentes. Para los nombres de las 20 variables diferentes la sentencia que calcula el total de la venta ser´a compleja, quedando como: total = primeraCant + segundaCant + terceraCant + ... La sentencia anterior trabajar´ıa para 20 vendedores, pero ¿qu´e suceder´ıa si se tienen 5,000 vendedores?
1.
Declaraci´ on de arreglos
La mejor soluci´on al problema de la secci´on anterior, el de los 5,000 vendedores, es creando un arreglo. Un arreglo es una lista de datos con nombre teniendo todos ellos el mismo tipo. Cada dato es un elemento del arreglo. Se declara una variable arreglo de la misma forma como se declara una variable simple, pero se inserta un par de corchetes despu´es del tipo. Por ejemplo, para declarar un arreglo de valores double para guardar las ventas, se puede escribir lo siguiente: double [] ventas; Nota. Se puede declarar una variable arreglo en Java poniendo los corchetes despu´es del nombre del arreglo, como en double ventas[];. Este formato es empleado en C y C++, pero el formato preferido entre los programadores de Java es poniendo los corchetes despu´es del tipo de la variable y antes del nombre de la variable.
Se puede dar cualquier identificador legal que se quiera para un arreglo, pero los programadores de Java nombran los arreglos siguiendo las mismas reglas usadas para variables, el nombre inicia con min´ uscula y letras may´ usculas iniciando palabras subsecuentes. Adicionalmente, varios programadores observan una de las siguientes convenciones para hacer m´as ´enfasis que el nombre representa un grupo de datos: Los arreglos son nombrados frecuentemente usando un sujeto plural como ventas. Los arreglos son nombrados frecuentemente combinando una palabra que implique un grupo, como listaVentas, tablaVentas, o arregloVentas. Despu´es de crear una variable arreglo, se necesita reservar espacio de memoria. Se usa el mismo procedimiento para crear un arreglo que el empleado para crear un objeto. Para declarar un arreglo y reservar memoria para este se hace en dos procesos distintos. Para reservar localidades de memoria para 20 valores de ventas, se declara la variable arreglo y luego se crea el arreglo mediante dos sentencias como sigue: 2
double [] ventas; ventas = new double [20]; Al igual que con los objetos, se puede declarar y crear un arreglo en una sola sentencia con lo siguiente: double [] ventas = new double [20]; Con la sentencia anterior se reservan 20 localidades de memoria para 20 valores double. Se puede distinguir cada dato ventas de los otros con un sub´ındice. Un sub´ındice es un entero contenido dentro de corchetes que especifica uno de los elementos del arreglo. Cualquier arreglo de elementos en Java est´a numerado iniciando con cero, as´ı que se pueden usar correctamente sub´ındices de 0 hasta 19 al trabajar con un arreglo de 20 elementos. Es decir, el primer elemento del arreglo ventas es ventas[0], y el u ´ltimo elemento es ventas[19]. En otros lenguajes de programaci´on el primer elemento del arreglo es el elemento uno, lo cual es un error com´ un al olvidar que en Java el primer elemento en un arreglo es el elemento cero. Tambi´en lo anterior hace que se olvide que el sub´ındice del u ´ltimo elemento es uno menos que el tama˜ no del arreglo y no el tama˜ no de este. Para no olvidar el uso correcto se puede pensar que el sub´ındice de un elemento indica la cantidad de elementos que le preceden. Si se emplea un sub´ındice que es negativo, o igual a, o mayor que el tama˜ no del arreglo, el sub´ındice est´a fuera de l´ımites y un mensaje de error es generado. Cuando se trabaja con alg´ un elemento del arreglo, se emplea de igual forma como se hace con una variable. Por ejemplo, para asignar un valor al primer elemento de ventas en un arreglo, se usa una sentencia de asignaci´on simple, tal como la siguiente: ventas[0] = 12345.0; Para mostrar el u ´ltimo elemento del arreglo ventas de tama˜ no 20, se escribe: System.out.println(ventas[19]); Cuando se declara o accesa un arreglo, se puede usar cualquier expresi´on para indicar el tama˜ no, siempre y cuando esta d´e un entero. Para declarar un arreglo double llamado valoresMoneda, se podr´ıa usar cualquiera de las siguientes: Una constante literal entera double [] valoresMoneda = new double [10]; Una constante con nombre entera 3
double [] valoresMoneda = new double [CANT ELEMS]; Una variable entera double [] valoresMoneda = new double [cantElems]; Un c´alculo que involucre variables con enteros, o que d´e un entero double [] valoresMoneda = new double [x + y * z]; Un valor entero devuelto por un m´etodo double [] valoresMoneda = new double [getElementos()];
2.
Inicializaci´ on de arreglos
Una variable que tiene un tipo primitivo, como un int, guarda un valor. Una variable con un tipo referencia, como un arreglo, guarda una direcci´on de memoria donde un valor est´a guardado. Los nombres de arreglos contienen referencias, al igual como todos los objetos Java. No se asigna direcci´on de memoria cuando se declara un arreglo usando s´olo un tipo de dato, corchetes, y un nombre. El nombre de la variable arreglo tiene el valor especial null, que significa que el identificador no est´a asociado con alguna direcci´on, como sucede con el valor de numeros que es null en la siguiente declaraci´on: int [] numeros; Al emplear la palabra reservada new para definir un arreglo, el nombre arreglo recibe el valor de una direcci´on de memoria, como se hace en la siguiente sentencia al definir numeros int [] numeros = new int [10]; En esta declaraci´on numeros tiene una direcci´on, pero cada elemento de numeros tiene el valor de cero porque es un arreglo de enteros. Los elementos en un arreglo float o double tienen asignado 0.0. Por defecto, los elementos de un arreglo char tienen asignado ’\u0000’, el cual es el valor Unicode para el car´acter null, y los elementos de un arreglo boolean tienen asignado false. En arreglos de objetos, incluyendo String, cada elemento tiene asignado null por defecto. Adem´as de asignar un valor a un elemento de un arreglo, como en: 4
numeros[0] = 45; Se pueden tambi´en asignar valores diferentes al valor por defecto a los elementos de un arreglo en la creaci´on. Para inicializar un arreglo, se usa una lista de inicializaci´ on de valores separados por comas y encerrados entre llaves. Dando valores para todos los elementos en un arreglo tambi´en es llamado poblar un arreglo. Por ejemplo, si se quiere crear un arreglo llamado multiplosDiez y guardar los primeros seis m´ ultiplos de diez en el arreglo, se puede declarar como sigue: int[] multiplosDiez = {10, 20, 30, 40, 50, 60}; Observar el punto y coma al final de la sentencia. Cuando se pueble un arreglo en la creaci´on dando una lista de inicializaci´on, no se da el tama˜ no del arreglo, el tama˜ no es asignado de acuerdo a la cantidad de valores que se pongan en la lista de inicializaci´on. Por ejemplo, el arreglo multiplosDiez definido previamente tiene tama˜ no seis. Tambi´en, cuando se inicializa un arreglo, no se requiere usar la palabra reservada new, la nueva memoria es asignada de acuerdo al tama˜ no de la lista dada. No se puede directamente inicializar una parte de un arreglo en Java. En caso de que se requiera se deber´a hacer individualmente, una vez que se haya creado el arreglo usando la palabra reservada new.
3.
Uso de sub´ındices variables en un arreglo
Si se trata cada elemento de un arreglo como una entidad individual, no hay ventaja en declarar un arreglo respecto a variables individuales de tipos primitivos. El poder de los arreglos se da cuando se usan sub´ındices que son variables, en vez de sub´ındices que sean valores constantes. Suponer que se declara un arreglo de cinco enteros para manejar igual cantidad de puntuaciones, como se muestra enseguida: int [] arregloPunt = {13, 23, 54, 79, 95}; Luego se quiere realizar la misma operaci´on con cada elemento del arreglo, tal como incrementar cada puntuaci´on por una cantidad constante. Para incrementar cada elemento de arregloPunt por tres puntos, por ejemplo, se puede escribir lo siguiente: final int INCREMENTO = 3; 5
arregloPunt[0] arregloPunt[1] arregloPunt[2] arregloPunt[3] arregloPunt[4]
+= += += += +=
INCREMENTO; INCREMENTO; INCREMENTO; INCREMENTO; INCREMENTO;
Con un arreglo peque˜ no, la tarea es manejable, requiriendo solo cinco sentencias. Sin embargo, se puede reducir la cantidad de c´odigo necesario usando una variable como sub´ındice. Entonces, se puede usar un ciclo para realizar la aritm´etica con cada elemento, como en el siguiente ejemplo: final int INCREMENTO = 3; for (int sub = 0; sub < 5; ++sub) arregloPunt[sub] += INCREMENTO; La variable sub es puesta a cero, y entonces es comparado a cinco. Como el valor de sub es menor que cinco, el ciclo se ejecuta y tres es agregado a arregloPunt[0]. Luego, la variable sub es incrementada y se hace uno, que sigue siendo menor que cinco, as´ı que el ciclo se ejecuta nuevamente, arregloPunt[1] es incrementado por tres, y as´ı sucesivamente. Un proceso que toma cinco sentencias ahora toma solamente una. Considerar lo que suceder´ıa si el arreglo hubiese tenido 100 elementos, habr´ıa requerido 95 sentencias adicionales, pero el u ´nico cambio requerido, usando el segundo m´etodo, es el cambio del tama˜ no del arreglo a 100 en la segunda parte del ciclo for. Cuando una aplicaci´on contiene un arreglo y se quiere usar cada elemento del arreglo en alguna tarea, se sugiere usar ciclos que var´ıen la variable de control del ciclo desde cero hasta uno menos que el tama˜ no del arreglo. Estas tareas pueden ser el alterar cada valor en el arreglo, sumar todos los valores en el arreglo, o mostrar cada elemento en el arreglo. En una aplicaci´on que incluya un arreglo es conveniente declarar una constante simb´olica igual al tama˜ no del arreglo y usar la constante simb´olica como un valor l´ımite en cada ciclo que procese el arreglo. De esta forma, si el tama˜ no del arreglo cambia m´as adelante, s´olo se necesita modificar el valor guardado en la constante simb´olica, para no tener que buscar y modificar el valor l´ımite en cada ciclo que procesa el arreglo. Un ejemplo ser´ıa como el siguiente: int [] arregloPunt = {13, 23, 54, 79, 95}; final int INCREMENTO = 5; final int CANT DE PUNT = 5; for (int sub = 0; sub < CANT DE PUNT; ++sub) arregloPunt[sub] += INCREMENTO; Este formato tiene dos ventajas, primero, el uso de la constante simb´olica, CANT DE PUNT, el lector entiende que se est´a procesando cada elemento del arreglo por el tama˜ no del arreglo entero. Segundo, si el tama˜ no del arreglo cambia porque se agregaron o quitaron puntuaciones, s´olo se ocupa cambiar el valor de la constante simb´olica una sola vez. 6
Una segunda opci´on, es usar un campo, variable de instancia, al que es asignado autom´aticamente un valor para cada arreglo que es creado; el campo length contiene la cantidad de elementos en el arreglo. El siguiente ejemplo repite el c´odigo previo mostrando como se usa este campo como valor l´ımite en la parte central del ciclo for. int [] arregloPunt = {13, 23, 54, 79, 95}; final int INCREMENTO = 3; for (int sub = 0; sub < arregloPunt.length; ++sub) arregloPunt[sub] += INCREMENTO; Un error frecuente del programador es intentar usar length como un m´etodo del arreglo, escribiendo arregloPunt.length(), en vez de usarlo como un campo. Una variable de instancia o campo objeto como length es tambi´en llamada una propiedad del objeto. Java tambi´en soporta un ciclo for mejorado. Este ciclo permite recorrer un arreglo sin indicar los puntos de inicio y terminaci´on para la variable de control del ciclo. Para mostrar cada elemento del arreglo llamado arregloPuntos con el ciclo avanzado se hace as´ı: for (int valor : arregloPuntos) System.out.println(valor); valor es definido del mismo tipo que el arreglo nombrado que est´a despu´es de los dos puntos. Dentro del ciclo, valor adquiere, uno a la vez, cada dato del arreglo. Se puede leer como, “Para cada valor en arregloPuntos, mostrar valor”. El ciclo for avanzado es conocido tambi´en como ciclo foreach.
3.1.
Uso de una parte del arreglo
En ocasiones no se quiere usar cada valor en un arreglo. Por ejemplo, suponer que se escribe un programa que permite a un estudiante meter hasta 10 puntuaciones y luego calcular y mostrar el promedio. Para permitir 10 puntuaciones, se crea un arreglo que puede guardar 10 valores, pero como el estudiante podr´ıa meter menos de 10 valores, se podr´ıa usar una parte del arreglo, como se muestra en la clase PromedioFlexible, c´odigo 1.
7
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
import j a v a . u t i l . ∗ ; public c l a s s P r o m e d i o F l e x i b l e { public s t a t i c void main ( S t r i n g [ ] a r g s ) { int [ ] p u n t u a c i o n e s = new int [ 1 0 ] ; int p u n t u a c i o n = 0 ; int c u e n t a = 0 ; int t o t a l = 0 ; f i n a l int SALIR = 9 9 9 ; f i n a l int MAX = 1 0 ; Scanner e n t r a d a = new Scanner ( System . i n ) ; System . out . p r i n t ( ” Entrar p u n t u a c i ´on : ” ) ; puntuacion = entrada . nextInt ( ) ; while ( c u e n t a < MAX && p u n t u a c i o n != SALIR ) { i f ( p u n t u a c i o n != SALIR ) { puntuaciones [ cuenta ] = puntuacion ; t o t a l += p u n t u a c i o n e s [ c u e n t a ] ; System . out . p r i n t ( ” I n g r e s a r s i g u i e n t e p u n t u a c i ´on o ” + SALIR + ” para s a l i r : ” ) ; puntuacion = entrada . nextInt ( ) ; } c u e n t a++; } System . out . p r i n t l n ( ” Las p u n t u a c i o n e s dadas son : ” ) ; for ( int x = 0 ; x < c u e n t a ; ++x ) System . out . p r i n t ( p u n t u a c i o n e s [ x ] + ” ” ) ; System . out . p r i n t l n ( ”\n El promedio e s ” + ( t o t a l ∗ 1 . 0 / c u e n t a ) ) ; } }
C´odigo 1: Aplicaci´on PromedioFlexible. La aplicaci´on PromedioFlexible declara un arreglo que puede guardar diez puntuaciones. Al usuario se le pide la primera puntuaci´on; luego, en ciclo while la puntuaci´on es puesta en el arreglo puntuaciones. Luego la puntuaci´on es agregada a un total, y al usuario se le pide ingresar otra puntuaci´on o un valor de 999 para salir de la petici´on de n´ umeros. En el ciclo while se revisa para que hasta diez puntuaciones sean ingresadas y el usuario no quiera salir. Despu´es de cada puntuaci´on entrada, la variable cuenta es incrementada, y sirve para dos prop´ositos: para indicar el elemento donde la siguiente puntuaci´on deber´a ser guardada, y para cuando el ciclo termina, saber la cantidad de puntuaciones dadas. La variable cuenta luego es usada para controlar la salida del ciclo for y para el c´alculo del promedio.
4.
Declaraci´ on y uso de arreglos de objetos
Se pueden declarar arreglos que guarden elementos de cualquier tipo, incluyendo objetos. Por ejemplo, suponer que se ha creado la clase Asalariado, c´odigo 2, la cual incluye dos 8
campos de datos, numero y salario, un constructor, y m´etodos accesores para cada campo. 1 2 3 4 5 6 7 8 9 10 11 12 13 14
public c l a s s A s a l a r i a d o { private int numero ; private double s a l a r i o ; A s a l a r i a d o ( int n , double s ) { numero = n ; salario = s ; } public int getNumero ( ) { return numero ; } public int g e t S a l a r i o ( ) { return s a l a r i o ; } }
C´odigo 2: La clase Asalariado. Se pueden crear objetos individuales Asalariado con nombres u ´nicos, como los siguientes: Asalariado pintor, electricista, plomero; Asalariado trabajador1, trabajador2, trabajador3; En varios programas puede ser m´as conveniente crear un arreglo de objetos Asalariado. Un arreglo llamado plantilla que guarde siete objetos Asalariado se define como: Asalariado[] plantilla = new Asalariado[7]; La sentencia reserva suficiente memoria para siete objetos Asalariado llamados plantilla[0] hasta plantilla[6]. La sentencia no construye los objetos Asalariado, por lo tanto se requerir´a llamar al constructor siete veces. Se quiere n´ umerar a los trabajadores empezando en 500 y con un salario de $1,200, y como el constructor de la clase Asalariado requiere dos argumentos, n´ umero del asalariado y salario, el siguiente ciclo construye los siete objetos: final int NUM INICIAL = 500; final double SALARIO = 1200.0; for (int x = 0; x < plantilla.length; ++x) plantilla[x] = new Asalariado(NUM INICIAL + x, SALARIO); Como x var´ıa desde 0 hasta 6, cada uno de los siete objetos plantilla es construido con un n´ umero de empleado que es 500 m´as que x, y con el mismo salario de $1,200.00, como se asigna por la constante SALARIO. 9
Otras clases contienen solo el constructor por defecto, el cual es dado autom´aticamente cuando no hay ning´ un constructor escrito en la clase. Para construir un arreglo de objetos usando un constructor por defecto, tambi´en se debe llamar al constructor usando la palabra reservada new para cada elemento declarado del arreglo. Por ejemplo, suponer que se ha creado una clase llamado ArticuloInventario sin haber escrito un constructor. Para crear un arreglo de 1,000 objetos ArticuloInventario, se podr´ıa hacer as´ı: final int CANT ARTS = 1000; ArticuloInventario[] articulos = new ArticuloInventario[CANT ARTS]; for (int x = 0; x < CANT ARTS; ++x) articulos[x] = new ArticuloInventario(); Para usar un m´etodo que pertenece a un objeto que es parte de un arreglo, se inserta la notaci´on sub´ındice apropiada despu´es del nombre arreglo y antes del punto que precede al nombre del m´etodo. Por ejemplo, para mostrar los datos de los siete asalariados guardados en el arreglo plantilla, se puede escribir lo siguiente: for (int x = 0; x < plantilla.length; ++x) System.out.println(plantilla[x].getNumero() + " " + plantilla[x].getSalario()); La colocaci´on del sub´ındice entre corchetes es despu´es de plantilla para indicar que el m´etodo “pertenece” a un elemento particular de la plantilla.
4.1.
Uso de ciclos for avanzados con objetos
Se puede emplear el ciclo for avanzado para recorrer un arreglo de objetos. Para mostrar los datos de los siete asalariados guardados en el arreglo plantilla, se puede hacer de esta forma: for (Asalariado trabajador : plantilla) System.out.println(trabajador.getNumero() + " " + trabajador.getSalario()); En este ciclo, trabajador es una variable local que representa cada elemento de plantilla en turno. Usando el ciclo for avanzado se evita usar un valor limitante para el ciclo y de usar un sub´ındice siguiendo a cada elemento.
10
4.2.
Manipulaci´ on de arreglos String
Como con cualquier otro objeto, se puede crear un arreglo de objetos String. Por ejemplo, se puede guardar los nombres de los departamentos de una empresa como sigue: String[] departNombres = {"Contabilidad", "Recursos Humanos", "Ventas"}; Se acceden los nombres de los departamentos como otros arreglos objeto. Por ejemplo, se puede usar el siguiente c´odigo para mostrar la lista de String guardadas en el arreglo departNombres: for (int a = 0; a < departNombres.length; ++a) System.out.println(departNombres[a]);
5.
B´ usqueda y uso de arreglos paralelos
Suponer que una empresa manufactura diez art´ıculos. Cuando un cliente pone una orden se necesita determinar si la clave del art´ıculo es v´alida. Cuando se desea determinar si una variable tiene uno de varios valores v´alidos y estos son secuenciales, por ejemplo entre 201 y 220, la siguiente sentencia if, que usa el operador l´ogico Y, puede hacer la revisi´on para poner la bandera a true si el art´ıculo es v´alido. final int INF = 201; final int SUP = 220; boolean articuloValido = false; if (articuloOrdenado >= INF && articuloOrdenado