Производительность перечислений Java - PullRequest
5 голосов
/ 20 декабря 2010

Я думал об использовании типа enum для управления i18n в Java-игре, которую я разрабатываю, но мне было интересно узнать о проблемах с производительностью, которые могут возникнуть при работе с перечислениями, которые содержат много элементов (я думаю, тысячи).

На самом деле я пытаюсь что-то вроде:

public enum Text {
  STRING1,
  STRING2,
  STRING3;

  public String text() {
    return text;
  }

  public String setText() {
    this.text = text;
  }
}

Затем, чтобы загрузить их, я могу просто заполнить поля:

static
{
  Text.STRING1.setText("My localized string1"); 
  Text.STRING2.setText("My localized string2"); 
  Text.STRING3.setText("My localized string3"); 
}

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

Что я спрашиваю:

  • - это объект, выделенный (в дополнение к строке) для каждого элемента?(Думаю, да, поскольку перечисления реализованы с помощью объектов)
  • как получить правильный элемент из перечисления?это статично во время компиляции?(Я имею в виду, когда где-то я использую Text.STRING1.text()).Так что это должно быть постоянной сложности, или, может быть, они просто заменяются на этапе компиляции ..
  • в целом, это хороший подход или я должен ждать чего-то еще?

Спасибо

Ответы [ 6 ]

14 голосов
/ 20 декабря 2010

Найден и адаптирован хороший набор перечислений и ResourceBundle:

public enum Text {
  YELL, SWEAR, BEG, GREET /* and more */ ;

  /** Resources for the default locale */
  private static final ResourceBundle res =
    ResourceBundle.getBundle("com.example.Messages");

  /** @return the locale-dependent message */
  public String toString() {
    return res.getString(name() + ".string");
  }
}

# File com/example/Messages.properties
# default language (english) resources
YELL.string=HEY!
SWEAR.string=§$%&
BEG.string=Pleeeeeease!
GREET.string=Hello player!

# File com/example/Messages_de.properties
# german language resources
YELL.string=HEY!
SWEAR.string=%&$§
BEG.string=Biiiiiitte!
GREET.string=Hallo Spieler!
11 голосов
/ 20 декабря 2010

Возможно, вам лучше использовать класс java.util.ResourceBundle.Он предназначен для решения именно этой проблемы.

Чтобы ответить на ваши вопросы:

  1. Да, существует ровно один экземпляр каждого значения перечисления.
  2. Да, константасложность поиска значения Enum.
  3. Не совсем.Изменение содержания / поведения enum своего рода сводит на нет цель иметь перечисления в первую очередь.Предполагается, что они представляют константы фиксированного диапазона с безопасностью типов.Вы можете делать подобные вещи, но это не то, для чего они были предназначены.
7 голосов
/ 20 декабря 2010

Ненавижу переходить к теме, но, полагаясь на перечисления для i18n, вы в конечном итоге загоните вас в угол. Java имеет надлежащую поддержку i18n, даже если у нее есть учебник .

2 голосов
/ 20 декабря 2010

хотя в java есть поддержка i18n с использованием ResourceBundle, я не думаю, что идея использовать enum для этой цели настолько плоха. Я считаю, что эти 2 подхода могут быть объединены. Вы можете создать enum Texts, который содержит все ваши текстовые идентификаторы. Вы можете создавать пакеты ресурсов для каждого поддерживаемого языка и использовать те же идентификаторы в этом пакете.

Затем реализуйте метод getText() в перечислении следующим образом:

return ResourceBundle.getBundle("texts").getString(name());

Итак, вам не нужно заботиться об инициализации текстов для каждого языка. Об этом заботится стандартный механизм.

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

Я, вероятно, буду использовать этот подход в моем следующем проекте. Спасибо за идею!

1 голос
/ 21 декабря 2010

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

public static void main(String[] args) throws Exception {
    PrintWriter w = new PrintWriter("C:\\test.java");
    w.println("enum Test {");
    for (int i = 0; i < 3000; i++) {
        w.println("c" + i + ",");
    }
    w.println("}");
    w.close();
}

eclipse говорит

Код для статического инициализатора превышает ограничение 65535 байт

То жетест с использованием всего лишь 2000 констант компилируется без ошибок.

Конечно, если у вас столько констант, было бы неплохо организовать их в несколько исходных файлов.

Да, один(и только один) объект выделяется для каждой константы перечисления.С 2000 константами это колоссальные 16 КБ памяти :-) (на 32-битной виртуальной машине Sun другие виртуальные машины могут немного отличаться)

Каждая константа перечисления является объектом, и у каждой из них есть поле text,Поле не является окончательным и, следовательно, не подлежит встраиванию.Да, доступ к полям имеет постоянное время.

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

Хорошие подходы включают:

Делегирование в ResourceBundle, как показывает AlexR.Недостаток: вы должны вручную управлять файлами ресурсов.Если вы сделаете это, я рекомендую UnitTest для обнаружения опечаток / пропущенных / лишних ключей ресурсов или даже утилиту командной строки, чтобы добавить недостающие ключи в файл ресурсов, чтобы вам не пришлось (неправильно) вводить их.

Если вы поддерживаете только несколько языков, вы можете в качестве альтернативы сохранить все языки в перечислении:

enum Message {
    Hello("Hello", "Hallo", "Salut");

    String en;
    String de;
    String fr;

    Message(String en, String de, String fr) {
        this.en = en;
        this.fr = fr;
        this.it = it;
    }

Недостатки: редактирование не выполняется мирянами (необходим компилятор), а кодировка исходного файла лучшеподдерживать все специальные символы на целевом языке (экранирование Юникода неудобно ...).Кроме того, исходный файл загромождается, если у вас более 3 или 4 языков.

Преимущества: добавление / удаление текстов выполняется очень быстро, и компилятор ловит все опечатки в имени текста и "ресурс"file "всегда согласован.

В любом случае, вы должны использовать MessageFormat, как объясняет учебник, на который ссылается R.Bemrose в своем ответе.

И, наконец, при работе с Enums вы можете найтиудобный метод values ​​():

for (Text t : Text.values()) {

}
0 голосов
/ 20 декабря 2010

Я согласен, что перечисление лучше всего подходит для ключей I18n, а не для строк, в которые они переводятся.Однако для вашей конкретной проблемы вы должны конструктор, а не сеттер.ИМХО, на самом деле вы должны использовать конструктор в 90% + случаев, когда значение устанавливается в конструкции, а не изменяется, а не с помощью установщика.Не беспокойтесь об игре, уточнить и гибкость должны быть рассмотрены в первую очередь.1000 перечислений может добавить 1 мс ко времени запуска вашего приложения.cf Загрузка текста из файла, вероятно, добавит 10 мс.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...