Méthodes d’usine de commodité Java 9 pour les collections

Méthodes d'usine de commodité Java 9 pour les collections

1. Vue d'ensemble

Java 9 apporte le sucre syntaxique tant attendu pour créer de petites instancesCollections non modifiables en utilisant un code concis d'une ligne. SelonJEP 269, de nouvelles méthodes d'usine de commodité seront incluses dans JDK 9.

Dans cet article, nous couvrirons son utilisation ainsi que les détails de sa mise en œuvre.

2. Histoire et motivation

Créer un petitCollection immuable en Java est très détaillé en utilisant la méthode traditionnelle.

Prenons un exemple deSet:

Set set = new HashSet<>();
set.add("foo");
set.add("bar");
set.add("baz");
set = Collections.unmodifiableSet(set);

C’est beaucoup trop de code pour une tâche simple et cela devrait être possible en une seule expression.

Le est également vrai pour unMap, cependant, pourList, il existe une méthode d'usine:

List list = Arrays.asList("foo", "bar", "baz");

Bien que cette création deList soit meilleure que l'initialisation du constructeur, c'estless obvious car l'intuition commune ne serait pas de rechercher dans la classeArrays des méthodes pour créer unList:

Il existe d'autres moyens de réduire la verbosité comme la techniquedouble-brace:

Set set = Collections.unmodifiableSet(new HashSet() {{
    add("foo"); add("bar"); add("baz");
}});

ou en utilisant Java 8Streams:

Stream.of("foo", "bar", "baz")
  .collect(collectingAndThen(toSet(), Collections::unmodifiableSet));

La technique de la double attache est seulement un peu moins verbeuse mais réduit considérablement la lisibilité (et est considérée comme un anti-modèle).

La version Java 8, cependant, est une expression d’une ligne et pose également certains problèmes. Premièrement, ce n’est pas évident et intuitif, deuxièmement, c’est toujours verbeux, troisièmement, cela implique la création d’objets inutiles et quatrièmement, cette méthode ne peut pas être utilisée pour créer unMap.

Pour résumer les lacunes, aucune des approches ci-dessus ne traite le cas d'utilisation spécifique en créant un petit problème de première classe non modifiableCollection.

3. Description et utilisation

Des méthodes statiques ont été fournies pour les interfacesList,Set etMap qui prennent les éléments comme arguments et renvoient une instance deList,Set etMap respectivement.

Cette méthode est nomméeof(…) pour les trois interfaces.

3.1. List etSet

La signature et les caractéristiques des méthodes d'usineList etSet sont identiques:

static  List of(E e1, E e2, E e3)
static  Set  of(E e1, E e2, E e3)

utilisation des méthodes:

List list = List.of("foo", "bar", "baz");
Set set = Set.of("foo", "bar", "baz");

Comme vous pouvez le voir, c'est très simple, court et concis.

Dans l'exemple, nous avons utilisé la méthode avec prend exactement trois éléments comme paramètres et renvoie unList /Set de taille 3.

Cependant, il existe 12 versions surchargées de cette méthode - onze avec 0 à 10 paramètres et une avec var-args:

static  List of()
static  List of(E e1)
static  List of(E e1, E e2)
// ....and so on

static  List of(E... elems)

Dans la plupart des cas, 10 éléments suffisent, mais s'il en faut plus, la version var-args peut être utilisée.

Vous pouvez maintenant vous demander quel est l’intérêt d’avoir 11 méthodes supplémentaires s’il existe une version var-args qui peut fonctionner pour un nombre quelconque d’éléments.

La réponse à cette question est la performance. Every var-args method call implicitly creates an array. Having the overloaded methods avoid unnecessary object creation and the garbage collection overhead thereof.

Lors de la création d'unSet à l'aide d'une méthode de fabrique, si des éléments dupliqués sont passés en paramètres, alorsIllegalArgumentException est renvoyé à l'exécution:

@Test(expected = IllegalArgumentException.class)
public void onDuplicateElem_IfIllegalArgExp_thenSuccess() {
    Set.of("foo", "bar", "baz", "foo");
}

Un point important à noter ici est que puisque les méthodes d'usine utilisent des génériques, les types primitifs sont automatiquement contrôlés.

Si un tableau de type primitif est passé, unList dearray de ce type primitif est renvoyé.

Par exemple:

int[] arr = { 1, 2, 3, 4, 5 };
List list = List.of(arr);

Dans ce cas, unList<int[]> de taille 1 est renvoyé et l'élément à l'index 0 contient le tableau.

3.2. Map

La signature de la méthode d'usine deMapest:

static  Map of(K k1, V v1, K k2, V v2, K k3, V v3)

et l'utilisation:

Map map = Map.of("foo", "a", "bar", "b", "baz", "c");

Comme pourList etSet, la méthodeof(…) est surchargée pour avoir de 0 à 10 paires clé-valeur.

Dans le cas deMap, il existe une méthode différente pour plus de 10 paires clé-valeur:

static  Map ofEntries(Map.Entry... entries)

et c'est l'utilisation:

Map map = Map.ofEntries(
  new AbstractMap.SimpleEntry<>("foo", "a"),
  new AbstractMap.SimpleEntry<>("bar", "b"),
  new AbstractMap.SimpleEntry<>("baz", "c"));

Passer des valeurs en double pour Key générerait unIllegalArgumentException:

@Test(expected = IllegalArgumentException.class)
public void givenDuplicateKeys_ifIllegalArgExp_thenSuccess() {
    Map.of("foo", "a", "foo", "b");
}

Encore une fois, dans le cas deMap également, les types primitifs sont autoboxés.

4. Notes de mise en œuvre

Les collections créées à l'aide des méthodes de fabrique ne sont pas des implémentations couramment utilisées.

Par exemple, leList n'est pas unArrayList et leMap n'est pas unHashMap. Ce sont différentes implémentations qui sont introduites dans Java 9. Ces implémentations sont internes et leurs constructeurs ont un accès restreint.

Dans cette section, nous verrons quelques différences d'implémentation importantes qui sont communes aux trois types de collections.

4.1. Immuable

Les collections créées à l'aide de méthodes de fabrique sont immuables et la modification d'un élément, l'ajout de nouveaux éléments ou la suppression d'un élément jetteUnsupportedOperationException:

@Test(expected = UnsupportedOperationException.class)
public void onElemAdd_ifUnSupportedOpExpnThrown_thenSuccess() {
    Set set = Set.of("foo", "bar");
    set.add("baz");
}
@Test(expected = UnsupportedOperationException.class)
public void onElemModify_ifUnSupportedOpExpnThrown_thenSuccess() {
    List list = List.of("foo", "bar");
    list.set(0, "baz");
}
@Test(expected = UnsupportedOperationException.class)
public void onElemRemove_ifUnSupportedOpExpnThrown_thenSuccess() {
    Map map = Map.of("foo", "a", "bar", "b");
    map.remove("foo");
}

4.2. Aucun élémentnull autorisé

Dans le cas deList etSet, aucun élément ne peut êtrenull. Dans le cas d'unMap, ni les clés ni les valeurs ne peuvent être desnull. Passer l'argumentnull renvoie unNullPointerException:

@Test(expected = NullPointerException.class)
public void onNullElem_ifNullPtrExpnThrown_thenSuccess() {
    List.of("foo", "bar", null);
}

4.3. Instances basées sur la valeur

Les instances créées par les méthodes d'usine sont basées sur la valeur. Cela signifie que les usines sont libres de créer une nouvelle instance ou de renvoyer une instance existante.

Par conséquent, si nous créons des listes avec les mêmes valeurs, elles peuvent ou non faire référence au même objet sur le tas:

List list1 = List.of("foo", "bar");
List list2 = List.of("foo", "bar");

Dans ce cas,list1 == list2 peut ou non être évalué àtrue en fonction de la JVM.

4.4. La sérialisation

Les collections créées à partir des méthodes d'usine sontSerializable si les éléments de la collection sontSerializable.

5. Conclusion

Dans cet article, nous avons présenté les nouvelles méthodes d'usine pour les collections introduites dans Java 9.

Nous avons conclu pourquoi cette fonctionnalité est un changement bienvenu en passant en revue certaines méthodes antérieures de création de collections non modifiables. Nous avons couvert son utilisation et souligné les points clés à prendre en compte lors de leur utilisation.

Enfin, nous avons précisé que ces collections sont différentes des implémentations couramment utilisées et avons souligné les principales différences.

Le code source complet de cet article et les tests unitaires sontavailable over on GitHub.