2009-11-14 145 views
10

tengo una clase de enumeración como la siguiente:¿Cómo puedo ordenar enum alfabéticamente en Java?

public enum Letter { 
    OMEGA_LETTER("Omega"), 
    GAMMA_LETTER("Gamma"), 
    BETA_LETTER("Beta"), 
    ALPHA_LETTER("Alpha"), 

    private final String description; 

    Letter() { 
     description = toString(); 
    } 

    Letter(String description) { 
     this.description = description; 
    } 

    public String getDescription() { 
     return description; 
    } 
} 

más adelante en mi código que básicamente iterar sobre la enumeración de la letra e imprimir sus miembros en la consola:

for (Letter letter : Letter.values()) { 
System.out.println(letter.getDescription()); 
} 

pensé que los valores() método me daría una vista ordenada de la enumeración (como se menciona here), pero este no es el caso aquí. Simplemente obtengo los enumeradores en el orden en que los creé dentro de la clase Letter enum. ¿Hay alguna forma de mostrar los valores de una enumeración en orden alfabético? ¿Necesitaría un objeto de comparación por separado o hay una forma incorporada de hacerlo? Básicamente me gustaría que los valores a ser ordenada alfabéticamente basadas en el texto getDescription():

Alpha 
Beta 
Gamma 
Omega 
+1

El artículo que enlaza no es incorrecto. El orden al que se refiere es la secuencia en la que fueron escritos en la enumeración. El compilador/tiempo de ejecución no hace ninguna clasificación especial en los valores enum. – Suppressingfire

+0

Encontré este enlace aquí en stackoverflow y me funcionó. http://stackoverflow.com/a/18416259/6301287 –

Respuesta

19
SortedMap<String, Letter> map = new TreeMap<String, Letter>(); 
for (Letter l : Letter.values()) { 
    map.put(l.getDescription, l); 
} 
return map.values(); 

O simplemente reordenar las declaraciones :-)

Edit: Como la demandante señaló, esto supone que las descripciones son únicos dentro de la enumeración.

+5

¿Pero por qué usar un 'Mapa'? No se hace nada con las teclas, por lo que no lleva muy bien la intención, ¿verdad? Usar un 'SortedSet' (implementación' TreeSet') correspondería mejor.Pero la singularidad que caracteriza a un 'Conjunto' (o las teclas de un 'Mapa') no se menciona como requisito en la publicación original, por lo que sugiero usar una 'Lista ' más simple (y más eficiente), seguida de un 'Colecciones .sort() 'llamada. – KLE

+1

Las claves están ordenadas por, se devuelven los valores. La pregunta dice: "Básicamente, me gustaría que los valores se clasifiquen alfabéticamente en función del texto getDescription():", por lo que quizás imprimir las enumeraciones no es todo lo que tiene en mente. – meriton

+0

@meriton Gracias por su agradecimiento. Como comenté en mi respuesta, la palabra "valor" en la publicación original no está definida, pero la entiendo como el valor del campo * description *. En su ejemplo de código y su comentario, usted asume que "valor" significa 'instancia '. (Por supuesto, en mi código, estaría de acuerdo contigo, pero para joel_nc ???) – KLE

5

sólo una especie utilizando Arrays.sort y su propio comparador.

6

Pensé que el método values ​​() me daría una vista ordenada de la enumeración (como se menciona aquí), pero este no es el caso aquí. Simplemente obtengo los enumeradores en el orden en que los creé dentro de la clase Letter enum.

Precisamente, el orden de la declaración se considera significativo para las enumeraciones, por lo que nos complace que se devuelvan exactamente en ese orden. Por ejemplo, cuando un int i representa valores enum, hacer values()[i] es una forma muy simple y eficiente de encontrar la instancia enum. Para ir en sentido contrario, el método ordinal() devuelve el índice de una instancia enum.

¿Hay alguna manera de mostrar los valores de una enumeración en orden alfabético? ¿Necesitaría un objeto de comparación por separado o hay una forma incorporada de hacerlo? Básicamente me gustaría que los valores a ser ordenada alfabéticamente basadas en el texto getDescription():

Lo que llamas valor no es algo definido para las enumeraciones en general. Aquí, en su contexto, se refiere al resultado de getDescription().

Como dices, podrías crear un Comparador para estas descripciones.Eso sería perfecto :-)


Tenga en cuenta que, en general, que pueda necesitar varios pedidos para estos casos:

  • orden de la declaración (este es el orden oficial)
  • Descripción fin
  • otras según sea necesario

También podría empujar esa noción de DescriptionComparator un poco:

  1. Por motivos de rendimiento, se podría almacenar las descripciones calculados.

  2. Porque las enumeraciones no pueden heredar, la reutilización del código debe estar fuera de la clase enum. Permítanme dar el ejemplo usaríamos en nuestros proyectos:

Ahora los ejemplos de código ...

/** Interface for enums that have a description. */ 
public interface Described { 
    /** Returns the description. */ 
    String getDescription(); 
} 

public enum Letter implements Described { 
    // .... implementation as in the original post, 
    // as the method is already implemented 
} 

public enum Other implements Described { 
    // .... same 
} 

/** Utilities for enums. */ 
public abstract class EnumUtils { 

    /** Reusable Comparator instance for Described objects. */ 
    public static Comparator<Described> DESCRIPTION_COMPARATOR = 
    new Comparator<Described>() { 
     public int compareTo(Described a, Described b) { 
     return a.getDescription().compareTo(b.getDescription); 
     } 
    }; 

    /** Return the sorted descriptions for the enum. */ 
    public static <E extends Enum & Described> List<String> 
    getSortedDescriptions(Class<E> enumClass) { 
     List<String> descriptions = new ArrayList<String>(); 
     for(E e : enumClass.getEnumConstants()) { 
     result.add(e.getDescription()); 
     } 
     Collections.sort(descriptions); 
     return descriptions; 
    } 
} 

// caller code 
List<String> letters = EnumUtils.getSortedDescriptions(Letter.class); 
List<String> others = EnumUtils.getSortedDescriptions(Other.class); 

Tenga en cuenta que el código genérico en EnumUtils funciona no sólo para una clase de enumeración, pero funciona para cualquier clase enum en su proyecto que implemente la interfaz Described.

Como dijimos antes, el objetivo de tener el código fuera de las enumeraciones (donde pertenecería de otro modo) es reutilizar el código. No es gran cosa para dos enumeraciones, pero tenemos más de mil enums en nuestro proyecto, muchos de ellos con las mismas interfaces ...!

0

Aquí hay una forma genérica de hacerlo con cualquier clase sin tener que implementar Comparable en la clase que está ordenando o crear un comparador personalizado. He encontrado instancias en las que no quiero anular CompareTo porque tiene un propósito diferente, no se puede para enumeraciones de todos modos, y constantemente crear clases contenedoras es un problema. Puede pasar una función que arroje un objeto Comparable que le gustaría utilizar para fines de clasificación.

La función toComparable solo se llama una vez por elemento en la lista (no así para un comparador personalizado), por lo que es especialmente útil si esa llamada es costosa para alguna clase. Los valores nulos se manejan internamente, por lo que es más fácil de usar que un comparador personalizado. Una llamada al algoritmo TimSort de Java 7 es drásticamente más eficiente que hacer un grupo de insertos O (log N) en SortedMap (árbol rojo-negro u otra implementación de árbol equilibrado). Y no estás restringido a ninguna clase o interfaz en particular.

Los aumentos en el rendimiento del mundo real son significativos en muchos casos. Por ejemplo, el aumento del rendimiento es de aproximadamente 5 veces más rápido que usar un comparador al ordenar Dobles usando toString() en una lista de tamaño 100k.

import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.Collections; 
import java.util.List; 
import java.util.ListIterator; 

public class GenericLetterSorter { 
    public enum Letter { 
     OMEGA_LETTER("Omega"), 
     GAMMA_LETTER("Gamma"), 
     BETA_LETTER("Beta"), 
     ALPHA_LETTER("Alpha"); 

     private final String description; 

     Letter() { 
      description = toString(); 
     } 

     Letter(String description) { 
      this.description = description; 
     } 

     public String getDescription() { 
      return description; 
     } 
    } 

public static void main(String[] args) { 
    List<Letter> list = new ArrayList<>(Arrays.asList(Letter.values())); 

    sort(list, new ToComparable<Letter>() { 
     @Override 
     public Comparable toComparable(Letter letter) { 
      // sort based on the letter's description 
      return letter == null ? null : letter.getDescription(); 
     } 
    }); 

    for (Letter letter : list) 
     System.out.println(letter == null ? null : letter.name()); 
} 

    public interface ToComparable<T, C extends Comparable<? super C>> { 
     C toComparable(T t); 
    } 

    public static <T, C extends Comparable<? super C>> void sort(List<T> list, ToComparable<T, C> function) { 
     class Pair implements Comparable<Pair> { 
      final T original; 
      final C comparable; 

      Pair(T original, C comparable) { 
      this.original = original; 
      this.comparable = comparable; 
      } 

      @Override 
      public int compareTo(Pair other) { 
       return 
        comparable == null && other.comparable == null ? 0 : 
        comparable == null ? -1 : 
        other.comparable == null ? 1 : 
        comparable.compareTo(other.comparable); 
      } 
     } 

     List<Pair> pairs = new ArrayList<>(list.size()); 
     for (T original : list) 
      pairs.add(new Pair(original, function.toComparable(original))); 

     Collections.sort(pairs); 

     ListIterator<T> iter = list.listIterator(); 
     for (Pair pair : pairs) { 
      iter.next(); 
      iter.set(pair.original); 
     } 
    } 
} 
Cuestiones relacionadas