Хранение EnumSet в базе данных?

Таким образом в C++/C# можно создать перечисления флагов для содержания нескольких значений, и хранение единственного значимого целого числа в базе данных, конечно, тривиально.

В Java у Вас есть EnumSets, которые, кажется, вполне хороший способ раздать перечисления в памяти, но как Вы производите объединенный EnumSet к целому числу для устройства хранения данных? Там другой путь состоит в том, чтобы приблизиться к этому?

18
задан Flexo 27 April 2014 в 13:29
поделиться

5 ответов

// From Adamski's answer
public static <E extends Enum<E>> int encode(EnumSet<E> set) {
    int ret = 0;

    for (E val : set) {
        ret |= 1 << val.ordinal();
    }

    return ret;
}

@SuppressWarnings("unchecked")
private static <E extends Enum<E>> EnumSet<E> decode(int code,
        Class<E> enumType) {
    try {
        E[] values = (E[]) enumType.getMethod("values").invoke(null);
        EnumSet<E> result = EnumSet.noneOf(enumType);
        while (code != 0) {
            int ordinal = Integer.numberOfTrailingZeros(code);
            code ^= Integer.lowestOneBit(code);
            result.add(values[ordinal]);
        }
        return result;
    } catch (IllegalAccessException ex) {
        // Shouldn't happen
        throw new RuntimeException(ex);
    } catch (InvocationTargetException ex) {
        // Probably a NullPointerException, caused by calling this method
        // from within E's initializer.
        throw (RuntimeException) ex.getCause();
    } catch (NoSuchMethodException ex) {
        // Shouldn't happen
        throw new RuntimeException(ex);
    }
}
7
ответ дан 30 November 2019 в 06:19
поделиться

Если вы посмотрите в исходниках на RegularEnumSet, который является реализацией для <= 64 членов Enum, вы увидите, что он содержит:

/**
 * Bit vector representation of this set.  The 2^k bit indicates the
 * presence of universe[k] in this set.
 */
private long elements = 0L;

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

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

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

5
ответ дан 30 November 2019 в 06:19
поделиться

Хранить порядковый номер как представление EnumSet - не лучшая идея. Порядковые номера зависят от порядка определения в классе Enum (, связанное обсуждение здесь ). Ваша база данных может быть легко нарушена рефакторингом, который изменяет порядок значений Enum или вводит новые в середине.

Вы должны ввести стабильное представление отдельных значений перечисления. Это могут быть значения типа int, представленные в способе, предложенном для EnumSet .

Ваши Enums могут реализовывать интерфейсы, поэтому стабильное представление может быть непосредственно в значении перечисления (адаптировано из Адамски):

interface Stable{
    int getStableId();
}
public enum X implements Stable {
    A(1), B(2);

    private int stableId;

    X(int id){
        this.stableId = id;
    }

    @Override public int getStableId() {
        return stableId;
    }
}

адаптировано из кода Адамски:

public <E extends Stable> int encode(EnumSet<E> set) {
  int ret = 0;

  for (E val : set) {
    ret |= (1 << val.getStableId());
  }

  return ret;
}
16
ответ дан 30 November 2019 в 06:19
поделиться

EnumSet реализует Serializable, но если вы используете его, то это очень накладно (он записан как массив идентификаторов, а не как BitSet, как вы могли бы ожидать, плюс заголовок объектного потока)

.
3
ответ дан 30 November 2019 в 06:19
поделиться

Правильный способ представления коллекции из нескольких выбранных параметров - использование массива путем именования тэга SELECT суффиксом [].
Проблема в том, что он неправильно обрабатывается методом jQuery serialize ().
Для такого SELECT infact:

<select name="a[]">
    <option value="five">5</option>
    <option value="six">6</option>
    <option value="seven">7</option>
</select>

serialize отправляет этот массив: a [] = 0 & a [] = 1 & a [] = 2 получен PHP таким образом:

[a] => Array
    (
        [0] => 0
        [1] => 1
        [2] => 2
    )

, где реальные значения теряются.

-121--4749311-

Пока я этого не сделал, другие пользователи Интернета, например,

Мы исследовали относительные достоинства C++ и Erlang в реализации параллельного отслеживания акустических лучей алгоритм для ВМС США. Мы нашли гораздо меньшая кривая обучения и улучшение среды отладки для параллельный эрланг, чем для программирование C++ на основе потоков. Наш Реализация C++ превзошла Программа Erlang не менее чем на 12x. Попытки использования Erlang в Камеру Микропроцессор BE был разочарован Память Эрланга. (Источник)

И что-то ближе к моему сердцу, что я помню после конкурса ICFP:

Кодирование было очень простым, перевод псевдокода в C++. Я мог бы использовать Java или C #, но я на точка, где программирование на высоком уровне уровень в C++ так же прост, и я хотел сохранить возможность быстрого опускаясь вниз в некоторые низкоуровневые немного трепещет, если он спустился к нему. Эрланг - мой другой любимый язык за взлом в, но беспокоился о достижении некоторой производительности проблема, которую я не смог устранить я от. (Источник)

-121--865222-

Обеспечивая соответствие перечисления int (т.е. существует < = 32 значения), я бы свернул собственную реализацию, используя порядковое значение каждого перечисления; Например,

public <E extends Enum<E>> int encode(EnumSet<E> set) {
  int ret = 0;

  for (E val : set) {
    // Bitwise-OR each ordinal value together to encode as single int.
    ret |= (1 << val.ordinal());
  }

  return ret;
}

public <E extends Enum<E>> EnumSet<E> decode(int encoded, Class<E> enumKlazz) {
  // First populate a look-up map of ordinal to Enum value.
  // This is fairly disgusting: Anyone know of a better approach?
  Map<Integer, E> ordinalMap = new HashMap<Integer, E>();
  for (E val : EnumSet.allOf(enumKlazz)) {
    ordinalMap.put(val.ordinal(), val);
  }

  EnumSet<E> ret= EnumSet.noneOf(enumKlazz);
  int ordinal = 0;

  // Now loop over encoded value by analysing each bit independently.
  // If the bit is set, determine which ordinal that corresponds to
  // (by also maintaining an ordinal counter) and use this to retrieve
  // the correct value from the look-up map.
  for (int i=1; i!=0; i <<= 1) {
    if ((i & encoded) != 0) {
      ret.add(ordinalMap.get(ordinal));
    }

    ++ordinal;
  }

  return ret;
}

Отказ от ответственности : Я не тестировал это!

EDIT

Как упоминает Томас в комментариях, порядковые номера нестабильны тем, что любое изменение определения перечисления в коде приведет к повреждению кодировок в базе данных (например, если вставить новое значение перечисления в середине существующего определения). Мой подход к решению этой проблемы заключается в определении таблицы «Enum» для перечисления, содержащей числовой идентификатор (, а не порядковый номер ) и значение перечисления последовательность. При запуске приложения Java первое, что делает уровень DAO, это считывает каждую таблицу Enum в память и:

  • Убедитесь, что все значения перечисления последовательность в базе данных соответствуют определению Java.
  • Инициализируйте двунаправленную карту ID для перечисления и наоборот, которую я затем использую всякий раз, когда я сохраняю перечисление (Другими словами, все таблицы «данных» ссылаются на идентификатор перечисления базы данных, а не явно сохраняют Последовательность значение).

Это гораздо более чистый/надежный IMHO, чем порядковый подход, который я описал выше.

12
ответ дан 30 November 2019 в 06:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: