Реализация битового поля с использованием перечислений java

Я веду большой архив документов и часто использую битовые поля для записи статуса Моих документов во время обработки или при их проверке. Мой устаревший код просто использует статические константы int, такие как:

static int DOCUMENT_STATUS_NO_STATE = 0
static int DOCUMENT_STATUS_OK = 1
static int DOCUMENT_STATUS_NO_TIF_FILE = 2
static int DOCUMENT_STATUS_NO_PDF_FILE = 4

Это позволяет довольно легко указать состояние документа, установив соответствующие флаги. Например:

status = DOCUMENT_STATUS_NO_TIF_FILE | DOCUMENT_STATUS_NO_PDF_FILE;

поскольку подход к использованию статических констант является плохой практикой, и потому что я хотел бы улучшить код, я был хотите использовать перечисления для достижения того же. Существует несколько требований, одним из которых является необходимость сохранения статуса в базе данных как числового типа. Поэтому необходимо преобразовать константы перечисления в числовое значение. Ниже приведен мой первый подход, и я задаюсь вопросом, правильно ли это сделать?

class DocumentStatus{

    public enum StatusFlag {

        DOCUMENT_STATUS_NOT_DEFINED(1<<0),
        DOCUMENT_STATUS_OK(1<<1), 
        DOCUMENT_STATUS_MISSING_TID_DIR(1<<2),
        DOCUMENT_STATUS_MISSING_TIF_FILE(1<<3),
        DOCUMENT_STATUS_MISSING_PDF_FILE(1<<4),
        DOCUMENT_STATUS_MISSING_OCR_FILE(1<<5),
        DOCUMENT_STATUS_PAGE_COUNT_TIF(1<<6),
        DOCUMENT_STATUS_PAGE_COUNT_PDF(1<<7),
        DOCUMENT_STATUS_UNAVAILABLE(1<<8);


        private final long statusFlagValue;

        StatusFlag(long statusFlagValue) {
            this.statusFlagValue = statusFlagValue;
        }

        public long getStatusFlagValue(){
            return statusFlagValue;
        } 

       }


    /**
     * Translates a numeric status code into a Set of StatusFlag enums
     * @param numeric statusValue 
     * @return EnumSet representing a documents status
     */
    public EnumSet<StatusFlag> getStatusFlags(long statusValue) {
        EnumSet statusFlags = EnumSet.noneOf(StatusFlag.class);
        StatusFlag.each { statusFlag -> 
            long flagValue = statusFlag.statusFlagValue
            if ( (flagValue&statusValue ) == flagValue ) {
               statusFlags.add(statusFlag);
            }
        }
        return statusFlags;
    }


    /**
     * Translates a set of StatusFlag enums into a numeric status code
     * @param Set if statusFlags
     * @return numeric representation of the document status 
     */
    public long getStatusValue(Set<StatusFlag> flags) {
        long value=0;
        flags.each { statusFlag -> 
            value|=statusFlag.getStatusFlagValue() 
        }
        return value;
    }

     public static void main(String[] args) {

        DocumentStatus ds = new DocumentStatus();
        Set statusFlags = EnumSet.of(
            StatusFlag.DOCUMENT_STATUS_OK,
            StatusFlag.DOCUMENT_STATUS_UNAVAILABLE);

        assert ds.getStatusValue( statusFlags )==258 // 0000.0001|0000.0010

        long numericStatusCode = 56;
        statusFlags = ds.getStatusFlags(numericStatusCode);

        assert !statusFlags.contains(StatusFlag.DOCUMENT_STATUS_OK);
        assert statusFlags.contains(StatusFlag.DOCUMENT_STATUS_MISSING_TIF_FILE);
        assert statusFlags.contains(StatusFlag.DOCUMENT_STATUS_MISSING_PDF_FILE);
        assert statusFlags.contains(StatusFlag.DOCUMENT_STATUS_MISSING_OCR_FILE);

    }

}

5 ответов


ваш подход-это именно то, как это сделать.


вместо определения параметров конструктора вы можете просто использовать внутренний ordinal() значение для вычисления этого.

public enum StatusFlag {

    DOCUMENT_STATUS_NOT_DEFINED,
    DOCUMENT_STATUS_OK, 
    DOCUMENT_STATUS_MISSING_TID_DIR,
    DOCUMENT_STATUS_MISSING_TIF_FILE,
    DOCUMENT_STATUS_MISSING_PDF_FILE,
    DOCUMENT_STATUS_MISSING_OCR_FILE,
    DOCUMENT_STATUS_PAGE_COUNT_TIF,
    DOCUMENT_STATUS_PAGE_COUNT_PDF,
    DOCUMENT_STATUS_UNAVAILABLE;


    public long getStatusFlagValue(){
        return 1 << this.ordinal();
    } 

}

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


немного лучшим способом было бы сохранить результат 1 << this.ordinal() в поле, когда построены значения перечисления. Таким образом, вам не нужно предоставлять каждое значение вручную, а флаг вычисляется только один раз.

public enum StatusFlag {

  DOCUMENT_STATUS_NOT_DEFINED,
  DOCUMENT_STATUS_OK, 
  DOCUMENT_STATUS_MISSING_TID_DIR,
  DOCUMENT_STATUS_MISSING_TIF_FILE,
  DOCUMENT_STATUS_MISSING_PDF_FILE,
  DOCUMENT_STATUS_MISSING_OCR_FILE,
  DOCUMENT_STATUS_PAGE_COUNT_TIF,
  DOCUMENT_STATUS_PAGE_COUNT_PDF,
  DOCUMENT_STATUS_UNAVAILABLE;

  public final int flag;

  StatusFlag() { 
    this.flag = 1 << this.ordinal();
  } 
}

EDIT:было бы полезно иметь метод isFlagSet.


не давайте значения перечислений. Используйте EnumSet объединить их, и использовать Enum.ordinal() при сохранении для преобразования в / из одного целого числа. Вы также можете найти Class.getEnumConstants() полезно при восстановлении множества из целого числа.


Я сделал полную библиотеку для этой задачи: http://claude-martin.ch/enumbitset/

главная цель-магазина наборы типов enum в разряды. Но он также поддерживает другие типы.

с этим вам не понадобятся дополнительные методы, такие как"getStatusFlags ()". Его можно использовать на любом существующем типе перечисления, просто добавив интерфейс EnumBitSetHelper (используется как "черта"). Затем каждая константа enum может создать "EnumBitSet", который имеет все методы EnumSet и BitSet Java. Затем вы можете работать с этими наборами констант перечисления и конвертировать их в поле значения.

Он поддерживает множество форматов, таких как BigInteger и long, чтобы легко хранить значение в битовом поле. Но учтите, что это работает только с Java версии 8 и новее.