如何使用Lambda-Expressions通过Key对KeyValue对象进行过滤?

时间:2014-10-22 15:42:20

标签: java lambda java-8

鉴于我想过滤键值对象列表。

我的(文档) - 下面示例中的对象看起来像这样

{
    "attributeEntityList" : [
        {key: 'key1', value: 'somevalue1'},
        {key: 'key2', value: 'somevalue2'},
        {key: 'key3', value: 'somevalue3'}
    ]
}
  1. 当我传入以下键["key1", "key2", "key3"]的列表时,我希望我的函数返回整个给定的属性列表。

  2. 当我传入以下键["key1", "key2"]的列表时,我希望我的函数返回一个具有给定键名的属性列表。

  3. 当我传入以下键["key1", "key2", "faultyKey"]的列表时,我希望我的函数返回一个空列表。

  4. 我的命令式解决方案看起来像这样,它可以正常工作:

    private List<AttributeEntity> getAttributeEntities(List<String> keys, Document value) {
        final List<AttributeEntity> documentAttributeList = value.getAttributeEntityList();
        final List<AttributeEntity> resultList = new ArrayList<>();
    
        for(String configKey: keys){
            boolean keyInAttribute = false;
            for(AttributeEntity documentAttribute : documentAttributeList){
                if(configKey.equals(documentAttribute.getAttribute_key())){
                    keyInAttribute = true;
                    resultList.add(documentAttribute);
                    break;
                }
            }
            if(!keyInAttribute){
                resultList.clear();
                break;
            }
        }
    
        return resultList;
    }
    

    对于教育和娱乐(也许更好的扩展),我想知道如何使用新的Java 8 streaming-api将这段代码转换为解决方案。


    这就是我想到的,将我的Java8前代码转换为Java8。

    在我看来,它看起来更简洁,而且更短。但它没有,我期望它做什么:/

    我真的很难实现我的要求的第三个要点。 它总是返回所有(找到的)属性,即使我传入一个不存在的键。

    private List<AttributeEntity> getAttributeEntities(List<String> keys, Document value) {
        final List<AttributeEntity> documentAttributeList = value.getAttributeList();
    
        return documentAttributeList.stream()
                .filter(attribute ->
                        keys.contains(attribute.getAttribute_key())
                ).collect(Collectors.toList());
    }
    

    我正在考虑实现自己的自定义收集器。 由于我的收集器只应返回List,因为收集的结果至少包含一次给定的键。

    关于如何实现这一目标的任何其他想法?


    此解决方案通过了我的测试。 但感觉就像我把车推到了马前。

    它既不简洁也不简短或优雅。

    private List<AttributeEntity> getAttributeEntities(List<String> keys, Document value) {
        final List<AttributeEntity> documentAttributeList = value.getAttributeList();
    
        return documentAttributeList.stream()
                .filter(attribute ->
                                keys.contains(attribute.getAttribute_key())
                )
                .collect(Collectors.collectingAndThen(Collectors.toList(), new Function<List<AttributeEntity>, List<AttributeEntity>>() {
                    @Override
                    public List<AttributeEntity> apply(List<AttributeEntity> o) {
                        System.out.println("in finisher code");
                        if (keys.stream().allMatch(key -> {
                            return o.stream().filter(attrbiute -> attrbiute.getAttribute_key().equals(key)).findAny().isPresent();
                        })) {
                            return o;
                        } else {
                            return new ArrayList<AttributeEntity>();
                        }
                    }
                }));
    }
    

3 个答案:

答案 0 :(得分:3)

首先,我必须说我也是Java 8新功能的新手,因此我对所有内容都不熟悉,并且不习惯函数式编程。我尝试了一种不同的方法,将其全部划分为一些方法。

这是:

public class Main {

    private static List<AttributeEntity> documentAttributeList;

    static {
        documentAttributeList = new ArrayList<>();
        documentAttributeList.add(new AttributeEntity("key1", "value1"));
        documentAttributeList.add(new AttributeEntity("key2", "value2"));
        documentAttributeList.add(new AttributeEntity("key3", "value3"));
    }

    public static void main(String[] args) {
        Main main = new Main();
        List<AttributeEntity> attributeEntities = main.getAttributeEntities(Arrays.asList("key1", "key2"));
        for (AttributeEntity attributeEntity : attributeEntities) {
            System.out.println(attributeEntity.getKey());
        }
    }

    private List<AttributeEntity> getAttributeEntities(List<String> keys) {
        if(hasInvalidKey(keys)){
            return new ArrayList<>();
        } else {
            return documentAttributeList.stream().filter(attribute -> keys.contains(attribute.getKey())).collect(toList());
        }
    }

    private boolean hasInvalidKey(List<String> keys) {
        List<String> attributeKeys = getAttributeKeys();
        return keys.stream().anyMatch(key -> !attributeKeys.contains(key));
    }

    private List<String> getAttributeKeys() {
        return documentAttributeList.stream().map(attribute -> attribute.getKey()).collect(toList());
    }

}

答案 1 :(得分:1)

如果一个文档永远不能有多个具有相同名称的属性,我认为你可以这样做(没有编译器方便尝试):

Map<String, AttributeEntity> filteredMap=value.getAttributeEntityList().stream()
    .filter(at->keys.contains(at.getKey()))
    .collect(toMap(at->at.getKey(), at->at));

return filteredMap.keySet().containsAll(keys) 
    ? new ArrayList<>(filteredMap.values()) 
    : new ArrayList<>();

如果允许每个名称包含多个属性,则必须使用groupingBy而不是toMap。当然,你可以用collectAndThen重写它,但我认为它不太清楚。

答案 2 :(得分:0)

我想出了一些东西。

我不知道它是否是最优雅的解决方案,但至少它是有效的,我可以推理它。

private List<AttributeEntity> getAttributeEntities(List<String> keys, Document value) {
    final List<AttributeEntity> documentAttributeList = value.getAttributeList();

    boolean allKeysPresentInAnyAttribute = keys.stream()
            .allMatch(key ->
                    documentAttributeList.stream()
                            .filter(attrbiute ->
                                    attrbiute.getAttribute_key().equals(key)
                            )
                            .findAny()
                            .isPresent()
            );
    if (allKeysPresentInAnyAttribute) {
        return documentAttributeList.stream()
                .filter(attribute ->
                    keys.contains(attribute.getAttribute_key())
                )
                .collect(Collectors.toList());
    }
    return new ArrayList<>();
}

任何提示或评论都非常感谢。