Autor Tema: Ordenación ArrayList  (Leído 146 veces)

usuHelp

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 3
    • Ver Perfil
Ordenación ArrayList
« en: 20 de Enero 2021, 04:38 »
Hola, mi problema viene al intentar ordenar un ArrayList de jugadores por sus ids, el problema viene que al ser el id con números me pilla solo el primero,al ser letras lo ordena bien al ser String. Hay alguna solución para que pueda hacer ambas formas? Se pase letras o números, siendo id un String.Gracias! Paso algunos ejemplos:
Código: [Seleccionar]
ArrayList <Jugador> jugadores = new ArrayList<Jugador>();
jugadores.add(new Jugador("j3","pepe"));
jugadores.add(new Jugador("j20","juan"));
jugadores.add(new Jugador("j1","maria"));
jugadores.add(new Jugador("j10","maria"));
Collections.sort(jugadores);
                for(Jugador jug: jugadores) jug.mostrarDatos();
Sale por pantalla:
ID: j1 Nombre: maria
ID: j10 Nombre: maria
ID: j20 Nombre: juan
ID: j3 Nombre: pepe
Código: [Seleccionar]
ArrayList <Jugador> jugadores = new ArrayList<Jugador>();
jugadores.add(new Jugador("jA","pepe"));
jugadores.add(new Jugador("jD","juan"));
jugadores.add(new Jugador("jC","maria"));
jugadores.add(new Jugador("jB","maria"));
Collections.sort(jugadores);
for(Jugador jug: jugadores) jug.mostrarDatos();
ID: jA Nombre: pepe
ID: jB Nombre: maria
ID: jC Nombre: maria
ID: jD Nombre: juan
Collections.sort llama a:
Código: [Seleccionar]
public int compareTo(Jugador j) { //Ordena por ID
// TODO Auto-generated method stub
return this.id.compareTo(j.id);
}

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 523
    • Ver Perfil
Re: Ordenación ArrayList
« Respuesta #1 en: 21 de Enero 2021, 12:00 »
Hola.

La ordenación que te hace es "correcta", porque al ser un String te hace una comparación lexicográfica.

Pero da igual si es correcto o no, lo que importa es que tú, el programador, quieres una comparación numérica, a pesar de que el dato no es exactamente numérico.

En este caso, la única solución es trabajar con el String para "enseñarle" a la clase Jugador cómo ha de compararse.

Habrá que hacer un método compareTo() más elaborado.
Establecemos que formato del ID va a ser siempre "jxx", es decir, siempre va a empezar por el carácter 'j' y a continuación tendrá uno o más caracteres que en unos casos podrán ser letras y en otros pueden ser números.

Pues antes de empezar a comparar habrá que comprobar que tipo de ID tienen los objetos Jugador que vamos a comparar.

Si ambos tienen id's con números, habrá que hacer una comparación numérica.
Pero si solo uno tiene id numérico, o no lo tienen ninguno de los dos, entonces habrá que hacer una comparación lexicográfica normal.

Para poder distinguir fácilmente entre un tipo de id del otro, podemos hacer un método de tipo boolean, que si detecta caracteres numéricos en el id, retorna true.

Código: [Seleccionar]
private boolean idContieneNumeros(String id) {

for (int i = 0; i < id.length(); i++) {
switch(id.charAt(i)) {
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return true; //Se han encontrado caracteres numéricos
}
}
//Si el bucle termina sin retorna true...
return false; //...es que no hay caracteres numéricos
}


Y ahora que podemos distinguir un id del otro, podemos hacer que el compareTo() decida que tipo de comparación ha de realizar.
Si detecta que AMBOS id son numéricos, lo que hay que hacer es crear a partir de ellos un nuevo String quitando la 'j' del principio.
De este modo, este nuevo String se supone que solo son caracteres numéricos, así que podemos hacer un parseo y convertirlos a tipo int.
Y una vez convertidos, ya solo comparamos quien es mayor o menor y devolvemos el valor correspondiente. (El método compareTo() lo que hace es devolver un entero positivo o negativo, según quien es mayor o menor)

El método podría quedar así. Incluye comentarios explicando cada paso:
Código: [Seleccionar]
public int compareTo(Jugador jug) {
//Comprobamos si AMBOS Jugador tienen id con numéros
if (idContieneNumeros(id) && idContieneNumeros(jug.id)) {
/*
* Los id se compone del carácter 'j' seguido de uno o más
* caracteres numéricos.
* Crearemos dos nuevos String solo con los números de los id
*/
String numerosDeEsteJugador = id.substring(1); //Descartamos primer carácter
String numerosDeOtroJugador = jug.id.substring(1);
/*
* Intentaremos convertirlos a valores enteros.
* Si la conversión fallase, quizás porque un ID no respeta
* el formato "jxx" y tiene más letras entre los números, entonces
* devoveremos una comparación lexicográfica normal
*/
try {
int valorDeEste = Integer.parseInt(numerosDeEsteJugador);
int valorDeOtro = Integer.parseInt(numerosDeOtroJugador);
//Tenemos enteros numéricos, comparamos
if (valorDeEste < valorDeOtro)
return -1; //Este es MENOR
else if (valorDeEste > valorDeOtro)
return 1; //Este es MAYOR
else
return 0; //Ambos son iguales,no debería ocurrir,porque significaría que tienen mismo id
}
catch(NumberFormatException nfe) {
return id.compareTo(jug.id); //Conversión numérica ha fallado
}

}
else//Solo uno, o ninguno, tiene id con números
return id.compareTo(jug.id);//Devolvemos comparación lexicográfica normal
}


Con esto, he creado mi propia clase Jugador:
Código: [Seleccionar]
public class Jugador implements Comparable<Jugador>{

private String id;
private String nombre;

public Jugador(String id, String nombre) {
this.id = id;
this.nombre = nombre;
}

@Override
public String toString() {
return "ID: " + id + " Nombre: " + nombre;
}

@Override
public int compareTo(Jugador jug) {
//Comprobamos si AMBOS Jugador tienen id con numéros
if (idContieneNumeros(id) && idContieneNumeros(jug.id)) {
/*
* Los id se compone del carácter 'j' seguido de uno o más
* caracteres numéricos.
* Crearemos dos nuevos String solo con los números de los id
*/
String numerosDeEsteJugador = id.substring(1); //Descartamos primer carácter
String numerosDeOtroJugador = jug.id.substring(1);
/*
* Intentaremos convertirlos a valores enteros.
* Si la conversión fallase, quizás porque un ID no respeta
* el formato "jxx" y tiene más letras entre los números, entonces
* devoveremos una comparación lexicográfica normal
*/
try {
int valorDeEste = Integer.parseInt(numerosDeEsteJugador);
int valorDeOtro = Integer.parseInt(numerosDeOtroJugador);
//Tenemos enteros numéricos, comparamos
if (valorDeEste < valorDeOtro)
return -1; //Este es MENOR
else if (valorDeEste > valorDeOtro)
return 1; //Este es MAYOR
else
return 0; //Ambos son iguales,no debería ocurrir,porque significaría que tienen mismo id
}
catch(NumberFormatException nfe) {
return id.compareTo(jug.id); //Conversión numérica ha fallado
}

}
else//Solo uno, o ninguno, tiene id con números
return id.compareTo(jug.id);//Devolvemos comparacion lexicográfica normal
}

private boolean idContieneNumeros(String id) {

for (int i = 0; i < id.length(); i++) {
switch(id.charAt(i)) {
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return true; //Se han encontrado caracteres numéricos
}
}
//Si el bucle termina sin retorna true...
return false; //...es que no hay caracteres numéricos
}

}


Y en la siguiente clase principal, he creado tres ArrayList para poner a prueba el algoritmo de ordenación.
Uno tiene jugadores con id numéricos, otro jugadores con id de solo letras y el tercero tiene ambos tipos de jugadores.

Código: [Seleccionar]
public class Ordenar {

public static void main(String[] args) {

System.out.println("Ordenación solo IDs con números");
ArrayList<Jugador> jugadores = new ArrayList<Jugador>();
jugadores.add(new Jugador("j3","pepe"));
jugadores.add(new Jugador("j20","juan"));
jugadores.add(new Jugador("j1","maria"));
jugadores.add(new Jugador("j10","maria"));

jugadores.sort(null);

for (Jugador jug: jugadores)
System.out.println(jug);

System.out.println("\nOrdenación solo IDs con letras");
ArrayList <Jugador> jugadores2 = new ArrayList<Jugador>();
jugadores2.add(new Jugador("jA","pepe"));
jugadores2.add(new Jugador("jD","juan"));
jugadores2.add(new Jugador("jC","maria"));
jugadores2.add(new Jugador("jB","maria"));

jugadores2.sort(null);

for (Jugador jug: jugadores2)
System.out.println(jug);

System.out.println("\nOrdenación con ambos tipos de ID");
ArrayList<Jugador> jugadoresTodos = new ArrayList<Jugador>();
jugadoresTodos.add(new Jugador("j3","pepe"));
jugadoresTodos.add(new Jugador("jA","pepe"));
jugadoresTodos.add(new Jugador("j20","juan"));
jugadoresTodos.add(new Jugador("jD","juan"));
jugadoresTodos.add(new Jugador("j1","maria"));
jugadoresTodos.add(new Jugador("jC","maria"));
jugadoresTodos.add(new Jugador("j10","maria"));
jugadoresTodos.add(new Jugador("jB","maria"));

jugadoresTodos.sort(null);

for (Jugador jug: jugadoresTodos)
System.out.println(jug);

}

}

Y en los tres casos, parece que hace la ordenación que deseábamos:

Citar
Ordenación solo IDs con números
ID: j1 Nombre: maria
ID: j3 Nombre: pepe
ID: j10 Nombre: maria
ID: j20 Nombre: juan

Ordenación solo IDs con letras
ID: jA Nombre: pepe
ID: jB Nombre: maria
ID: jC Nombre: maria
ID: jD Nombre: juan

Ordenación con ambos tipos de ID
ID: j1 Nombre: maria
ID: j3 Nombre: pepe
ID: j10 Nombre: maria
ID: j20 Nombre: juan
ID: jA Nombre: pepe
ID: jB Nombre: maria
ID: jC Nombre: maria
ID: jD Nombre: juan
NO respondo dudas por mensaje privado
Publicando vuestras dudas en el foro público conseguimos:
- Que más gente aporte respuestas mejores o complementarias.
- Que otras personas puedan aprender de vuestras dudas.

Mejor en PÚBLICO que en privado. Gracias

 

Esto es un laboratorio de ideas...
Aprender a programar

Preguntas y respuestas

¿Cómo establecer o cambiar la imagen asociada (avatar) de usuario?
  1. Inicia sesión con tu nombre de usuario y contraseña.
  2. Pulsa en perfil --> perfil del foro
  3. Elige la imagen personalizada que quieras usar. Puedes escogerla de una galería de imágenes o subirla desde tu ordenador.
  4. En la parte final de la página pulsa el botón "cambiar perfil".