Java8从映射中匹配的值中提取第一个键

Java8从映射中匹配的值中提取第一个键,java,lambda,java-8,java-stream,optional,Java,Lambda,Java 8,Java Stream,Optional,假设我有一个给定名称、姓氏对的映射,我想找到该映射中第一个条目的给定名称,该条目的姓氏与某个值匹配。 我们将如何以Java8的方式实现这一点 在下面的测试用例示例中,我提出了两种方法 但是,第一个(查找第一个姓氏为“驴子”的人的名字)将抛出java.util.NoSuchElementException:不存在值,因此不安全 第二个很好用,但它不仅很难阅读,而且有点不太实用 我只是想知道这里是否有人会建议我使用stream()或forEach()或两者都可以更简单更清晰地实现这一点 @Test

假设我有一个给定名称、姓氏对的映射,我想找到该映射中第一个条目的给定名称,该条目的姓氏与某个值匹配。 我们将如何以Java8的方式实现这一点

在下面的测试用例示例中,我提出了两种方法

但是,第一个(查找第一个姓氏为“驴子”的人的名字)将抛出java.util.NoSuchElementException:不存在值,因此不安全

第二个很好用,但它不仅很难阅读,而且有点不太实用

我只是想知道这里是否有人会建议我使用
stream()
forEach()
或两者都可以更简单更清晰地实现这一点

@Test
public void shouldBeAbleToReturnTheKeyOfTheFirstMatchingValue() throws Exception {
    Map<String, String> names = new LinkedHashMap<>();
    names.put("John", "Doe");
    names.put("Fred", "Flintstone");
    names.put("Jane", "Doe");
    String keyOfTheFirst = names.entrySet().stream().filter(e -> e.getValue().equals("Doe")).findFirst().get().getKey();
    assertEquals("John", keyOfTheFirst);

    try {
        names.entrySet().stream().filter(e -> e.getValue().equals("Donkey")).findFirst().get();
    } catch (NoSuchElementException e){
        // Expected
    }

    Optional<Map.Entry<String, String>> optionalEntry = names.entrySet().stream().filter(e -> e.getValue().equals("Donkey")).findFirst();
    keyOfTheFirst = optionalEntry.isPresent() ? optionalEntry.get().getKey() : null;

    assertNull(keyOfTheFirst);
}
@测试
public void应该能够返回FirstMatching Value()的键引发异常{
映射名称=新LinkedHashMap();
姓名。填写(“约翰”、“多伊”);
姓名。填写(“弗雷德”、“弗林斯通”);
姓名。填写(“Jane”、“Doe”);
字符串keyOfFirst=names.entrySet().stream().filter(e->e.getValue().equals(“Doe”).findFirst().get().getKey();
assertEquals(“约翰”,第一位的钥匙);
试一试{
name.entrySet().stream().filter(e->e.getValue().equals(“驴”)).findFirst().get();
}捕获(无接触元素例外e){
//期望
}
可选optionalEntry=names.entrySet().stream().filter(e->e.getValue().equals(“驴”)).findFirst();
KeyOfFirst=optionalEntry.isPresent()?optionalEntry.get().getKey():null;
assertNull(KeyofFirst);
}
提前感谢您。

来自类似网站:

我喜欢老式的:

static <K, V> K findFirstKeyByValue(Map<K, V> map, String value) {
    for (Entry<K, V> e : map.entrySet())
        if (e.getValue().equals(value))
            return e.getKey();
    return null;
}
静态K findFirstKeyByValue(映射映射,字符串值){
对于(条目e:map.entrySet())
如果(如getValue().equals(value))
返回e.getKey();
返回null;
}

要在不匹配时返回默认值,请使用


如果您不想使用第三方代码,@Misha提供的解决方案是最好的。对于我发现这是一项非常常见的任务的情况,具有特殊的快捷方式:

StreamEx.ofKeys(names, "Donkey"::equals).findFirst().orElse(null);

下面是我从映射中获取密钥的代码片段

Map<String,String> pageDetails = new HashMap<String,String>();

public String getAssociatedKey(){
pageDetails.entrySet().stream().filter( e -> e.getValue().contains("John").findFirst().get().getKey();
}
Map pageDetails=newhashmap();
公共字符串getAssociatedKey(){
pageDetails.entrySet().stream().filter(e->e.getValue().contains(“John”).findFirst().get().getKey();
}

为了避免映射条目存在空值时出现空指针异常:

private String getValueByKey(Map<String, String> map, String key)
{
        return map.entrySet().stream().filter(e -> 
        StringUtils.equalsIgnoreCase(e.getKey(), key)).findFirst().get()
                .getValue();
}
私有字符串getValueByKey(映射映射,字符串键)
{
返回map.entrySet().stream().filter(e->
StringUtils.equalsIgnoreCase(e.getKey(),key)).findFirst().get()
.getValue();
}

而不是
collect
您可以使用
findAny
来获取找到的第一个。@PeterLawrey提醒得很好!但它将是
findFirst
,不是吗?除非您使用并行流,否则它将是第一个。但这与我的第二个解决方案类似。您仍然需要迭代到收集的集或检查选项是否正确findFirst返回的所有值都存在,以便获得实际的密钥。我正在寻找lambda中发生的事情,如果有,它将返回我想要的密钥,否则将返回空值。@Julian,恐怕您不能再进一步了。您不能保证始终能找到密钥。显然,使用双向映射将更有效。您好patryk.我完全同意你的看法。这就是我发布这个问题的原因。你能提供一个解决方案吗?对我来说,一些气味来自于使用
null
。发布的答案都提供了
null
,因为这是你要求的。但是既然你买了
可选的
,为什么要使用
null
keyoffirst
be
Optional
和do
assertTrue(keyoffirst.isEmpty())
或任何东西。@Stuart.当然,使用
java.util.Optional
是自然的方式,但这不是我想要的。我真正想要的是一个纯函数式的解决方案,而不需要任何额外的检查/迭代等。这更像是一个理论问题“java 8能做到吗?”因为我自己找不到答案,所以我把它贴在这里,而不是得出结论说找不到。单元测试只是为了帮助海报轻松地运用他们的解决方案,而不是别的。@Julian Fair。我对上面的评论做出了回应,“这段代码很臭。设计很差。”这个评论可能是真的,但并不是特别有用。虽然它与您所关注的问题没有直接关系,但我确实意识到代码的一个问题是它使用了
null
,因此我建议了一个避免
null
。谢谢Misha和Doon。您的两个回答都帮助我找到了我想要的解决方案。我我选择Misha作为最接近它的。要返回
java.lang.String
而不是
java.util.Optional
我所要做的就是调用
orElseGet(new Supplier(){@Override public String get(){return null;})
如果确实希望使用
orElseGet
而不是
orElse
,则可以使用lambda表达式而不是匿名类:
.orElseGet(()->null)
。谢谢Misha。这更好,也正是我想要的:这个问题的纯函数解决方案。Java 8 rocks!使用,它将是
PairStream.from(names).filterValue(“驴子”::equals).keys().findFirst()
。在某些情况下,
.orElse(null)
可以更改为
.orElseGet(()->Iterables.getLast(yourCollection))
这不会过滤名为驴的键,而不是对应名为驴的值的键吗?@flup,不会,这是对对应名为
“驴”
的值的键的过滤。要过滤键,可以使用常用的过滤方法,如
StreamEx.ofKeys(names).filter(“驴”::equals)
StreamEx.ofKeys(names, "Donkey"::equals).findFirst().orElse(null);
Map<String,String> pageDetails = new HashMap<String,String>();

public String getAssociatedKey(){
pageDetails.entrySet().stream().filter( e -> e.getValue().contains("John").findFirst().get().getKey();
}
private String getValueByKey(Map<String, String> map, String key)
{
        return map.entrySet().stream().filter(e -> 
        StringUtils.equalsIgnoreCase(e.getKey(), key)).findFirst().get()
                .getValue();
}