Как отсортировать элементы enum по алфавиту в Java? - PullRequest
11 голосов
/ 14 ноября 2009

У меня есть перечислимый класс, подобный следующему:

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 в том порядке, в котором я их создала в классе enum Letter. Есть ли способ вывести значения перечисления в алфавитном порядке? Нужен ли мне отдельный объект компаратора или есть встроенный способ сделать это? По сути, я бы хотел, чтобы значения сортировались по алфавиту на основе текста getDescription ():

Alpha
Beta
Gamma
Omega

Ответы [ 4 ]

19 голосов
/ 14 ноября 2009
SortedMap<String, Letter> map = new TreeMap<String, Letter>();
for (Letter l : Letter.values()) {
    map.put(l.getDescription, l);
}
return map.values();

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

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

6 голосов
/ 14 ноября 2009

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

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

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

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

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


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

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

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

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

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

Теперь примеры кода ...

/** 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 работает не только для одного перечислимого класса, но работает для любого перечислимого класса в вашем проекте, который реализует интерфейс Described .

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

5 голосов
/ 14 ноября 2009

Просто отсортируйте их, используя Arrays.sort и ваш собственный компаратор.

0 голосов
/ 25 сентября 2013

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

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

Реальное увеличение производительности во многих случаях является значительным. Например, увеличение производительности примерно в 5 раз быстрее, чем при использовании компаратора при сортировке значений Double с использованием 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'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);
       }
    }
}
...