Je souhaite traduire une liste d'objets en une carte à l'aide des flux et des lambdas de Java 8.
Voici comment je l'écrirais dans Java 7 et inférieur.
private Map<String, Choice> nameMap(List<Choice> choices) {
final Map<String, Choice> hashMap = new HashMap<>();
for (final Choice choice : choices) {
hashMap.put(choice.getName(), choice);
}
return hashMap;
}
Je peux y arriver facilement avec Java 8 et Guava, mais j'aimerais savoir comment le faire sans Guava.
En goyave:
private Map<String, Choice> nameMap(List<Choice> choices) {
return Maps.uniqueIndex(choices, new Function<Choice, String>() {
@Override
public String apply(final Choice input) {
return input.getName();
}
});
}
Et Goyave avec Java 8 lambdas.
private Map<String, Choice> nameMap(List<Choice> choices) {
return Maps.uniqueIndex(choices, Choice::getName);
}
Basé sur Collectors
documentation c'est aussi simple que:
Map<String, Choice> result =
choices.stream().collect(Collectors.toMap(Choice::getName,
Function.identity()));
Si votre clé estPASgarantie d'être unique pour tous les éléments de la liste, vous devez la convertir en Map<String, List<Choice>>
au lieu d'un Map<String, Choice>
Map<String, List<Choice>> result =
choices.stream().collect(Collectors.groupingBy(Choice::getName));
Utilisez getName () comme clé et choisissez lui-même comme valeur de la carte:
Map<String, Choice> result =
choices.stream().collect(Collectors.toMap(Choice::getName, c -> c));
En voici un autre au cas où vous ne voudriez pas utiliser Collectors.toMap ()
Map<String, Choice> result =
choices.stream().collect(HashMap<String, Choice>::new,
(m, c) -> m.put(c.getName(), c),
(m, u) -> {});
La plupart des réponses énumérées manquent un cas lorsque la liste contient des éléments en double . Dans ce cas, la réponse jettera IllegalStateException
. Référez-vous au code ci-dessous pour gérer les doublons de liste ainsi:
public Map<String, Choice> convertListToMap(List<Choice> choices) {
return choices.stream()
.collect(Collectors.toMap(Choice::getName, choice -> choice,
(oldValue, newValue) -> newValue));
}
Une autre option de manière simple
Map<String,Choice> map = new HashMap<>();
choices.forEach(e->map.put(e.getName(),e));
Par exemple, si vous souhaitez convertir des champs d’objet en mappage:
Exemple d'objet:
class Item{
private String code;
private String name;
public Item(String code, String name) {
this.code = code;
this.name = name;
}
//getters and setters
}
Et opération convertir List To Map:
List<Item> list = new ArrayList<>();
list.add(new Item("code1", "name1"));
list.add(new Item("code2", "name2"));
Map<String,String> map = list.stream()
.collect(Collectors.toMap(Item::getCode, Item::getName));
Si vous ne craignez pas l’utilisation de bibliothèques tierces, le fichier cyclops-react lib d’AOL possède des extensions pour tous Collection JDK types, y compris Liste et Carte .
ListX<Choices> choices;
Map<String, Choice> map = choices.toMap(c-> c.getName(),c->c);
J'essayais de faire cela et j'ai constaté qu'en utilisant les réponses ci-dessus, lorsque j'ai utilisé Functions.identity()
pour la clé de la carte, j'avais des problèmes avec l'utilisation d'une méthode locale comme this::localMethodName
pour fonctionner réellement à cause de problèmes de frappe.
Functions.identity()
fait réellement quelque chose à la dactylographie dans ce cas afin que la méthode ne fonctionne qu'en renvoyant Object
et en acceptant un paramètre de Object
Pour résoudre ce problème, j'ai fini par abandonner Functions.identity()
et à utiliser s->s
à la place.
Ainsi, mon code, dans mon cas pour répertorier tous les répertoires d'un répertoire, et pour chacun d'eux, utilise le nom du répertoire comme clé de la carte, puis appelle une méthode portant le nom du répertoire et renvoie une collection d'éléments.
Map<String, Collection<ItemType>> items = Arrays.stream(itemFilesDir.listFiles(File::isDirectory))
.map(File::getName)
.collect(Collectors.toMap(s->s, this::retrieveBrandItems));
Vous pouvez créer un flux des index en utilisant un IntStream, puis les convertir en carte:
Map<Integer,Item> map =
IntStream.range(0,items.size())
.boxed()
.collect(Collectors.toMap (i -> i, i -> items.get(i)));
Je vais écrire comment convertir une liste en carte en utilisant generics et inversion of control . Juste méthode universelle!
Peut-être nous avons liste des entiers ou liste des objets. La question est donc la suivante: que devrait être la clé de la carte?
créer une interface
public interface KeyFinder<K, E> {
K getKey(E e);
}
utilise maintenant l'inversion de contrôle:
static <K, E> Map<K, E> listToMap(List<E> list, KeyFinder<K, E> Finder) {
return list.stream().collect(Collectors.toMap(e -> Finder.getKey(e) , e -> e));
}
Par exemple, si nous avons des objets de livre, cette classe doit choisir la clé pour la carte
public class BookKeyFinder implements KeyFinder<Long, Book> {
@Override
public Long getKey(Book e) {
return e.getPrice()
}
}
J'utilise cette syntaxe
Map<Integer, List<Choice>> choiceMap =
choices.stream().collect(Collectors.groupingBy(choice -> choice.getName()));
Map<String, Set<String>> collect = Arrays.asList(Locale.getAvailableLocales()).stream().collect(Collectors
.toMap(l -> l.getDisplayCountry(), l -> Collections.singleton(l.getDisplayLanguage())));
Pour cela, il est possible d'utiliser des flux. Pour supprimer le besoin d'utiliser explicitement Collectors
, il est possible d'importer toMap
de manière statique (comme recommandé par Effective Java, troisième édition).
import static Java.util.stream.Collectors.toMap;
private static Map<String, Choice> nameMap(List<Choice> choices) {
return choices.stream().collect(toMap(Choice::getName, it -> it));
}
Voici la solution par StreamEx
StreamEx.of(choices).toMap(Choice::getName, c -> c);
Cela peut être fait de 2 manières. Soit personne la classe que nous allons utiliser pour le démontrer.
public class Person {
private String name;
private int age;
public String getAge() {
return age;
}
}
Soit personnes la liste des personnes à convertir sur la carte
1.Utiliser Simple foreach et une expression Lambda sur la liste
Map<Integer,List<Person>> mapPersons = new HashMap<>();
persons.forEach(p->mapPersons.put(p.getAge(),p));
2.Utilisation de collecteurs sur un flux défini dans la liste donnée.
Map<Integer,List<Person>> mapPersons =
persons.stream().collect(Collectors.groupingBy(Person::getAge));
List<V> choices; // your list
Map<K,V> result = choices.stream().collect(Collectors.toMap(choice::getKey(),choice));
//assuming class "V" has a method to get the key, this method must handle case of duplicates too and provide a unique key.
String array[] = {"ASDFASDFASDF","AA", "BBB", "CCCC", "DD", "EEDDDAD"};
List<String> list = Arrays.asList(array);
Map<Integer, String> map = list.stream()
.collect(Collectors.toMap(s -> s.length(), s -> s, (x, y) -> {
System.out.println("Dublicate key" + x);
return x;
},()-> new TreeMap<>((s1,s2)->s2.compareTo(s1))));
System.out.println(map);
Clé publique AA {12 = ASDFASDFASDF, 7 = EEDDDAD, 4 = CCCC, 3 = BBB, 2 = AA}
Map<String,Choice> map=list.stream().collect(Collectors.toMap(Choice::getName, s->s));
Même sert ce but pour moi,
Map<String,Choice> map= list1.stream().collect(()-> new HashMap<String,Choice>(),
(r,s) -> r.put(s.getString(),s),(r,s) -> r.putAll(s));
If every new value for the same key name has to be overidden
then below will be the code :
public Map<String, Choice>
convertListToMap(List<Choice> choices) {
return choices.stream()
.collect(Collectors.toMap(Choice::getName,
Function.identity(),
(oldValue, newValue) -> newValue));
}
If all choices have to be grouped in a list for a name then
below will be the code:
public Map<String, Choice>
convertListToMap(List<Choice> choices) {
return choices.stream().collect(Collectors.groupingBy(Choice::getName));
}