Как отсортировать элементы перечисления по алфавиту в 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;
    }
}

Позже мой код я в основном перебираю перечисление Letter и печатаю его элементы на консоль:

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

Я думал, что метод values ​​() дал бы мне упорядоченный вид enum (как упоминалось здесь), но это не случай здесь. Я просто получаю членов перечисления в том порядке, в котором я их создал, в классе перечисления Letter. Есть ли способ вывести значения перечисления в алфавитном порядке? Нужен ли мне отдельный объект компаратора или есть встроенный способ сделать это? В основном я хотел бы, чтобы значения были отсортированы в алфавитном порядке на основе текста getDescription():

Alpha
Beta
Gamma
Omega

Ответ 1

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

Или просто измените порядок объявлений: -)

Изменить: Как указывал KLE, это предполагает, что описания уникальны в перечислении.

Ответ 2

Я думал, что метод values ​​() дал бы мне упорядоченное представление перечисления (как упоминалось здесь), но здесь это не так. Я просто получаю членов перечисления в том порядке, в котором я их создал, в классе перечисления Letter.

Точно так же порядок декларации считается значимым для перечислений, поэтому мы рады, что они возвращены именно в этом порядке. Например, когда int i представляет значения перечисления, выполнение values()[i] - очень простой и эффективный способ найти экземпляр enum. Чтобы идти вразрез с ошибкой, метод ordinal() возвращает индекс экземпляра перечисления.

Есть ли способ вывести значения перечисления в алфавитном порядке? Нужен ли мне отдельный объект компаратора или есть встроенный способ сделать это? В основном я хотел бы, чтобы значения были отсортированы в алфавитном порядке на основе текста getDescription():

То, что вы называете значением, не является чем-то определенным для перечислений в целом. Здесь, в вашем контексте, вы имеете в виду результат getDescription().

Как вы говорите, вы могли бы создать компаратор для этих описаний. Это было бы прекрасно: -)


Обратите внимание, что в общем случае вам может понадобиться несколько заказов для этих экземпляров:

  • порядок объявления (это официальный заказ)
  • порядок описания
  • другие при необходимости

Вы также можете немного подтолкнуть это понятие DescriptionComparator:

  • По соображениям производительности вы можете хранить вычисленные описания.

  • Поскольку перечисления не могут наследовать, повторное использование кода должно быть вне класса enum. Позвольте мне привести пример, который мы будем использовать в наших проектах:

Теперь образцы кода...

/** 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);

Обратите внимание, что общий код в EnumUtils работает не только для одного класса enum, но работает для любого класса enum в вашем проекте, который реализует интерфейс Described.

Как говорилось ранее, точка с кодом вне перечислений (где он иначе принадлежал) заключается в повторном использовании кода. Это не имеет большого значения для двух перечислений, но у нас в нашем проекте более тысячи перечислений, многие из них с теми же интерфейсами...!

Ответ 3

Просто отсортируйте их с помощью массива Arrays.sort и собственного компаратора.

Ответ 4

Вот общий способ сделать это с любым классом без необходимости реализации Comparable в классе, который вы сортируете или создаете пользовательский компаратор. Я нашел экземпляры, где я не хочу переопределять compareTo, потому что он служит другой цели, вы не можете перечислить все равно, и постоянно создавать классы-оболочки - это боль. Вы можете передать функцию, которая выводит объект Comparable, который вы хотите использовать для сортировки.

Функция toComparable вызывается только один раз для каждого элемента в списке (не так для пользовательского компаратора), поэтому особенно хорошо, если этот вызов является дорогостоящим для некоторого класса. Нулевые значения обрабатываются внутренне, поэтому их проще использовать, чем пользовательский компаратор. Один вызов алгоритма TimSort Java 7 значительно эффективнее, чем выполнение вложений в O (log N) в SortedMap (реализация красного-черного дерева или другого сбалансированного дерева). И вы не ограничены каким-либо конкретным классом или интерфейсом.

Во многих случаях значительный рост производительности в реальном времени значителен. Например, увеличение производительности примерно в 5 раз быстрее, чем при использовании компаратора при сортировке парных чисел, использующих toString() в списке размером 100 тыс.

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 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);
       }
    }
}