Java 8 Map of Collections удалить элемент из коллекции и удалить запись, если она пуста - PullRequest
0 голосов
/ 18 декабря 2018

У меня есть карта, для которой значения являются коллекцией.Учитывая ключ, я хочу удалить элемент коллекции и вернуть его, но я также хочу удалить запись, если коллекция пуста.Есть ли способ сделать это коротким путем, используя один из многочисленных новых методов Map Java 8?

Один простой пример (я использую стек, но это может быть список, набор и т. Д.),Для примера давайте предположим, что уже проверено, что карта содержит ключ.

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 голосов
/ 18 декабря 2018

Или вы можете переписать его, используя 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();
}
0 голосов
/ 18 декабря 2018

Мультикарта Guava обрабатывает логику удаления-сбора-пустых для вас.Вы можете получить поведение, эквивалентное вашему методу, в двух строках:

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

Как ваше существующее решение, так и вышеприведенные исключения, если на карте нет записей для данного ключа.При желании вы можете изменить код для обработки этого:

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);
}
0 голосов
/ 18 декабря 2018

Я полностью согласен с @NicholasK.Здесь нет причин использовать какие-либо потоки или лямбды.

Ваш подход довольно хорош.Единственное, что я хотел бы добавить, это сделать его универсальным:

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

Этот метод будет применим ко всем коллекциям (значениям карты), возвращающим действительный итератор.

0 голосов
/ 18 декабря 2018

Есть ли способ сделать это кратко, используя один из многочисленных новых методов Map Java 8?

Начиная с JDK8 нет нового метода, который улучшил бы ваш кодбудь то с точки зрения читабельности или эффективности.

если вы делаете это как упражнение для себя, тогда я могу в некоторой степени понять, почему вы хотите сократить код (, если возможно ), но когда дело доходит до производстваследует избегать игры в коде и использовать подход, который является наиболее читабельным и обслуживаемым;не важно, длиннее ли это.

Ваш подход хорош как есть.

0 голосов
/ 18 декабря 2018

Ну, это даже хуже, чем то, что у вас уже есть, но, я думаю, есть способ:

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 и т.п. возвращают значение , а в вашем случае это Stack/Set/List, а не отдельный элемент из этой коллекции.

0 голосов
/ 18 декабря 2018
/* 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));
    }
}
...