whatwg spec java collections variadic-functions java-9

java - whatwg - html spec



¿Cuál es el objetivo de los Convenience Factory Methods overload for Collections en Java 9? (6)

Java 9 viene con métodos de fábrica de conveniencia para crear listas inmutables. Finalmente, una creación de lista es tan simple como:

List<String> list = List.of("foo", "bar");

Pero hay 12 versiones sobrecargadas de este método, 11 con 0 a 10 elementos y uno con var args.

static <E> List<E> of(E... elements)

Lo mismo es el caso de Set and Map .

Como hay un método var args, ¿de qué sirve tener 11 métodos adicionales?

Lo que creo es que los var-args crean una matriz, por lo que los otros 11 métodos pueden omitir la creación de un objeto adicional y, en la mayoría de los casos, de 0 a 10 elementos. ¿Hay alguna otra razón para esto?


Como sospechabas, esta es una mejora del rendimiento. Los métodos de Vararg crean una matriz "bajo el capó", y tener un método que tome 1-10 argumentos directamente evita esta creación de matriz redundante.


Del documento JEP mismo -

Descripción -

Estos incluirán sobrecargas varargs, de modo que no hay un límite fijo en el tamaño de la colección. Sin embargo, las instancias de la colección así creadas pueden ajustarse para tamaños más pequeños. Se proporcionarán API de casos especiales (sobrecargas de argumentos fijos) para hasta diez elementos. Si bien esto introduce un poco de desorden en la API, evita la asignación de la matriz, la inicialización y la sobrecarga de la recolección de basura en las llamadas varargs. Significativamente, el código fuente del sitio de llamadas es el mismo independientemente de si se llama una sobrecarga de arcos fijos o varargs.

Editar - Para agregar motivación y como ya se mencionó en los comentarios de @CKing también:

No objetivos :

No es un objetivo respaldar colecciones escalables de alto rendimiento con números arbitrarios de elementos. El foco está en colecciones pequeñas .

Motivación -

Crear una colección pequeña e inmodificable (por ejemplo, un conjunto) implica construirla, almacenarla en una variable local e invocar add () en ella varias veces, y luego envolverla.

Set<String> set = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("a", "b", "c")));

Java 8 Stream API se puede usar para construir colecciones pequeñas, combinando métodos de fábrica de secuencias y colectores.

// Java 8 Set<String> set1 = Collections.unmodifiableSet(Stream.of("a", "b", "c").collect(Collectors.toSet()));

Gran parte del beneficio de los literales de recopilación se puede obtener proporcionando API de la biblioteca para crear pequeñas instancias de recopilación, a un costo y un riesgo significativamente reducidos en comparación con el cambio del idioma. Por ejemplo, el código para crear una pequeña instancia de Set podría verse así:

// Java 9 Set set2 = Set.of("a", "b", "c");


Este patrón se usa para la optimización de métodos que aceptan parámetros varargs.

Si puede descubrir que la mayor parte del tiempo está usando solo dos de ellos, probablemente quiera definir una sobrecarga de métodos con la cantidad de parámetros más utilizados:

public void foo(int num1); public void foo(int num1, int num2); public void foo(int num1, int num2, int num3); public void foo(int... nums);

Esto te ayudará a evitar la creación de matriz mientras llamas al método varargs. El patrón utilizado para la optimización del rendimiento:

List<String> list = List.of("foo", "bar"); // Delegates call here static <E> List<E> of(E e1, E e2) { return new ImmutableCollections.List2<>(e1, e2); // Constructor with 2 parameters, varargs avoided! }

Lo más interesante detrás de esto es que a partir de 3 parámetros estamos delegando al constructor varargs de nuevo:

static <E> List<E> of(E e1, E e2, E e3) { return new ImmutableCollections.ListN<>(e1, e2, e3); // varargs constructor }

Esto parece extraño por ahora, pero como puedo adivinar, esto está reservado para mejoras futuras y como una opción, una posible sobrecarga de todos los constructores List3(3 params), List7(7 params)... y etc.


Puede encontrar el siguiente pasaje del ítem 42 de Effective Java (2nd ed.) De Josh Bloch:

Cada invocación de un método varargs causa una asignación de matriz e inicialización. Si ha determinado empíricamente que no puede pagar este costo, pero necesita la flexibilidad de los varargs, hay un patrón que le permite tener su pastel y comérselo también. Supongamos que ha determinado que el 95 por ciento de las llamadas a un método tienen tres o menos parámetros. A continuación, declare cinco sobrecargas del método, uno con cero a través de tres parámetros ordinarios, y un único método varargs para usar cuando el número de argumentos excede tres [...]


Según el documento Java : las colecciones devueltas por los métodos de fábrica de conveniencia son más eficientes en cuanto a espacio que sus equivalentes mutables.

Antes de Java 9:

Set<String> set = new HashSet<>(3); // 3 buckets set.add("Hello"); set.add("World"); set = Collections.unmodifiableSet(set);

En la implementación anterior de Set , hay 6 objetos creando: el contenedor no modificable; el HashSet , que contiene un HashMap ; la tabla de cubos (una matriz); y dos instancias de nodo (una para cada elemento). Si una VM toma 12 bytes por objeto, entonces hay 72 bytes consumiendo como gastos generales, más 28 * 2 = 56 bytes para 2 elementos. Aquí la gran cantidad es consumida por la sobrecarga en comparación con los datos almacenados en la colección. Pero en Java 9, esta sobrecarga es muy inferior.

Después de Java 9:

Set<String> set = Set.of("Hello", "World");

En la implementación anterior de Set , solo un objeto está creando y esto tomará muy poco espacio para contener datos debido a una sobrecarga mínima.


También puedes mirarlo al revés. Dado que los métodos varargs pueden aceptar matrices, dicho método serviría como un medio alternativo para convertir una matriz en una List .

String []strArr = new String[]{"1","2"}; List<String> list = List.of(strArr);

La alternativa a este enfoque es usar Arrays.asList pero cualquier cambio realizado en la List en este caso se reflejaría en la matriz, que no es el caso con List.of Por lo tanto, puede utilizar List.of cuando no desea que List y la matriz estén sincronizados.

Nota La justificación dada en la especificación parece una micro-optimización para mí. (Esto ahora ha sido confirmado por el propietario de la API en los comentarios a another respuesta)