Estoy preguntando cuál es la mejor forma es en Java 8 para trabajar con todos los valores de una enumeración. Específicamente cuando usted necesita para obtener todos los valores y agregarlo a otro lugar, por ejemplo, suponiendo que tenemos la siguiente enumeración:

public enum Letter {
 A, B, C, D;
}

Yo podría, por supuesto, hacer lo siguiente:

for (Letter l : Letter.values()) {
    foo(l);
}

Pero, yo también podría agregue el método siguiente a la definición enum:

public static Stream<Letter> stream() {
    return Arrays.stream(Letter.values());
}

Y, a continuación, reemplace el de arriba con:

Letter.stream().forEach(l -> foo(l));

Es este enfoque ACEPTAR o tiene alguna falla en el diseño o rendimiento? Por otra parte, ¿por qué no las enumeraciones tiene un flujo() método?

  • ¿Por qué no añadir un método static void forEach(Consumer<Letter> action) a su enum? A continuación, puede simplemente llamar con Letter.forEach(l -> foo(l)).
  • Esto parece demasiado para mí. Puesto que las matrices no son compatibles con la corriente de la API sólo quiero usar un bucle for y hacer. Parece que estás añadiendo complejidad sólo para ser nuevo y brillante, pero no gana nada aquí.
  • Esto se ve como la optimización. Yo recomendaría hacer un punto de referencia para su situación específica y de la tecnología de la pila mediante el uso de JMH o Pinza y utilizar el mejor enfoque.
  • Primero que funcione, y luego ir por las optimizaciones de rendimiento, no la otra manera alrededor.
  • esa es una de las cosas que yo estaba pensando, si en un esfuerzo para hacer el código más java8 manera en que yo no era «matar una mosca con un bazooka». Pero en el otro lado, el código con el flujo de método se ve mejor 😉
  • Como un aparte, forEach(l -> foo(l)) podría estar mejor escrito como forEach(foo).
  • C# la gente se mueve incómodamente al hacerlo – he aquí por qué.
  • Tenga en cuenta que llamar Enum.values devuelve un Enum[]. Como una matriz es mutable de la enum debe devolver una copia diferente de la matriz cada vez (sólo en caso de que alguien mutado es); esto significa que cualquier llamada a Enum.values debe ser O(n). Este es un fallo en el diseño de enum – una inmutable Set habría sido una opción mucho mejor – pero esto significa que si el rendimiento es fundamental, entonces usted debe almacenar en caché Enum.value en una inmutable Set y que ya ofrecen un stream() método…
  • la Araña: o simplemente utilizar EnumSet.allOf(Letter.class).stream() que usará una matriz compartida bajo el capó. O como esta respuesta señala, incluso no necesita un Stream cuando todo el OP quiere es forEach(…).
  • es eso correcto? Buscando en la código fuente el EnumSet llama a un abstract addAll método dado que EnumSet también es mutable – ¿no que también tiene que ser O(n)?
  • la Araña: No, ese no es el estándar de la Colección de addAll método, es un especial enum método específico. Ni siquiera tocar una matriz.
  • Me escribió una rápida jmh de referencia a intentar poner algunos números a todo esto. Sorprendentemente parece que Enum.values() es bastante significativamente más rápido que otros enfoques. Tal vez estoy abusando de JMH de alguna manera…

InformationsquelleAutor Rumal | 2015-04-28

4 Comentarios

  1. 26

    Tres preguntas: respuesta de tres partes:

    Está bien desde un punto de vista del diseño?

    Absolutamente. Nada de malo con ello. Si usted necesita para hacer un montón de iterar sobre su enumeración, la secuencia de la API, es la limpieza del camino a seguir y el ocultamiento de la caldera de la placa de atrás un poco el método está bien. Aunque me gustaría considerar OldCumudgeon‘s versión aún mejor.

    Está bien desde un punto de vista del rendimiento?

    Lo más probable es que no importa. La mayoría de las veces, las enumeraciones no son tan grandes. Por lo tanto, cualquiera que sea la sobrecarga de un método u otro, probablemente, no importa en el 99,9% de los casos.

    Por supuesto, hay el 0.1% donde lo hace. En ese caso: medir correctamente, con su mundo real de los datos y de los consumidores.

    Si tuviera que apostar, yo esperaría que el for each bucle para ser más rápido, ya que se asigna a más directamente en el modelo de memoria, pero no trate de adivinar cuando se habla de rendimiento, y no la melodía antes de que no hay necesidad real para la optimización. Escribir el código en un camino que es correcto en primer lugar, fácil a leer la segunda y sólo luego preocuparse por el rendimiento de estilo de código.

    ¿Por qué no las Enumeraciones correctamente integrado en el Flujo de la API?

    Si se compara Java Stream API para el equivalente en muchos otros idiomas, aparece seriamente limitada. Hay varias piezas que faltan (reutilizables Arroyos y Opcionales como Arroyos, por ejemplo). Por otro lado, la implementación de la Secuencia de la API fue sin duda un gran cambio para la API. Fue aplazado varias veces por una razón. Así que supongo que Oracle quería limitar los cambios a la mayoría de los casos de uso. Las enumeraciones no se usan mucho de todos modos. Asegúrese de que, cada proyecto tiene un par de ellos, pero son nada en comparación con el número de Listas y otras Colecciones. Incluso cuando usted tiene un Enum, en muchos casos, que no siempre va a iterar sobre ella. Muestra y Establece, por otro lado, son probablemente recorrer casi todo el tiempo. Supongo que estas fueron las razones por las Enumeraciones no obtener su propio adaptador a la Corriente del mundo. Vamos a ver si a más de esto se agrega que en versiones futuras. Y hasta entonces siempre se puede utilizar Arrays.stream.

    • En cuanto a rendimiento, si es que esto es un bucle ajustado, entonces puede ser vale la pena destacar que values() en realidad clones de la matriz. Así que es una buena oferta de la asignación, arraycopying, y liberando a los que tiene que pasar. Probablemente muy bien optimizado por la JVM, pero si es posible escribir Letter[] allLetters = Letter.values() sólo una vez y, a continuación, bucle sobre que, que obtendrá una ganancia en el rendimiento.
  2. 27

    Me gustaría ir para EnumSet. Porque forEach() también se define en Iterable, usted puede evitar la creación de la corriente por completo:

    EnumSet.allOf(Letter.class).forEach(x -> foo(x));

    O con un método de referencia:

    EnumSet.allOf(Letter.class).forEach(this::foo);

    Aún, la vieja escuela de bucle se siente un poco más simple:

    for (Letter x : Letter.values()) {
        foo(x);
    }
    • Esta parece una buena solución para las enumeraciones, dado el uso de EnumSet.
    • Incluso cuando se utiliza el flujo adicional de los intermedios de las operaciones, por ejemplo,EnumSet.allOf(Letter.class) .stream().filter(…).forEach(…), el EnumSet es la solución preferida, ya que evita la creación de la matriz temporal, que podría ser más costoso que el ligero Stream instancia (a menos que la JVM detecta y a la vez disminuye la matriz como «defensiva copia», pero incluso entonces el uso de EnumSet está a la par con otras soluciones)
    • A la derecha, el truco de EnumSet es que nunca se asigna una enumeración de la matriz, sino que utiliza un vector de bits en lugar de (un solo largo o una matriz de enteros largos según el recuento de la enumeración de las constantes).
  3. 12

    Mi supongo es que las enumeraciones son limitados en tamaño (i.e el tamaño no está limitado por el lenguaje, pero limitada por el uso)y por lo tanto no es necesario un nativo stream api. Los arroyos son muy buenos cuando se tiene que manipular transformar y recuerden los elementos en una secuencia; estos no son comunes los usos caso de Enum (por lo general, usted iterar sobre los valores enum, pero rara vez se necesita para transformar, mapa y recoger de ellos).

    Si usted sólo tiene que hacer una acción en cada uno de los elementos tal vez debería exponer sólo un método forEach

         public static void forEach(Consumer<Letter> action) {
                Arrays.stream(Letter.values()).forEach(action);
         }
    
         .... //example of usage
         Letter.forEach(e->System.out.println(e));
    • «rara vez se necesita para transformar, mapa y recoger» Así que depende de lo que estás haciendo con ellos. Hoy en día enum se recomienda como un reemplazo para, por ejemplo, bits de banderas y cosas por el estilo. Esto es facilitado por el EnumSet.
    • de hecho parece extraño porque normalmente no proceso de las banderas, de procesar los datos que contiene una bandera (o banderas) en ella.
    • EnumSet es un Conjunto que es una Colección y la Colección tiene la secuencia() método para que en java8 si utiliza EnumSet ya tiene la secuencia de capacidades
    • Eso es cierto. Supongo que mi propio punto valida la hipótesis de una manera.
    • System.out::println es más idiomática.
  4. 5

    Creo que el menor código para obtener un Stream de constantes enum es Stream.of(Letter.values()). No es tan bonito como Letter.values().stream() pero ese es un problema con las matrices, no específicamente de las enumeraciones.

    Por otra parte, ¿por qué no las enumeraciones tener un stream() método?

    Tienes razón en que el mejor posible, sería Letter.stream(). Por desgracia, una clase no puede tener dos métodos con la misma firma, por lo que no sería posible implícitamente agregar un método estático stream() a cada enum (de la misma manera que cada enum tiene un implícitamente añadido método estático values()), ya que esto rompería todos los enumeración que ya tiene un método estático o de instancia sin parámetros llamados stream().

    Es este enfoque OK?

    Me lo creo. El inconveniente es que stream es un método estático, por lo que no hay manera de evitar la duplicación de código, habría que ser añadido a cada enum por separado.

Dejar respuesta

Please enter your comment!
Please enter your name here