【问题标题】:Removing negative values from Set从 Set 中删除负值
【发布时间】:2020-02-22 22:15:14
【问题描述】:

我预计 remove() 方法会删除负值并仅打印正整数,但输出为 -1 -2 -3 4

 import java.util.HashSet;
    import java.util.Iterator;
    import java.util.Set;

    public class GenericSetTester {
        public static void main(String[] args) {

            Set<Integer> integerSet = new HashSet<>();

            integerSet.add(0);
            integerSet.add(1);
            integerSet.add(2);
            integerSet.add(3);
            integerSet.add(4);
            integerSet.add(-1);
            integerSet.add(-2);
            integerSet.add(-3);

            for(Iterator<Integer> in = integerSet.iterator(); in.hasNext();){
                if(in.next() < 0)
                    in.remove();
                   int i = in.next();
                System.out.printf("%d ",i);
            }
        }
    }

【问题讨论】:

    标签: java loops collections iterator set


    【解决方案1】:

    @Eran 答案纠正了您当前的方法。请注意,流提供了另一种方式来处理您的要求:

    Integer[] array = new Integer[] {0, 1, 2, 3, 4, -1, -2, -3};
    Set<Integer> integerSet = new HashSet<>(Arrays.asList(array));
    integerSet = integerSet.stream()
        .filter(i -> i >= 0)
        .collect(Collectors.toSet());
    

    【讨论】:

    • 其实这也是可行的。 integerSet.stream().filter(a -&gt; a &gt; 0).forEach(System.out::println); 对我来说,循环内部发生的事情很有趣。
    • 由于问题和循环示例是关于修改现有集合而不是创建新集合,因此最简单的解决方案是integerSet.removeIf(i -&gt; i &lt; 0);。我不知道为什么每个人都顽固地忽略了直截了当的 Collection 方法……
    • @Holger 您的个人资料确实令人印象深刻,而且您是我见过的仅有的几位用户之一,拥有多个最近得到大量支持的答案。
    【解决方案2】:

    此循环在每次迭代中消耗Set 的两个元素(因为您在每次迭代中调用in.next() 两次),这是错误的,并导致负元素未被删除。

    考虑您的Set 元素的迭代顺序:

    0 // consumed by if(in.next() < 0), which returns false, element not removed
    -1 // consumed by int i = in.next();, and later printed -1
    1 // consumed by if(in.next() < 0), which returns false, element not removed
    -2 // consumed by int i = in.next();, and later printed -2
    2 // consumed by if(in.next() < 0), which returns false, element not removed
    -3 // consumed by int i = in.next();, and later printed -3
    3 // consumed by if(in.next() < 0), which returns false, element not removed
    4 // consumed by int i = in.next();, and later printed 4
    

    如您所见,您的循环不会从 Set 中删除任何元素,并且只打印一半元素 (-1 -2 -3 4)。

    应该是:

    for (Iterator<Integer> in = integerSet.iterator(); in.hasNext();) {
        int i = in.next();
        if (i < 0)
            in.remove();
        else
            System.out.printf("%d ",i);
    }
    

    正如蒂姆建议的那样,如果您在循环之后打印Set,您将看到它是否只包含它应该包含的元素。您的循环将变为:

    for(Iterator<Integer> in = integerSet.iterator(); in.hasNext();){
        if(in.next() < 0)
            in.remove();
    }
    System.out.println (integerSet);
    

    【讨论】:

    • 实际上,我会在 OP 的 for 循环之后打印列表,以表明只剩下非负元素。
    • @TimBiegeleisen 这也可以工作,但会产生相同的输出(因为只删除了负元素并且只打印了非负元素)
    【解决方案3】:

    发生的情况如下: integerSet 按此顺序添加数字(但我不知道为什么): 0:0 1:-1 2:1 3:-2 4:2 5:-3 6:3 7:4

    这就是为什么如果你执行循环,它首先检查 0 的下一个是否小于 0。这将返回 true,因为 -1 小于 0。所以 0 将被删除。然后它检查 -1 的下一个是 1,小于 0。这将返回 false。所以 -1 不会被删除。

    这就是为什么输出是 -1 -2 -3 4

    【讨论】:

    • So 0 will be deleted - no 0 没有被删除,它只是没有被打印出来。实际上,在 OP 的代码中,没有为给定的输入删除任何内容。如果您在循环完成后打印 Set,您将看到所有元素。
    • 集合的顺序是随机的,不能依赖(根据文档)。当前实现按哈希值的顺序进行迭代。但这会根据 JDK 版本和实现而有所不同。
    【解决方案4】:

    一个保留原始的 lambda Set

    integerSet.removeAll( integerSet.stream().filter( i -> i < 0 ).collect( Collectors.toList() ) );
    …
    

    …或简单: Holger's 解决方案 – 见下方评论

    integerSet.removeIf( i -> i < 0 );
    

    【讨论】:

    • 这很聪明,但你传递给removeAll 的东西不就是一个新集合吗?
    • 不——传递给removeAll的是List(更准确地说是一个ArrayList)。关键是,integerSet 的值没有被重新分配。
    • 这不是重新分配integerSet,而是为了修改旧集合而不必要地创建新集合。只需改用integerSet.removeIf(i -&gt; i &lt; 0);
    猜你喜欢
    • 2015-05-17
    • 1970-01-01
    • 2018-04-27
    • 1970-01-01
    • 2012-06-07
    • 2013-01-16
    • 2019-05-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多