java - 如何从键的 Map<K,V> 和 List<K> 创建 List<T>?

标签 java java-8 java-stream

使用 Java 8 lambda,有效创建新 List<T> 的“最佳”方法是什么?给出 List<K>可能的键和一个 Map<K,V> ?这是给你 List 的场景可能的Map键并预计生成 List<T>其中 T是基于 V 的某些方面构建的某种类型, 映射值类型。

我已经探索了一些,并且觉得声称一种方法比另一种方法更好(可能有一个异常(exception) - 请参阅代码)。我会将“最佳”解释为代码清晰度和运行时效率的结合。这些是我想出的。我相信有人可以做得更好,这是这个问题的一个方面。我不喜欢 filter大多数方面,因为这意味着需要创建中间结构并多次传递名称 List .现在,我选择示例 6 —— 一个普通的 'ol 循环。 (注意:代码注释中有一些神秘的想法,尤其是“需要从外部引用...”,这意味着在 lambda 的外部。)

public class Java8Mapping {
    private final Map<String,Wongo> nameToWongoMap = new HashMap<>();
    public Java8Mapping(){
        List<String> names = Arrays.asList("abbey","normal","hans","delbrook");
        List<String> types = Arrays.asList("crazy","boring","shocking","dead");
        for(int i=0; i<names.size(); i++){
            nameToWongoMap.put(names.get(i),new Wongo(names.get(i),types.get(i)));
        }
    }

    public static void main(String[] args) {
        System.out.println("in main");
        Java8Mapping j = new Java8Mapping();
        List<String> testNames = Arrays.asList("abbey", "froderick","igor");
        System.out.println(j.getBongosExample1(testNames).stream().map(Bongo::toString).collect(Collectors.joining(", ")));
        System.out.println(j.getBongosExample2(testNames).stream().map(Bongo::toString).collect(Collectors.joining(", ")));
        System.out.println(j.getBongosExample3(testNames).stream().map(Bongo::toString).collect(Collectors.joining(", ")));
        System.out.println(j.getBongosExample4(testNames).stream().map(Bongo::toString).collect(Collectors.joining(", ")));
        System.out.println(j.getBongosExample5(testNames).stream().map(Bongo::toString).collect(Collectors.joining(", ")));
        System.out.println(j.getBongosExample6(testNames).stream().map(Bongo::toString).collect(Collectors.joining(", ")));
    }

    private static class Wongo{
        String name;
        String type;
        public Wongo(String s, String t){name=s;type=t;}
        @Override public String toString(){return "Wongo{name="+name+", type="+type+"}";}
    }

    private static class Bongo{
        Wongo wongo;
        public Bongo(Wongo w){wongo = w;}
        @Override public String toString(){ return "Bongo{wongo="+wongo+"}";}
    }

    // 1:  Create a list externally and add items inside 'forEach'.
    //     Needs to externally reference Map and List
    public List<Bongo> getBongosExample1(List<String> names){
        final List<Bongo> listOne = new ArrayList<>();
        names.forEach(s -> {
                  Wongo w = nameToWongoMap.get(s);
                  if(w != null) {
                      listOne.add(new Bongo(nameToWongoMap.get(s)));
                  }
              });
        return listOne;
    }

    // 2: Use stream().map().collect()
    //    Needs to externally reference Map
    public List<Bongo> getBongosExample2(List<String> names){
        return names.stream()
              .filter(s -> nameToWongoMap.get(s) != null)
              .map(s -> new Bongo(nameToWongoMap.get(s)))
              .collect(Collectors.toList());
    }

    // 3: Create custom Collector
    //    Needs to externally reference Map
    public List<Bongo> getBongosExample3(List<String> names){
        Function<List<Wongo>,List<Bongo>> finisher = list -> list.stream().map(Bongo::new).collect(Collectors.toList());
        Collector<String,List<Wongo>,List<Bongo>> bongoCollector =
              Collector.of(ArrayList::new,getAccumulator(),getCombiner(),finisher, Characteristics.UNORDERED);

        return names.stream().collect(bongoCollector);
    }
    // example 3 helper code
    private BiConsumer<List<Wongo>,String> getAccumulator(){
        return (list,string) -> {
            Wongo w = nameToWongoMap.get(string);
            if(w != null){
                list.add(w);
            }
        };
    }
    // example 3 helper code
    private BinaryOperator<List<Wongo>> getCombiner(){
        return (l1,l2) -> {
            l1.addAll(l2);
            return l1;
        };
    }

    // 4: Use internal Bongo creation facility
    public List<Bongo> getBongosExample4(List<String> names){
        return names.stream().filter(s->nameToWongoMap.get(s) != null).map(s-> new Bongo(nameToWongoMap.get(s))).collect(Collectors.toList());
    }

    // 5: Stream the Map EntrySet.  This avoids referring to anything outside of the stream, 
    // but bypasses the lookup benefit from Map.
    public List<Bongo> getBongosExample5(List<String> names){
        return nameToWongoMap.entrySet().stream().filter(e->names.contains(e.getKey())).map(e -> new Bongo(e.getValue())).collect(Collectors.toList());
    }

    // 6: Plain-ol-java loop
    public List<Bongo> getBongosExample6(List<String> names){
        List<Bongo> bongos = new ArrayList<>();
        for(String s : names){
            Wongo w = nameToWongoMap.get(s);
            if(w != null){
                bongos.add(new Bongo(w));
            }
        }
        return bongos;
    }
}

最佳答案

如果 namesToWongoMap 是实例变量,您就无法真正避免捕获 lambda。

您可以通过进一步拆分操作来清理流:

return names.stream()
    .map(n -> namesToWongoMap.get(n))
    .filter(w -> w != null)
    .map(w -> new Bongo(w))
    .collect(toList());
return names.stream()
    .map(namesToWongoMap::get)
    .filter(Objects::nonNull)
    .map(Bongo::new)
    .collect(toList());

这样你就不会调用 get 两次。

这非常类似于 for 循环,除了,例如,如果 namesToWongoMap 不能同时发生变化,理论上它可以并行化。

I don't like the filter aspect of most as it means needing to create intermediate structures and multiple passes over the names List.

没有中间结构,列表只有一次传递。流管道表示“对于每个元素……执行此操作序列”。每个元素被访问一次并应用管道。

以下是来自 java.util.stream package description 的一些相关引述:

A stream is not a data structure that stores elements; instead, it conveys elements from a source such as a data structure, an array, a generator function, or an I/O channel, through a pipeline of computational operations.

Processing streams lazily allows for significant efficiencies; in a pipeline such as the filter-map-sum example above, filtering, mapping, and summing can be fused into a single pass on the data, with minimal intermediate state.

关于java - 如何从键的 Map<K,V> 和 List<K> 创建 List<T>?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30538647/

相关文章:

java - 按对象数组列表分组而不创建映射

Java 日历和日期格式

java - HTTP 响应无法获取响应正文

java - AWS Athena ClientExecutionTimeoutException

java - Stream Java 中的私有(private)排序规则

java - NoSuchMethodException : java. time.LocalDateTime.<init>() 使用 Super CSV 读取 CSV

Java 8 流 : find items from one list that match conditions calculated based on values from another list

java - 当您在其中调用另一个方法时,方法的执行流程是否等待?

java - 流 : how map in Streams work

java - 如何使用流从实际数组列表中获取子列表