Enum и метод values



Если вы новичок и мало знаете про enum, то лучше начать с этого лонгрида. Сегодня обсудим итерацию через метод values и как его оптимизировать.



Итак, enums — это синтаксический сахар, который при компиляции превращается в обычный класс. Класс из примера выше превратится в



public final class Number extends Enum<Number
>



Элементы енума станут статическими полями:



public static final Number ONE;

public static final Number TWO;

public static final Number THREE;



Внутри нового класса появится массив:



Number[] VALUES = { ONE, TWO, THREE};



И его копия будет возвращаться в методе values:



return VALUES.clone();



При каждом вызове values возвращается новая копия массива. Дело в том, что массивы — это изменяемый объект. Если возвращать ссылку на VALUES напрямую, любой желающий сможет поменять исходный массив:



Number.values()[2] = ONE;



Это небезопасно, поэтому каждый раз возвращается копия.



Если цикл с values используется в высоконагруженном коде, то разумно сохранить массив в отдельную переменную и переиспользовать её:



static Number[] numbers = Number.values();



for (Number n : numbers) {…}



Если код вызывается редко, то смысла в отдельной переменной нет.



Пример из жизни



В Spring Web 5.2 в классе HttpStatus есть такой код:



for (HttpStatus status : values()) {

if (status.value == statusCode) {

return status;

}

}



Этот цикл вызывается почти в каждом запросе, но только в этом году завели баг. К описанию прилагался бенчмарк: при нагрузке 600 запросов/сек код производил мегабайт мусора каждую секунду.



Теперь код выглядит так:



private static final HttpStatus[] VALUES;



static {

VALUES = values();

}



for (HttpStatus status : VALUES) {

if (status.value == statusCode) {

return status;

}

}



Ответ на вопрос перед постом



Будет создано 3 массива: один внутри класса Number и два клона при вызове values()