3

想象一下我有一个像这样的文件:

1, Apple
1, Pear
1, Orange
2, Apple
3, Melon
3, Orange

我想将它解析成一个列表,每个条目都是一个映射,或者我猜它可能是我自己的对象,但我认为映射是最好的,因为它是一个键。价值。

我在尝试:

private List<Map<String, String>> readRecords(String path) {
    return Files.lines(Paths.get(path))
            .map(line -> Arrays.asList(line.split(SEPARATOR)))
            .map(snippets -> new HashMap<Integer, String>().put(Integer.parseInt(snippets.get(0)), snippets.get(1)))
            .collect(Collectors.toList());
}  

但这给了我一个关于无法在List<String>和之间转换的错误List<Map<String, String>>

或者也许有更好的方法来做到这一点?

4

5 回答 5

5

除了返回类型不正确(List<Map<Integer, String>>)之外,该put方法还将为您提供先前映射的值(如果有)。

所以

.map(snippets -> new HashMap<Integer, String>().put(Integer.parseInt(snippets.get(0)), snippets.get(1)))

实际上是映射List<String> -> String而不是List<String> -> Map<Integer, String>

如果你想坚持使用官方 API,我会返回 a List<SimpleEntry<Integer, String>>,因为每一行代表一个键值对并将map调用更改为:

.map(snippets -> new SimpleEntry<>(Integer.parseInt(snippets.get(0)), snippets.get(1)))

...或将整个内容收集到一个Map<Integer, List<String>>(查看groupingBy收集器)。

于 2015-08-04T12:24:06.220 回答
2

map您应该从第二个lambda返回创建的地图:

private List<Map<Integer, String>> readRecords(String path) {
    return Files.lines(Paths.get(path))
            .map(line -> Arrays.asList(line.split(SEPARATOR)))
            .map(snippets -> {
                Map<Integer, String> map = new HashMap<Integer, String>();
                map.put(Integer.parseInt(snippets.get(0)), snippets.get(1));
                return map; 
            })
            .collect(Collectors.toList());
}  

由于您的地图仅包含单个映射,因此最好使用singletonMap

private List<Map<Integer, String>> readRecords(String path) throws IOException {
    return Files.lines(Paths.get(path))
        .map(line -> Arrays.asList(line.split(SEPARATOR)))
        .map(snippets -> Collections.singletonMap(Integer.parseInt(snippets.get(0)), snippets.get(1)))
        .collect(Collectors.toList());
}  
于 2015-08-04T12:24:58.160 回答
2

主要问题是这一行:

snippets -> new HashMap<Integer, String>().put(Integer.parseInt(snippets.get(0)), snippets.get(1))

这个 lambda 的返回类型是String因为Map.put(key,value)返回的不是地图本身,而是value.

除此之外,我不太确定使用完全成熟的、可变的哈希映射来存储单个键值对是否合理。我可能会将值对收集到单个映射中,而不是单条目映射列表中。


Eran 发现的另一个问题是您的方法应该返回List<Map<Integer,String>>而不是List<Map<String,String>>.

于 2015-08-04T12:23:09.413 回答
1

我想在这里使用地图是一种矫枉过正。您可以查看https://docs.oracle.com/javase/8/javafx/api/javafx/util/Pair.html

于 2015-08-04T12:23:08.063 回答
0

添加到我对原始帖子的评论中,这是我想出的代码。显然,它假设您确实想要一个一对多的关系,一个整数映射到 a List<String>

public class MappingDemo {

    public static void main(String[] args) {
        MappingDemo demo = new MappingDemo();
        System.out.println("... Using custom collector ...");
        demo.dumpMap(demo.getFruitMappingsWithCustomCollector());
        System.out.println("... Using 'External' map ...");
        demo.dumpMap(demo.getFruitMappingsWithExternalMap());
    }

    public Map<Integer, List<String>> getFruitMappingsWithCustomCollector(){
        // Resulting map is created from within the lambda expression.
        return getContent().stream().map(s -> s.split(",\\s"))
                .collect(
                      HashMap::new,
                      (map, ary) -> map.computeIfAbsent(Integer.parseInt(ary[0]),
                            k -> new ArrayList<>()).add(ary[1]),
                      (map1, map2) -> map1.entrySet().addAll(map2.entrySet())
                );
    }

    public Map<Integer,List<String>> getFruitMappingsWithExternalMap(){
        // Create the map external from the lambda and add to it.
        final Map<Integer,List<String>> fruitMappings = new HashMap<>();
        getContent().stream().map(s -> s.split(",\\s"))
              .forEach(ary ->
                    fruitMappings.computeIfAbsent(Integer.parseInt(ary[0]),
                          k -> new ArrayList<>()).add(ary[1]));
        return fruitMappings;
    }

    public void dumpMap(Map<Integer,List<String>> map){
        map.entrySet().forEach(e -> System.out.println(e.getKey() + " -> " + e.getValue()));
    }

    public List<String> getContent(){
        return Arrays.asList("1, Apple",
              "1, Pear",
              "1, Orange",
              "2, Apple",
              "3, Melon",
              "3, Orange",
              "1, Mango",
              "3, Star Fruit",
              "4, Pineapple",
              "2, Pomegranate");
    }
}

和输出

...使用自定义收集器...
1 -> [苹果、梨、橙、芒果]
2 -> [苹果、石榴]
3 -> [甜瓜、橙子、杨桃]
4 -> [菠萝]
...使用“外部”地图...
1 -> [苹果、梨、橙、芒果]
2 -> [苹果、石榴]
3 -> [甜瓜、橙子、杨桃]
4 -> [菠萝]

我很确定有人可以做得更好。

getContent只是一种使用您提供的文本获取值的简单方法。如果您实际上正在Files.readAllLines阅读. getContent()File

于 2015-08-04T17:53:42.520 回答