使用Java 8和lambdas

时间:2015-06-27 16:35:08

标签: java lambda

我有

List<Gift> gifts = new ArrayList<>();
gifts .add(new Gift().withType(INF, CHD));
gifts .add(new Gift().withType(ADT, CHD));
gifts .add(new Gift().withType(INF, ADT));

礼品有一种方法List<Type> getTypes();

现在我想把礼物清单变成类似的东西 Map<Type,List<Gift>>。 我想在一行中使用Java 8和lambdas。有可能吗?

public class Gift {

    public List<Type> getTypes() {
        return types;
    }

    public Gift withType(Type... types) {
        this.types = Arrays.asList(types);
        return this;
    }

    List<Type> types = new ArrayList<>();
}

public enum Type {
    ADT,
    CHD,
    INF;
}

以前的旧代码(看起来非常糟糕)。这就是我的全部。

Map<Type, List<Gift>> byTypes = new HashMap<>();

for (Gift gift : gifts) {
    for (Type type : gift.getTypes()) {
        List<Gift> giftList = byTypes.get(type);
        if (giftList == null) {
            giftList = new ArrayList<>();
        }
        giftList.add(gift);
        byTypes.put(type,giftList);
    }
}

4 个答案:

答案 0 :(得分:3)

使用Guava的Multimap

ListMultimap<Type, Gift> multimap = ArrayListMultimap.create();

gifts.forEach(g -> g.getTypes().forEach(t -> multimap.put(t, g)));

Map<Type, Collection<Gift>> map = multimap.asMap();

答案 1 :(得分:1)

好的,我找到了满足我的解决方案:-)我写了我的收藏家:-D

Map<Type, List<Gift>> collect1 = gifts.stream().collect(new TypeToManyGiftCollector());

public class TypeToManyGiftCollector
        implements Collector<Gift, Map<Type, List<Gift>>, Map<Type, List<Gift>>> {

    @Override
    public Supplier<Map<Type, List<Gift>>> supplier() {
        return () -> new HashMap<Type, List<Gift>>() {{
            for (Type type : Type.values()) {
                put(type, new ArrayList<Gift>());
            }
        }};
    }

    @Override
    public BiConsumer<Map<Type, List<Gift>>, Gift> accumulator() {
        return (Map<Type, List<Gift>> map, Gift gift) -> {
            gift.getTypes().stream().forEach(type -> map.get(type).add(gift));
        };
    }

    @Override
    public BinaryOperator<Map<Type, List<Gift>>> combiner() {
        return (Map<Type, List<Gift>> map1, Map<Type, List<Gift>> map2) ->
        {
            for (Type type : Type.values()) {
                map1.get(type).addAll(map2.get(type));
            }
            return map1;
        };
    }

    @Override
    public Function<Map<Type, List<Gift>>, Map<Type, List<Gift>>> finisher() {
        return Function.identity();
    }

    @Override
    public Set<Characteristics> characteristics() {
        return Collections.unmodifiableSet(EnumSet.of(IDENTITY_FINISH));
    }
}

答案 2 :(得分:0)

重新引入中间对类P,但方式略有不同。我想我们可能期望在以后的Java迭代中,这将成为更常见的地方,并且更容易引入内置的Pair类型和Value Objects的实现。

package play;

import java.io.IOException;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static play.Type.*;

public class Play {
  static class P<K,V> { K k; V v; P(K kk,V vv) {k=kk;v=vv;}}
  public static void main(String[] args) throws IOException {
    List<Gift> gifts = new ArrayList<>();
    gifts .add(new Gift().withType(INF, CHD));
    gifts .add(new Gift().withType(ADT, CHD));
    gifts .add(new Gift().withType(INF, ADT));
    Map<Type,List<Gift>> m = gifts.stream()
      .flatMap((g)->g.getTypes().stream().map((t)->new P<>(t,g)))
      .collect(
        Collectors.groupingBy(
          (p)->p.k,
          // Does not work with <code>new EnumMap<>(Type.class)</code>
          // I dont know why, ...
          ()->new EnumMap(Type.class),
          Collectors.mapping(
            (p)->p.v, 
            Collectors.toList() 
          )
        )
      );
    System.out.println("Map: "+m.toString());
  }
}

我在这段代码中唯一的问题是我无法解释是否需要将“无类型”地图作为地图工厂。如果您知道原因,请随时解释一下......

答案 3 :(得分:0)

我用reduce方法找到了其他解决方案。在我看来,较小的一个,更容易理解。

      HashMap<Type, List<Gift>> result = gifts.stream().reduce(
            new HashMap<Type, List<Gift>>() {{
                asList(Type.values()).stream().forEach(type -> put(type, new ArrayList<>()));
            }}
            ,
            (map, gift) -> {
                gift.getTypes().stream().forEach(type -> map.get(type).add(gift));
                return map;
            }
            ,
            (map1, map2) -> {
                asList(Type.values()).stream().forEach(type -> map1.get(type).addAll(map2.get(type)));
                return map1;
            }
    );