简体   繁体   English

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

[英]Java 8 Map of Collections remove element from collection and remove entry if empty

I have a map for which the values are a collection. 我有一个地图,其值是一个集合。 Given a key, I want to remove an element of the collection and return it, but I also want to remove the entry if the collection is empty. 给定一个键,我想删除该集合的一个元素并将其返回,但如果该集合为空,我还想删除该条目。 Is there a way to do this in a short way using one of the numerous new Map methods of Java 8? 有没有办法使用Java 8的众多新Map方法之一以简短的方式完成此操作?

One easy example (I use a Stack but it could be a List, a Set, etc.). 一个简单的例子(我使用Stack但它可以是List,Set等)。 For the sake of the example, let's say that it is already checked that the map contains the key. 为了示例,我们假设已经检查了地图包含密钥。

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

I tried doing something like 我试过做类似的事情

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

But even though it does indeed remove the entry if empty, I don't know how to get the value returned by pop() . 但即使它确实删除了条目,如果为空,我不知道如何获取pop()返回的值。

Well, it's even uglier than what you have already in place, but there is a way, I guess: 嗯,它甚至比你现有的还要丑陋,但是有一种方法,我想:

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];
}

Problem is that merge/compute and the like return the value , and in your case that is a Stack/Set/List , not and individual element from that collection. 问题是merge/compute等返回 ,在您的情况下是Stack/Set/List ,而不是该集合中的单个元素。

Or you could possibly rewrite it using size as: 或者您可以使用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();
}

Is there a way to do this in a short way using one of the numerous new Map methods of Java 8? 有没有办法使用Java 8的众多新Map方法之一以简短的方式完成此操作?

There's no new method as of JDK8 that would improve your code whether that's in terms of readability or efficient. JDK8没有新方法可以改善您的代码,无论是在可读性还是高效方面。

if you're doing this as an exercise for your self then I can understand to some extent why you'd want to shorten the code ( if possible ) but when it comes to production code golfing should be avoided and instead go with the approach that is most readable and maintainable; 如果你这样做是为了锻炼你自己,那么我可以在某种程度上理解为什么你想要缩短代码( 如果可能的话 )但是当涉及到生产代码时应该避免打高尔夫球,而是采用那种方法最具可读性和可维护性; doesn't matter if it's longer. 没关系,如果它更长。

Your approach is good as is. 你的方法很好。

Guava's Multimap handles the remove-collection-if-empty logic for you. Guava的Multimap为您处理remove-collection-if-empty逻辑。 You can get equivalent behaviour to your method in two lines: 您可以在两行中获得与方法相同的行为:

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

Both your existing solution and the above throw Exceptions if the map has no entries for the given key. 如果映射没有给定键的条目,则现有解决方案和上述解决方案都会抛出异常。 Optionally you can change the code to handle this: 您可以选择更改代码来处理此问题:

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

And of course you can make this generic: 当然,你可以使这个通用:

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

I fully agree with @NicholasK. 我完全赞同@NicholasK。 There's no reason to use any streams or lambdas here. 这里没有理由使用任何流或lambdas。

Your approach is pretty good. 你的方法非常好。 The only thing I would like to add is making it generic: 我想要添加的唯一内容是将其设为通用:

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

This method will be applicable to any collections (map values) returning valid iterator. 此方法适用于返回有效迭代器的任何集合(映射值)。

/* 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));
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM