想象一下,我有一个像:
这样的文件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>>
或许有更好的方法可以做到这一点?
答案 0 :(得分: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,我会返回List<SimpleEntry<Integer, String>>
,因为每行代表一个键值对,并将map
调用更改为:
.map(snippets -> new SimpleEntry<>(Integer.parseInt(snippets.get(0)), snippets.get(1)))
...或将整个内容收集到Map<Integer, List<String>>
(查看groupingBy
收藏家)。
答案 1 :(得分: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>>
。
答案 2 :(得分: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());
}
答案 3 :(得分:1)
我想在这里使用地图是一种矫枉过正。您可以查看https://docs.oracle.com/javase/8/javafx/api/javafx/util/Pair.html
答案 4 :(得分:0)
添加我对原帖的评论,这是我提出的代码。显然,它假设你做希望有一对多的关系,整数映射到https://www.googleapis.com/youtube/v3/search?part=snippet&q=VIDEO_TITLE_HERE&type=playlist&key=API_KEY_HERE
。
List<String>
输出
... Using custom collector ... 1 -> [Apple, Pear, Orange, Mango] 2 -> [Apple, Pomegranate] 3 -> [Melon, Orange, Star Fruit] 4 -> [Pineapple] ... Using 'External' map ... 1 -> [Apple, Pear, Orange, Mango] 2 -> [Apple, Pomegranate] 3 -> [Melon, Orange, Star Fruit] 4 -> [Pineapple]
我确信有人可以做得更好。
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");
}
}
只是使用您提供的文字获取值的简单方法。如果您实际上正在阅读getContent
,那么使用Files.readAllLines
代替getContent()
同样容易。