【问题标题】:How to perform filtering by Key on KeyValue objects using Lambda-Expressions?如何使用 Lambda-Expressions 对 KeyValue 对象执行 Key 过滤?
【发布时间】:2014-10-22 15:42:19
【问题描述】:

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

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

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

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

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

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

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 流式 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());
}

我正在考虑实现我自己的自定义收集器。 由于我的收集器应该只返回列表,当收集的结果包含每个给定键至少一次时。

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


这个解决方案通过了我的测试。 但感觉就像本末倒置。

不再简洁、简洁、优雅。

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>();
                    }
                }
            }));
}

【问题讨论】:

  • 我认为您的代码还可以,那么为什么要使用任何 Java8 功能呢?这不会让你的代码更容易
  • 好的...在否决票之后。我只能猜测我做错了什么。没有提供我尝试过的示例。这看起来像do my homework-question。我会解决我的问题
  • 听起来你的老师告诉你写Java8的程序来学习Lambda,却没有想到Java7标准更适合你的代码
  • 如果你使用来自 Java8 的工具,如 forEach 方法,我只会看到多线程错误出现
  • "为了教育和娱乐(或许还有更好的扩展性),我想知道如何使用新的 Java 8 流式 API 将这段代码转换为解决方案 " 所以尝试用 lambdas 来实现。是什么阻止了你?我看不出你有什么问题(我假设你没有要求我们重写你的只是这样你就会看到它是如何完成的在你尝试之前,IMO 会关闭-关于堆栈溢出的主题)。

标签: java lambda java-8


【解决方案1】:

首先我必须说,我对 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());
    }

}

【讨论】:

  • 感谢您的意见。确实,您的解决方案看起来结构良好。很容易看到发生了什么(尤其是 getAttributeEntities 中的业务逻辑如何“流动”)
  • IMO 你的不是“功能齐全”,我试图用我的不同方法来做。但是,嘿,我并没有特别要求。
  • 您只需在可能对这个问题有意义的地方使用函数式语言元素 :) 我认为这就是将 Java 与函数式语言元素一起使用时的全部意义所在。 :)
【解决方案2】:

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

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 重写它,但我认为它会不太清楚。

【讨论】:

    【解决方案3】:

    我想出了一个办法。

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

    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<>();
    }
    

    非常感谢任何提示或 cmets。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-21
      • 1970-01-01
      • 1970-01-01
      • 2020-09-13
      • 1970-01-01
      • 2016-03-23
      相关资源
      最近更新 更多