【问题标题】:How to remove items from a java list - by a set of positions?如何从 java 列表中删除项目 - 通过一组位置?
【发布时间】:2015-04-06 08:51:36
【问题描述】:

给定一组位置Set<Integer> positionsToRemove,我想从列表List<?> list 中删除所有位置与该组中的值相对应的项目。

public static void remove(List<?> list, Set<Integer> positionsToRemove) {
  // ?
}

注意事项:

  • 列表可能很长。
  • 集合可能很长。
  • 小心不要陷入 IndexOutOfBounds 或 ConcurrentModificationException。
  • 您可以假设列表和集合不包含空值。

【问题讨论】:

  • 您真的要修改原始列表,还是可以返回新列表?
  • @LukasEder - 修改原始列表。
  • 如果您希望生成的列表很短,构建新列表可能会更快,具体取决于原始列表中remove() 操作的复杂性。

标签: java list set


【解决方案1】:

您必须确保删除项目反向,否则索引将失效。

您可以使用List 轻松完成此操作:

public static void remove(List<?> list, Set<Integer> positionsToRemove) {
    final ListIterator<?> iter = list.listIterator(list.size());
    while (iter.hasPrevious()) {
        iter.previous();
        if (positionsToRemove.contains(iter.nextIndex())) {
            iter.remove();
        }
    }
}

这利用了 if ListIterator 提供当前索引的能力以及反向迭代的能力。

它也不依赖于“标记值”,因此在输入 List 中允许使用 null

显然,您可以使用“旧式”索引循环更简单地做到这一点:

public static void remove(List<?> list, Set<Integer> positionsToRemove) {
    for (int i = list.size() - 1; i >= 0; --i) {
        if (positionsToRemove.contains(i)) {
            list.remove(i);
        }
    }
}

但应该注意的是,如果List 缺少RandomAccess,这将是O(n^2),而基于ListIterator 的解决方案将是O(n),因为List 具有O(1) remove .

返回new List 可能会更快,具体取决于您要删除的元素的数量以及List 的类型 - 对于ArrayListremove 操作非常昂贵:

public static <T> List<T> remove(List<T> list, Set<Integer> positionsToRemove) {
    return IntStream.range(0, list.size())
            .filter(i -> !positionsToRemove.contains(i))
            .mapToObj(list::get)
            .collect(toList());
}

【讨论】:

    【解决方案2】:
    public static void remove(List<?> list, Set<Integer> positionsToRemove) {
        for (Integer position : positionsToRemove) {
            list.set((int)position, null);
        }
        for (Iterator<?> iterator = list.iterator(); iterator.hasNext(); ) {
            Object o =  iterator.next();
            if (o == null) iterator.remove();
        }
    }
    

    【讨论】:

    • 这将删除其他 null 值。
    • @BoristheSpider - 请参阅问题中的注释。不允许使用空值。
    • 如果nulls 被允许,您也可以使用Object marker = new Object(); 并将其用作删除标记​​span>
    • 这个(我的)解决方案是有问题的,因为它假定对列表项的访问是即时的,如果它是 LinkedList,则不是这样。
    【解决方案3】:
    public static void remove(List<?> list, Set<Integer> positionsToRemove) {
        int i = 0;
        for (Iterator<?> iterator = list.iterator(); iterator.hasNext(); ++i) {
            iterator.next();
            if (positionsToRemove.contains(i)) iterator.remove();
        }
    }
    

    【讨论】:

    • 这个解决方案看起来比其他所有解决方案都简单。
    • 废话!没注意到这是我的回答。
    猜你喜欢
    • 1970-01-01
    • 2017-02-11
    • 1970-01-01
    • 2015-04-02
    • 2011-06-21
    • 2019-07-06
    • 1970-01-01
    • 2014-12-21
    • 2021-03-12
    相关资源
    最近更新 更多