通过映射列表中的键查找值

Find a value by the key in a List of Maps

我有一个 list of maps List<Map<String,String>> input.

可以用以下方式表示:

[{AddressField=AddressUsageType, AddressValue=PRINCIPAL},
 {AddressField=StreetNumber, AddressValue=2020},
 {AddressField=StreetName, AddressValue=Some street}]

我想获得特定 AddressFieldAddressValue

例如,我想为 key "AddressUsageType" 获取 value "PRINCIPAL"

我已经尝试使用 过滤器 和许多其他 MAP 函数,但最终无法找到合适的解决方案。

这是我获取第一个键值对值的代码片段:

DataTable table;
List<Map<String,String>> input= table.asMaps(String.class, String.class);

    String AddressField = input.get(0).get("AddressField");
    String AddressValue = input.get(0).get("AddressValue");
    System.out.println("AddressField " +AddressField);
    System.out.println("AddressValue " +AddressValue);

以上代码段的输出如下:

AddressField AddressUsageType
AddressValue PRINCIPAL

您对地图的使用在这里有点奇怪,因为您的实际键“AddressUsageType”是地图内部的一个值,而每个地图只是存储在静态键后面的一对键和值。如果你不能改变它,你可以用这样的东西:

String key = "AddressUsageType";
String result = "";

for (Map<String,String> map : input)
{
    if (map.containsValue(key))
    {
        result = map.get("AddressValue");
        break;
    }
}

System.out.println(key + " is " + result);

我想这就是您要找的:

Map<String, String> map = data.stream()
        .filter(m -> m.values().contains("AddressUsageType"))
        .findFirst()
        .orElse(null);

if (map != null) {
    System.out.println("AddressField " + map.get("AddressField"));
    System.out.println("AddressValue " + map.get("AddressValue"));
}

这里还有一个测试主

public class Test {
    public static void main(String[] args) {
        List<Map<String, String>> data = new ArrayList<>();

        Map<String, String> map1 = new HashMap<>();
        map1.put("AddressField", "AddressUsageType");
        map1.put("AddressValue", "PRINCIPAL");
        data.add(map1);

        Map<String, String> map2 = new HashMap<>();
        map2.put("AddressField", "StreetNumber");
        map2.put("AddressValue", "2020");
        data.add(map2);

        Map<String, String> map3 = new HashMap<>();
        map3.put("AddressField", "StreetName");
        map3.put("AddressValue", "Some street");
        data.add(map3);

        Map<String, String> map = data.stream()
                .filter(m -> m.values().contains("AddressUsageType"))
                .findFirst()
                .orElse(null);

        if (map != null) {
            System.out.println("AddressField " + map.get("AddressField"));
            System.out.println("AddressValue " + map.get("AddressValue"));
        }
    }
}

输出

过滤输入以仅保留包含 AddressField=AddressUsageType 的地图。然后使用 .map 函数提取 AddressValue 条目并将它们收集到结果列表中。

public static void main(String[] args) {
    List<Map<String, String>> input = new ArrayList<>();
    Map<String, String> map = new HashMap<>();
    map.put("AddressField", "AddressUsageType");
    map.put("AddressValue", "PRINCIPAL");
    input.add(map);

    map = new HashMap<>();
    map.put("AddressField", "StreetNumber");
    map.put("AddressValue", "2020");
    input.add(map);
    map = new HashMap<>();
    map.put("AddressField", "StreetName");
    map.put("AddressValue", "Some street");
    input.add(map);

    map = new HashMap<>();
    map.put("AddressField", "AddressUsageType");
    map.put("AddressValue", "NOT_PRINCIPAL");
    input.add(map);
    
    List<String> collect = input.stream().filter(e -> e.get("AddressField").equals("AddressUsageType"))
            .map(e -> e.get("AddressValue")).collect(Collectors.toList());
    System.out.println(input);
    System.out.println(collect);
}

输出为

[{AddressField=AddressUsageType, AddressValue=PRINCIPAL}, {AddressField=StreetNumber, AddressValue=2020}, {AddressField=StreetName, AddressValue=Some street}, {AddressField=AddressUsageType, AddressValue=NOT_PRINCIPAL}]

[PRINCIPAL, NOT_PRINCIPAL]

I would like to get the value of AddressValue from value of AddressField. For eg.,

I want to get the value "PRINCIPAL" from the key value "AddressUsageType"

您在这里误用了 Map。因为每个地图都应该有一组有限的 well-defined 键,每个键在您的域中都有特定的含义。每张地图都代表某种地址。

一旦这些数据在您的域模型中具有特定含义,我们强烈建议您将这些数据分组到一个自定义对象中。

优点是:

  • 能够对值使用不同的数据类型,而不是将它们存储为 String
  • 您不必依赖字符串键。
  • 您获得干净 self-explanatory 代码的优势(以防您使用清晰准确的名称)。

我知道它可能是很久以前由其他人开发的代码,或者您可能认为现在存储数据更容易。但是你越是推迟重构,它就变得越昂贵。

通过将对象视为一个集合,您可能会使用深度嵌套的集合,例如地图的地图或地图列表的地图,这会影响代码的可维护性。

如果您选择将数据分组到域 class。

,您将如何处理此任务

为了简洁起见,我会使用Java 16条记录。

public record UserAddress(AddressUsageType addressUsageType, int streetNumber, String streetName) {}

AddressUsageType将表示为enum。如果我理解正确的话,地址类型的数量是有限的,所以 error-prone 将此数据存储为 enum 而不是依赖字符串值会更少,并且也提供了一些额外的机会.

public enum AddressUsageType { PRINCIPAL, OTHER_TYPE }

这就是如何从列表中的每个 UserAddress 对象中提取特定字段,方法是能够从其类型中获益,而不是将它们视为字符串:

public static <T> List<T> getAddressValue(List<UserAddress> addresses,
                                          Function<UserAddress, T> keyExtractor) {
    return addresses.stream()
        .map(keyExtractor)
        .toList(); // available with Java 16 onwards
}

上面显示的方法需要一个 list 地址和一个 function 从地址对象中提取特定的 属性 .

main() - 演示

public static void main(String[] args) {
    List<UserAddress> addresses =
        List.of(new UserAddress(AddressUsageType.PRINCIPAL, 71, "greenStreet"),
                new UserAddress(AddressUsageType.PRINCIPAL, 83, "purpleStreet"),
                new UserAddress(AddressUsageType.OTHER_TYPE, 85, "pinkStreet"));
    
    List<AddressUsageType> addressUsageTypes = getAddressValue(addresses, UserAddress::addressUsageType);
    List<Integer> streetNumbers = getAddressValue(addresses, UserAddress::streetNumber);
    List<String> streetNames = getAddressValue(addresses, UserAddress::streetName);

    System.out.println("AddressUsageTypes:\n" + addressUsageTypes);
    System.out.println("StreetNumbers:\n" + streetNumbers);
    System.out.println("StreetNames:\n" + streetNames);
}

输出

AddressUsageTypes:
[PRINCIPAL, PRINCIPAL, OTHER_TYPE]
StreetNumbers:
[71, 83, 85]
StreetNames:
[greenStreet, purpleStreet, pinkStreet]