Java 8集合地图从集合中删除元素,如果为空,则删除条目

时间:2018-12-18 11:31:08

标签: java dictionary collections java-8 hashmap

我有一张地图,其值是一个集合。给定一个键,我想删除集合的一个元素并返回它,但是如果集合为空,我也想删除该条目。使用Java 8的众多新Map方法之一,有没有一种简短的方法?

一个简单的例子(我使用一个Stack,但是它可以是一个List,一个Set等)。为了便于说明,我们假设已经检查了地图是否包含键。

public static String removeOne(Map<Integer, Stack<String>> map, int key) {
    Stack<String> stack = map.get(key);
    String result = stack.pop();
    if(stack.isEmpty()){
        map.remove(key);
    }
    return result;
}

我尝试做类似的事情

map.compute(1, (k, v) -> {v.pop(); return v.size() == 0 ? null : v;});

但是,即使确实为空,它也确实删除了该条目,但我不知道如何获取pop()返回的值。

6 个答案:

答案 0 :(得分:5)

嗯,它比您已经使用的要难看,但我想有一种方法:

public static String removeOne(Map<Integer, Stack<String>> map, int key) {
    String[] removed = new String[1];
    map.compute(key, (k, v) -> {
        removed[0] = v.pop();
        return v.size() == 0 ? null : v;
    });
    return removed[0];
}

问题是merge/compute等返回 value ,在您的情况下,它是Stack/Set/List,而不是该集合中的单个元素。

答案 1 :(得分:4)

或者您也可以使用size将其重写为:

public static String removeOne(Map<Integer, Stack<String>> map, int key) {
    return map.get(key).size() == 1 ? map.remove(key).pop() : map.get(key).pop();
}

答案 2 :(得分:2)

  

有没有一种方法可以使用众多新方法之一在短时间内完成此操作   Java 8的Map方法?

从JDK8开始,没有新方法可以提高代码的可读性或效率。

如果您这样做是为了自己,那么我可以在某种程度上理解为什么您想缩短代码(如果可能),但是涉及生产代码打高尔夫球应避免,而应采用最易读和可维护的方法;再长也没关系。

您的方法很好。

答案 3 :(得分:2)

Guava's Multimap为您处理remove-collection-if-empty逻辑。您可以在两行中获得与您的方法相同的行为:

public static String removeOne(ListMultimap<Integer, String> map, int key) {
    List<String> stack = map.get(key);
    return stack.remove(stack.size() - 1);
}

如果映射没有给定键的条目,则现有解决方案和上述方法均会引发Exception。您可以选择更改代码来处理此问题:

public static String removeOne(ListMultimap<Integer, String> map, int key) {
    List<String> stack = map.get(key);
    if (stack.isEmpty()) {
        return null;
    }
    return stack.remove(stack.size() - 1);
}

当然可以使它通用:

public static <K, V> V removeOne(ListMultimap<K, V> map, K key) {
    List<V> stack = map.get(key);
    if (stack.isEmpty()) {
        return null;
    }
    return stack.remove(stack.size() - 1);
}

答案 4 :(得分:1)

我完全同意@NicholasK。没有理由在这里使用任何流或lambda。

您的方法非常好。我唯一要添加的就是使其通用:

public static <K, E, C extends Collection<E>> E removeOne(Map<K, C> map, K key) {
    C col = map.get(key);
    Iterator<E> it = col.iterator();
    E e = it.next();
    it.remove();
    if (!it.hasNext()) {
        map.remove(key);
    }
    return e;
}

此方法将适用于任何返回有效迭代器的集合(映射值)。

答案 5 :(得分:-1)

/* quite ugly
String rv = Optional.ofNullable(map.get(1)).map(stack -> {
            if (!stack.isEmpty()) {
                String v = stack.pop();
                if (stack.isEmpty()) {
                    map.remove(1);
                }
                return v;
            }
            return null;
        }).orElse(null);
*/ 

@Test
public void test() {
    {
        Map<Integer, Stack<String>> map = new HashMap<>();
        Stack<String> s = new Stack<String>();
        s.addAll(Arrays.asList("a", "b"));
        map.put(1, s);
        String rv = Optional.ofNullable(map.get(1)).map(stack -> {
            if (!stack.isEmpty()) {
                String v = stack.pop();
                if (stack.isEmpty()) {
                    map.remove(1);
                }
                return v;
            }
            return null;
        }).orElse(null);
        Assert.assertEquals("b", rv);
        Assert.assertEquals(1, map.get(1).size());
        Assert.assertEquals("a", map.get(1).iterator().next());
    }
    {
        Map<Integer, Stack<String>> map = new HashMap<>();
        Stack<String> s = new Stack<String>();
        s.add("a");
        map.put(1, s);
        String rv = Optional.ofNullable(map.get(1)).map(stack -> {
            if (!stack.isEmpty()) {
                String v = stack.pop();
                if (stack.isEmpty()) {
                    map.remove(1);
                }
                return v;
            }
            return null;
        }).orElse(null);
        Assert.assertEquals("a", rv);
        Assert.assertNull(map.get(1));
    }
}