【问题标题】:How to nicely intersect two sets build from two maps?如何很好地相交从两张地图构建的两组?
【发布时间】:2019-08-29 12:19:19
【问题描述】:

我们的对象有“属性”;并且它们的当前状态表示为Map<String, Object>,其中的键类似于属性的名称。这些值可以有不同的类型,但我目前的任务只是处理 Boolean 属性。

除了当前状态之外,对象的“更新”也通过此类地图进行组织。

现在我必须防止当前为true 的属性被禁用(转为false)。

使用流,这在这里有效:

Set<String> currentlyEnabled = currentObjectPropertiesMap.
            .entrySet()
            .stream()
            .filter(e -> Boolean.TRUE.equals(e.getValue()))
            .map(Entry::getKey)
            .collect(Collectors.toSet());

Set<String> goingDisabled = updatedObjectPropertiesMap
        .entrySet()
        .stream()
        .filter(e -> Boolean.FALSE.equals(e.getValue()))
        .map(Entry::getKey)
        .collect(Collectors.toSet());

currentlyEnabled.retainAll(goingDisabled);

if (currentlyEnabled.isEmpty()) {
    return;
} else {
  throw new SomeExceptionThatKnowsAllBadProperties(currentlyEnabled);
}

上面的代码首先获取一组true的所有属性,然后分别收集所有将变为false的属性。而如果这两个集合的交集是空的,我很好,否则报错。

上述方法可行,但我觉得它很笨拙,而且我不喜欢 currentlyEnabled 集合被滥用来计算交集这一事实。

有什么建议可以以更惯用但可读的“流式”方式完成吗?

【问题讨论】:

  • 为什么不在currentObjectPropertiesMap.entrySet()、continue 上迭代值为false 的条目,否则检查updatedObjectPropertiesMap.hasKey() 是否为真,如果该键的值为真,则抛出异常是false?

标签: java dictionary java-8 set java-stream


【解决方案1】:

试试anyMatch

boolean anyMatch = currentXXXMap.entrySet()
    .stream()
    .anyMatch(e -> e.getValue() && !updatedXXXMap.getOrDefault(e.getKey(), true));

【讨论】:

    【解决方案2】:

    不包含显式集合交集的一种解决方案可能是:

    Set<String> violatingProperties = new HashSet<String>();
    for (Entry<String, Object> entry : currentObjectPropertiesMap.entrySet()) {
        if (! (Boolean) entry.getValue()) {
            continue;
        }
        if (! updatedObjectPropertiesMap.hasKey(entry.getKey())) {
            continue;
        }
        if (! (Boolean) updatedObjectPropertiesMap.get(entry.getKey())) {
            violatingProperties.add(entry.getKey());
        }
    }
    if (violatingProperties.size() > 0) {
        throw ...
    }
    

    【讨论】:

    • 问题是我 A) 更喜欢流解决方案 B) 我需要“收集”所有违反我条件的属性名称。
    • 更新了代码以保存违规的属性名称
    【解决方案3】:

    您可以只选择值为true的所有键值对,然后通过键检查“更新”映射中的值是否为false。

    Set<String> matches = currentObjectPropertiesMap
        .entrySet()
        .stream()
        .filter(e -> Boolean.TRUE.equals(e.getValue()))
        .map(Map.Entry::getKey)
        .filter(k -> Boolean.FALSE.equals(
            updatedObjectPropertiesMap.get(k)
        ))
        .collect(Collectors.toSet());
    
    if(!matches.isEmpty()) throw ...
    

    【讨论】:

    • 可以将两个filters合二为一,然后map。虽然差别不大.filter(e -&gt; Boolean.TRUE.equals(e.getValue()) &amp;&amp; Boolean.FALSE.equals(updatedObjectPropertiesMap.get(e.getKey()))) .map(Map.Entry::getKey)
    • @Naman 我同意,但是 IMO,流语句应该尽可能简单。我的经验法则是每个filter/map-操作的一个条件/语句(如果可能),但正如所说,这只是一种意见
    • 为了提高效率,您可以检查哪个地图更小并迭代更小的地图,即如果updatedObjectPropertiesMap更小,请改用Set&lt;String&gt; matches = updatedObjectPropertiesMap .entrySet() .stream() .filter(e -&gt; Boolean.FALSE.equals(e.getValue())) .map(Map.Entry::getKey) .filter(k -&gt; Boolean.TRUE.equals(currentObjectPropertiesMap.get(k))) .collect(Collectors.toSet());;结果是一样的。
    猜你喜欢
    • 2019-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-08
    • 1970-01-01
    • 2011-03-24
    • 2014-01-09
    • 2011-02-10
    相关资源
    最近更新 更多