我试图使用Java创建一个地图的笛卡尔乘积,其中包含了 String
钥匙和 List<String>
的值。我需要在创建列表时保留键值,就像这样。
Map<String, List<String>>
对象List<Map<String, String>>
对象我还要求,当一个 List<String>
原始地图中的值是空的,在创建笛卡尔积时仍应将其作为一个单一的值来处理,以避免乘以0而不创建地图。例如下面的地图。
{
"Location Number" = {"100", "500"}
"Department Number" = {"11", "22", "33"}
"District Number" = {}
"Preferred Language" = {"en-US"}
}
被翻译成:
{
{
"Location Number" = "100"
"Department Number" = "11"
"District Number" = {}
"Preferred Language" = "en-US"
},
{
"Location Number" = "100"
"Department Number" = "22"
"District Number" = {}
"Preferred Language" = "en-US"
},
{
"Location Number" = "100"
"Department Number" = "33"
"District Number" = {}
"Preferred Language" = "en-US"
},
{
"Location Number" = "500"
"Department Number" = "11"
"District Number" = {}
"Preferred Language" = "en-US"
},
{
"Location Number" = "500"
"Department Number" = "22"
"District Number" = {}
"Preferred Language" = "en-US"
},
{
"Location Number" = "500"
"Department Number" = "33"
"District Number" = {}
"Preferred Language" = "en-US"
}
}
下面是我目前使用的代码来完成类似的翻译 但它并没有保留我所需要的键值 我不知道这是否可以使用Java 8 Streams在一般情况下完成。
private static List<List<String>> createRuleListFromMap(Map<String, List<String>> ruleMap) {
List<List<String>> ruleList = new ArrayList<>();
cartesianProduct(ruleMap.values()).forEach(ruleList::add);
return ruleList;
}
private static <T> Stream<List<T>> cartesianProduct(Collection<? extends Collection<T>> collections) {
return cartesianProduct(new ArrayList<Collection<T>>(collections), Collections.emptyList());
}
private static <T> Stream<List<T>> cartesianProduct(List<? extends Collection<T>> collections, List<T> current) {
return collections.isEmpty() ? Stream.of(current) : collections.get(0).stream().flatMap(e -> {
List<T> list = new ArrayList<>(current);
list.add(e);
return cartesianProduct(collections.subList(1, collections.size()), list);
});
}
下面是一个简单的例子,如何将Map>转换为List>。
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Slf4j
public class StreamTest {
@Test
public void test() {
var map = new HashMap<String, List<String>>();
map.put("key1", Arrays.asList("value1", "value2", "value3"));
map.put("key2", Arrays.asList("value4", "value5", "value6"));
var list = map
.entrySet()
.stream()
.flatMap(e -> e.getValue().stream().map(v -> Map.of(e.getKey(), v)))
.collect(Collectors.toList());
log.info(list.toString());
}
}
不确定你是否可以用流的方式来做这个,因为它需要递归。
一个非流版本可以是
private static <T,T1> List<Map<T,T1>> createRuleListFromMap(Map<T, List<T1>> ruleMap) {
//Make iterator
Iterator<Map.Entry<T, List<T1>>> iterator = ruleMap.entrySet().iterator();
//Run initial generate list with empty arrayList
return generateList(new ArrayList<>(),iterator);
}
private static <T,T1> List<Map<T,T1>> generateList(List<Map<T,T1>> mapList,Iterator<Map.Entry<T, List<T1>>> iterator) {
//If there are any entries still in map
if(iterator.hasNext()){
//Get next entry
Map.Entry<T, List<T1>> entry = iterator.next();
//This is where this iterations maps will go
List<Map<T,T1>> newMapList = new ArrayList<>();
//Where comination of previous + this iterations maps
List<Map<T,T1>> combinedMapList = new ArrayList<>();
//For every value in current entry
//make into map and add to newMapList
for(T1 t1 : entry.getValue()){
Map<T,T1> newMap = new HashMap<>();
newMap.put(entry.getKey(),t1);
newMapList.add(newMap);
}
//if map list is still empty, due to previous loop not running,
//create single null entry
if(newMapList.isEmpty()){
Map<T,T1> newMap = new HashMap<>();
newMap.put(entry.getKey(),null);
newMapList.add(newMap);
}
//Combine previous map with current
for (Map<T,T1> newMap : newMapList){
for(Map<T,T1> map : mapList){
Map<T,T1> combinedMap = new HashMap<>();
combinedMap.putAll(map);
combinedMap.putAll(newMap);
combinedMapList.add(combinedMap);
}
}
//Return combinedMap if not empty
//otherwise return this iterations map
//Only required for first iteration since above
//loop won't run whilst previous mapList is empty
return generateList(combinedMapList.isEmpty() ? newMapList : combinedMapList,iterator);
}
//When no more elements return the mapList
return mapList;
}
也许可以重构,但应该能提供一个基本的概念。