2010-09-13 18 views
50

Necesito ordenar una matriz de entradas utilizando un comparador personalizado, pero la biblioteca de Java no proporciona una función de clasificación para ints con comparadores (los comparadores pueden usarse solo con objetos). ¿Hay alguna manera fácil de hacer esto?¿Cómo ordenar una matriz de entradas usando un comparador personalizado?

+0

¿solo desea ordenar la matriz en orden descendente o desea realizar algo más complicado? – Roman

+0

Algo más complicado. Quiero ordenar el int usando el valor absoluto como una clave. – Alexandru

+14

¡No puedo creer que Java no pueda ordenar primitive 'int's con un comparador personalizado! – HRJ

Respuesta

40

Si no puede cambiar el tipo de su matriz de entrada del siguiente trabajo:

final int[] data = new int[] { 5, 4, 2, 1, 3 }; 
final Integer[] sorted = ArrayUtils.toObject(data); 
Arrays.sort(sorted, new Comparator<Integer>() { 
    public int compare(Integer o1, Integer o2) { 
     // Intentional: Reverse order for this demo 
     return o2.compareTo(o1); 
    } 
}); 
System.arraycopy(ArrayUtils.toPrimitive(sorted), 0, data, 0, sorted.length); 

Este utiliza ArrayUtils del proyecto commons-lang para convertir fácilmente entre int[] y Integer[], crea una copia de la array, hace el tipo, y luego copia los datos ordenados sobre el original.

+1

¿Por qué no usa Arrays.sort en lugar de convertir array -> list -> array? – nanda

+0

Buen punto, lo he actualizado, estaba jugando con primitivos comunes, pero en realidad no hice nada útil –

+0

No sabía nada de commons-lang. Gracias por el consejo. – Alexandru

0

Aquí hay un método de ayuda para hacer el trabajo.

Primero de todo lo que necesita una nueva interfaz Comparator, como Comparator no soporta primitivas:

public interface IntComparator{ 
    public int compare(int a, int b); 
} 

(Se podría, por supuesto, hacerlo con autoboxing/unboxing pero no voy a ir allí, que es feo)

Entonces, aquí está un método de ayuda a ordenar una matriz int usando este comparador:

public static void sort(final int[] data, final IntComparator comparator){ 
    for(int i = 0; i < data.length + 0; i++){ 
     for(int j = i; j > 0 
      && comparator.compare(data[j - 1], data[j]) > 0; j--){ 
      final int b = j - 1; 
      final int t = data[j]; 
      data[j] = data[b]; 
      data[b] = t; 
     } 
    } 
} 

Y aquí es un poco de código de cliente. Un comparador estúpida que ordena todos los números que consisten solamente en el dígito '9' en la parte delantera (de nuevo clasificado por tamaño) y luego el resto (por lo bueno que es):

final int[] data = 
    { 4343, 544, 433, 99, 44934343, 9999, 32, 999, 9, 292, 65 }; 
sort(data, new IntComparator(){ 

    @Override 
    public int compare(final int a, final int b){ 
     final boolean onlyNinesA = this.onlyNines(a); 
     final boolean onlyNinesB = this.onlyNines(b); 
     if(onlyNinesA && !onlyNinesB){ 
      return -1; 
     } 
     if(onlyNinesB && !onlyNinesA){ 
      return 1; 
     } 

     return Integer.valueOf(a).compareTo(Integer.valueOf(b)); 
    } 

    private boolean onlyNines(final int candidate){ 
     final String str = String.valueOf(candidate); 
     boolean nines = true; 
     for(int i = 0; i < str.length(); i++){ 
      if(!(str.charAt(i) == '9')){ 
       nines = false; 
       break; 
      } 
     } 
     return nines; 
    } 
}); 

System.out.println(Arrays.toString(data)); 

Salida:

[9, 99, 999, 9999, 32, 65, 292, 433, 544, 4343, 44934343] 

El código de clasificación fue tomado de Arrays.sort(int[]), y solo utilicé la versión optimizada para arreglos pequeños. Para una implementación real, probablemente desee consultar el código fuente del método interno sort1(int[], offset, length) en la clase Arrays.

+3

Arrays.sort() parece usar quicksort mirando su código mientras que el ordenamiento propuesto parece usar ordenamiento por inserción. ¿No sería asintóticamente más lento? –

0

Intenté usar el comparador con el tipo primitivo. Finalmente, llegué a la conclusión de que no hay forma de engañar al comparador. Esta es mi implementación.

public class ArrSortComptr { 
    public static void main(String[] args) { 

     int[] array = { 3, 2, 1, 5, 8, 6 }; 
     int[] sortedArr=SortPrimitiveInt(new intComp(),array); 
     System.out.println("InPut "+ Arrays.toString(array)); 
     System.out.println("OutPut "+ Arrays.toString(sortedArr)); 

    } 
static int[] SortPrimitiveInt(Comparator<Integer> com,int ... arr) 
{ 
    Integer[] objInt=intToObject(arr); 
    Arrays.sort(objInt,com); 
    return intObjToPrimitive(objInt); 

} 
static Integer[] intToObject(int ... arr) 
{ 
    Integer[] a=new Integer[arr.length]; 
    int cnt=0; 
    for(int val:arr) 
     a[cnt++]=new Integer(val); 
    return a; 
} 
static int[] intObjToPrimitive(Integer ... arr) 
{ 
    int[] a=new int[arr.length]; 
    int cnt=0; 
    for(Integer val:arr) 
     if(val!=null) 
      a[cnt++]=val.intValue(); 
    return a; 

} 

} 
class intComp implements Comparator<Integer> 
{ 

    @Override //your comparator implementation. 
    public int compare(Integer o1, Integer o2) { 
     // TODO Auto-generated method stub 
     return o1.compareTo(o2); 
    } 

} 

@Roman: No puedo decir que este es un buen ejemplo, pero ya que preguntas esto es lo que vino a la mente. Suponga que en una matriz desea ordenar los números solo en función de su valor absoluto.

Integer d1=Math.abs(o1); 
Integer d2=Math.abs(o2); 
return d1.compareTo(d2); 

Otro ejemplo puede ser como se desea ordenar sólo los números mayores que 100.It en realidad depende de la situation.I no puede pensar en nada más situations.Maybe Alexandru puede dar más ejemplos ya que él dice es lo que quiere de usar un comparador para int array.

+0

@Emil: lo siento por un poco de offtop, pero solo tengo curiosidad, ¿podrías mostrarme un ejemplo de comparador que has utilizado para ordenar una matriz de enteros? Simplemente no puedo imaginar ninguna implementación a excepción de 'return sign * (i1 - i2);' donde 'sign' es -1 o +1 dependiendo del orden deseable. – Roman

+0

@Emil: en realidad, la implementación que acabo de mostrar probablemente se haya roto (las entradas se deben convertir en largas al principio) pero no importa en el contexto. – Roman

+0

¿Quiere decir que no se requiere un comparador para enteros aparte del orden de orden ascendente y descendente? – Emil

14

¿Qué tal el uso de flujos (Java 8)?

int[] ia = {99, 11, 7, 21, 4, 2}; 
ia = Arrays.stream(ia). 
    boxed(). 
    sorted((a, b) -> b.compareTo(a)). // sort descending 
    mapToInt(i -> i). 
    toArray(); 

O en el lugar:

int[] ia = {99, 11, 7, 21, 4, 2}; 
System.arraycopy(
     Arrays.stream(ia). 
      boxed(). 
      sorted((a, b) -> b.compareTo(a)). // sort descending 
      mapToInt(i -> i). 
      toArray(), 
     0, 
     ia, 
     0, 
     ia.length 
    ); 
+2

Me molesta que no podamos haber ordenado (IntComparator) en IntStream. – Trejkaz

+5

No use '(a, b) -> b - a' para el orden inverso. Este comparador puede desbordarse. Tenga en cuenta la existencia de 'Comparator.reverseOrder()' ... – Holger

+0

Falló por completo el posible desbordamiento. Adaptado la respuesta. Gracias Holger! – user3669782

3

Si no desea copiar la matriz (decir que es muy grande), es posible que desee crear una lista de contenedor que se puede utilizar en un tipo:

final int[] elements = {1, 2, 3, 4}; 
List<Integer> wrapper = new AbstractList<Integer>() { 

     @Override 
     public Integer get(int index) { 
      return elements[index]; 
     } 

     @Override 
     public int size() { 
      return elements.length; 
     } 

     @Override 
     public Integer set(int index, Integer element) { 
      int v = elements[index]; 
      elements[index] = element; 
      return v; 
     } 

    }; 

Y ahora puede hacer un tipo en esta lista de envoltura mediante un comparador personalizado.

+0

Me gusta mucho mejor que la respuesta aceptada. No es necesario copiar o convertir contenido de matriz, solo aprovechar la implementación personalizada de Listas. – OB1

+1

@ OB1: se ve limpio, pero la implementación estándar de 'clasificación' copia toda la lista en una matriz, la ordena y la escribe. Y dado que esta lista no implementa el marcador 'RandomAccess', el write-back usará un' ListIterator' en lugar de simplemente 'set'. – Holger

+0

Guau, Holger tiene razón sobre la copia. Ni siquiera pensé en revisar esto ya que asumí que nadie estaría tan loco como para hacer una copia. – user1460736

Cuestiones relacionadas