Java 8 List <V>를 Map <K, V>로


932

Java 8의 스트림과 람다를 사용하여 객체 목록을 Map으로 변환하고 싶습니다.

이것이 Java 7 이하에서 작성하는 방법입니다.

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;
}

Java 8 및 Guava를 사용하여 쉽게 수행 할 수 있지만 Guava 없이이 작업을 수행하는 방법을 알고 싶습니다.

구아바에서 :

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();
        }
    });
}

Java 8 람다가있는 구아바.

private Map<String, Choice> nameMap(List<Choice> choices) {
    return Maps.uniqueIndex(choices, Choice::getName);
}

답변:


1394

Collectors문서를 기반 으로 다음과 같이 간단합니다.

Map<String, Choice> result =
    choices.stream().collect(Collectors.toMap(Choice::getName,
                                              Function.identity()));

167
참고로, Java 8 이후에도 JDK는 여전히 간결한 경쟁에서 경쟁 할 수 없습니다. 구아바 대안은 다음과 같이 쉽게 읽을 수 Maps.uniqueIndex(choices, Choice::getName)있습니다.
Bogdan Calmac

3
JOOL 라이브러리 (Java 8을 사용하는 모든 사람에게 권장 ) Seq에서 (정적으로 가져 오기 ) 를 사용하여 다음과 같이 간결성을 향상시킬 수 있습니다. seq(choices).toMap(Choice::getName)
lukens

5
Function.identity를 사용하면 어떤 이점이 있습니까? 내 말은, 그것은-> 짧습니다
12

9
@ shabunc 나는 어떤 이점도 몰라 실제로 it -> it자신을 사용 합니다. Function.identity()여기에서 주로 사용됩니다. 왜냐하면 그것은 참조 된 문서에서 사용 되었기 때문입니다. 그것이 글을 쓰는 시점에서 제가 람다에 대해 아는 전부였습니다
zapl

12
- 오, @zapl, 실제로이 뒤에 이유가 밝혀 stackoverflow.com/questions/28032827/...
shabunc

307

키가 목록의 모든 요소에 대해 고유 하지 않다고 보장하는 경우 키 Map<String, List<Choice>>대신Map<String, Choice>

Map<String, List<Choice>> result =
 choices.stream().collect(Collectors.groupingBy(Choice::getName));

76
이것은 실제로 고유하지 않은 키의 가능성을 다루는 Map <String, List <Choice >>를 제공하지만 OP가 요청한 것은 아닙니다. 구아바에서 Multimaps.index (choices, Choice :: getName)은 아마도 이것이 원하는 경우 더 나은 옵션 일 것입니다.
Richard Nichols

또는 오히려 동일한 키가 여러 값에 매핑되는 시나리오에서 매우 유용한 Guava의 Multimap <String, Choice>를 사용하십시오. 구아바에는 Map <String, List <Choice >>
Neeraj B.

1
@RichardNichols 왜 구아바 Multimaps방법이 더 나은 옵션입니까? Map객체를 반환하지 않기 때문에 불편할 수 있습니다 .
theyuv

156

사용 getName()키와 같은 Choice맵의 값 자체 :

Map<String, Choice> result =
    choices.stream().collect(Collectors.toMap(Choice::getName, c -> c));

19
사용자가 이해할 수 있도록 설명을 작성하십시오.
Mayank Jain

4
이 답변이 가장 좋기 때문에 여기에 더 자세한 내용이없는 것이 너무 나쁩니다.
MadConan

Collectors.toMap(Choice::getName,c->c)(2 자 짧음)
Ferrybig

7
그것은 choices.stream().collect(Collectors.toMap(choice -> choice.getName(),choice -> choice)); 키의 첫 번째 기능, 값의 두 번째 기능
waterscar와 동일합니다.

16
이해하고 이해하는 c -> c것이 쉽지만 Function.identity()더 의미적인 정보를 전달합니다. 나는 보통 정적 임포트를 사용해서 그냥 사용할 수있다identity()
Hank D

28

Collectors.toMap ()을 사용하지 않으려는 경우를위한 또 다른 방법이 있습니다.

Map<String, Choice> result =
   choices.stream().collect(HashMap<String, Choice>::new, 
                           (m, c) -> m.put(c.getName(), c),
                           (m, u) -> {});

1
Collectors.toMap()위의 예제에서 보았 듯이 다음 또는 우리 자신의 HashMap 을 사용하는 것이 더 낫 습니까?
Swapnil Gangrade

1
이 예는지도에 다른 것을 배치하는 방법에 대한 예를 제공했습니다. 메서드 호출에서 제공하지 않는 값을 원했습니다. 감사!
th3morg

1
세 번째 인수 함수가 올바르지 않습니다. 두 HashMaps을, 해시 맵 :: putAll에 뭔가를 병합하는 몇 가지 기능이 제공해야
jesantana

25

나열된 답변의 대부분은 목록에 항목이 중복 된 경우를 놓치십시오 . 이 경우 답변이 발생 IllegalStateException합니다. 목록 중복처리 하려면 아래 코드 참조하십시오 .

public Map<String, Choice> convertListToMap(List<Choice> choices) {
    return choices.stream()
        .collect(Collectors.toMap(Choice::getName, choice -> choice,
            (oldValue, newValue) -> newValue));
  }

19

간단한 방법으로 하나 더 옵션

Map<String,Choice> map = new HashMap<>();
choices.forEach(e->map.put(e.getName(),e));

3
이 유형 또는 Java 7 유형을 사용하는 데 실질적인 차이는 없습니다.
Ashish Lohia

3
나는 다른 메커니즘보다 무슨 일이 일어나고 있는지 이해하고 이해하기가 더 쉽다는 것을 알았습니다
Freiheit

2
SO는 Java 8 Streams에 물었다.
Mehraj Malik

18

예를 들어, 오브젝트 필드를 맵으로 변환하려면 다음을 수행하십시오.

예제 개체 :

class Item{
        private String code;
        private String name;

        public Item(String code, String name) {
            this.code = code;
            this.name = name;
        }

        //getters and setters
    }

그리고 작업 목록을 맵으로 변환 :

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));

12

써드 파티 라이브러리를 사용하지 않아도 AOL의 cyclops-react lib (제공자 인 공개)에는 ListMap을 포함한 모든 JDK 콜렉션 유형에 대한 확장이 있습니다 .

ListX<Choices> choices;
Map<String, Choice> map = choices.toMap(c-> c.getName(),c->c);

10

나는 이것을 시도하고 있었고 위의 답변을 사용 Functions.identity()하여 Map의 키를 사용할 때 this::localMethodName실제로 타이핑 문제로 작동 하는 것과 같은 로컬 메소드를 사용하는 데 문제가 있음을 발견했습니다 .

Functions.identity()이 경우 실제로 타이핑에 무언가를 수행하므로 메소드는 Object매개 변수를 반환 하고 수락 해야만 작동합니다.Object

이 문제를 해결하기 위해 버리고 대신 Functions.identity()사용 s->s했습니다.

따라서 내 코드는 디렉토리에있는 모든 디렉토리를 나열하고 각 디렉토리에 대해 디렉토리의 이름을 맵의 키로 사용한 다음 디렉토리 이름으로 메소드를 호출하고 항목 모음을 반환합니다.

Map<String, Collection<ItemType>> items = Arrays.stream(itemFilesDir.listFiles(File::isDirectory))
.map(File::getName)
.collect(Collectors.toMap(s->s, this::retrieveBrandItems));

10

IntStream을 사용하여 인덱스 스트림을 만든 다음 맵으로 변환 할 수 있습니다.

Map<Integer,Item> map = 
IntStream.range(0,items.size())
         .boxed()
         .collect(Collectors.toMap (i -> i, i -> items.get(i)));

1
이것은 모든 요소에 대해 get () 호출을 수행하므로 조작의 복잡성을 증가시키기 때문에 좋은 옵션이 아닙니다 (항목이 해시 맵 인 경우 o (n * k)).
니콜라스 노벨리스

해시 맵 O (1)에 있지 않습니까?
Ivo van der Veeken

@IvovanderVeeken 코드 스 니펫의 get (i) 은지도가 아닌 목록에 있습니다.
Zaki

@Zaki 나는 Nicolas의 발언에 대해 이야기하고있었습니다. 항목이 목록 대신 해시 맵 인 경우 n * k 복잡성이 표시되지 않습니다.
Ivo van der Veeken

8

나는 genericsinversion of control을 사용하여 list를 map으로 변환하는 방법을 쓸 것이다 . 그냥 보편적 인 방법!

어쩌면 우리는이 정수의 목록 또는 목록 개체를. 문제는 다음과 같습니다. 지도의 핵심은 무엇 입니까?

인터페이스 만들기

public interface KeyFinder<K, E> {
    K getKey(E e);
}

이제 제어 반전을 사용합니다.

  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));
    }

예를 들어, book 객체가있는 경우이 클래스는지도의 키를 선택하는 것입니다.

public class BookKeyFinder implements KeyFinder<Long, Book> {
    @Override
    public Long getKey(Book e) {
        return e.getPrice()
    }
}

6

나는이 문법을 사용한다

Map<Integer, List<Choice>> choiceMap = 
choices.stream().collect(Collectors.groupingBy(choice -> choice.getName()));

11
groupingByMap<K,List<V>>아닌을 만듭니다 Map<K,V>.
Christoffer Hammarström 2016 년

3
ulises의 대답은 대답한다. 그리고, String getName();(정수가 아님)
Barett

5
Map<String, Set<String>> collect = Arrays.asList(Locale.getAvailableLocales()).stream().collect(Collectors
                .toMap(l -> l.getDisplayCountry(), l -> Collections.singleton(l.getDisplayLanguage())));

4

이것은 두 가지 방법으로 수행 할 수 있습니다. 사람이 우리가 그것을 설명하는 데 사용할 수업이되도록하십시오.

public class Person {

    private String name;
    private int age;

    public String getAge() {
        return age;
    }
}

하자 사람이 사람의 목록이 될지도로 변환 할

1. 목록에서 Simple foreach 및 Lambda 표현식 사용

Map<Integer,List<Person>> mapPersons = new HashMap<>();
persons.forEach(p->mapPersons.put(p.getAge(),p));

2. 주어진 목록에 정의 된 스트림에서 수집기 사용.

 Map<Integer,List<Person>> mapPersons = 
           persons.stream().collect(Collectors.groupingBy(Person::getAge));

4

이를 위해 스트림을 사용할 수 있습니다. 을 명시 적으로 사용할 필요가 없도록 정적으로 Collectors가져올 수 있습니다 toMap(Effective Java, 3 판에서 권장).

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));
}


3
Map<String,Choice> map=list.stream().collect(Collectors.toMap(Choice::getName, s->s));

이 목적을 위해서도

Map<String,Choice> map=  list1.stream().collect(()-> new HashMap<String,Choice>(), 
            (r,s) -> r.put(s.getString(),s),(r,s) -> r.putAll(s));

3

동일한 키 이름에 대한 모든 새 값을 재정의해야하는 경우 :

public Map < String, Choice > convertListToMap(List < Choice > choices) {
    return choices.stream()
        .collect(Collectors.toMap(Choice::getName,
            Function.identity(),
            (oldValue, newValue) - > newValue));
}

모든 선택 사항을 이름 목록으로 그룹화해야하는 경우 :

public Map < String, Choice > convertListToMap(List < Choice > choices) {
    return choices.stream().collect(Collectors.groupingBy(Choice::getName));
}

1
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.

0

guava하나 의 대안으로 kotlin-stdlib를 사용할 수 있습니다

private Map<String, Choice> nameMap(List<Choice> choices) {
    return CollectionsKt.associateBy(choices, Choice::getName);
}

-1
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);

중복 키 AA {12 = ASDFASDFASDF, 7 = EEDDDAD, 4 = CCCC, 3 = BBB, 2 = AA}


2
여기서 뭘하려고합니까 ?? 질문을 읽었습니까?
navderm 2016 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.