从特定索引处的列表中删除元素

Remove elements from a List at a specific index

我正在尝试编写一种删除每组 4 个元素的第一个、第二个和第三个元素的方法。 它似乎根本不起作用。 有人可以帮忙吗?

public static void reduziereKommentare(List<String> zeilen) {
        if (!zeilen.isEmpty()) {
            if (zeilen.size() % 4 != 0) {
                throw new RuntimeException("Illegal size " + zeilen.size() + " of list, must be divisible by 4.");
            }
            for (int i = 1; i <= zeilen.size() % 4; i++) {
                zeilen.remove(i);
                zeilen.remove(i + 1);
                zeilen.remove(i + 2);
            }
        }
        System.out.println(zeilen);
    }

如评论中所述,删除元素会影响索引。每当我需要做这样的事情时,我要么使用 Iterator,要么向后循环。:

for (int i = zeilen.size() - 4; i >= 0; i -= 4) {
    zeilen.remove(i + 2);
    zeilen.remove(i + 1);
    zeilen.remove(i);
}

请注意,我每次迭代都从 i 中减去 4,所以我每次都返回完整的四个块。

另请注意,我首先删除了最大的索引元素。如果我在循环中使用 ii + 1i + 2,我会再次 运行 进入相同的问题。我也可以使用 i 3 次,但这更清楚。

我的看法...不需要大小先决条件检查,但如果它代表比此方法范围更广的错误,您可能仍想捕获它。

鉴于此测试代码...

    // Test code
    List<String> myList = new ArrayList<>();
    for (int i = 0; i < 20; i++) {
        myList.add(String.valueOf(i));
    }
    

'zeilen' 循环可以实现为...

    // "before" diagnostics
    System.out.println(zeilen);

    // The 'zeilen' loop
    for (int i = 0, limit = zeilen.size(); i < limit; i++) {
        if ((i+1) % 4 > 0) zeilen.remove(i/4);
    }

    // "after" diagnostics
    System.out.println(zeilen);

并生产

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
[3, 7, 11, 15, 19]

适用于任何长度的列表,在列表中保留每个“第 4 个”元素。

还有几个测试用例:

Given                   Results in
[]                      []
[0,1]                   []
[0,1,2,3]               [3]
[0,1,2,3,4]             [3]
[0,1,2,3,4,5,6,7]       [3,7]
[0,1,2,3,4,5,6,7,8]     [3,7]

简单地将每四个项目添加到新列表并 return 不是更容易吗?这也将消除从列表中删除元素时可能涉及的任何重复复制。并且目标列表的大小可以适当地开始。

public static List<String> reduziereKommentare(List<String> zeilen) {
    Objects.requireNonNull(zeilen);
    List<String> zeilen1= new ArrayList<>(zeilen.size()/4);

    for(int i = 3; i < zeilen.size(); i+=4) {
            zeilen1.add(zeilen.get(i));
    }

    return zeilen1;
}

您也可以使用流。


zeilen = IntStream.iterate(3, i ->i < zeilen.size(), i->i+=4)
                .mapToObj(zeilen::get).toList();

备注:

  • 无论列表是否为空或者大小不能被[=12=整除],这都会起作用。它只会忽略额外的元素。
  • 将结果分配给原始变量将导致旧列表被垃圾回收。
  • 我只检查空参数,因为那会导致异常。当然,如果提醒用户尺寸很重要,只需重新添加其他支票即可。

您的代码示例使用列表数据类型 - List<String> zeilen - 但您单独编写了一条注释,说明您是从数组开始的:

"I used the Arrays.asList() function to add elements to the list"

asList() shows the input argument is an array, defined using varargs的签名:

public static <T> List<T> asList(T... a)

因此,您将从这样的事情开始:

// rely on automatic array creation via varargs
List<String> list = Arrays.asList("one", "two", "three");

或来自显式数组,如下所示:

String[] strings = {"one", "two", "three"};
List<String> list = Arrays.asList(strings);

这是您当前解决方案的更完整图片:

  • 从数组开始 – String[] – 显式创建它或依赖于通过可变参数自动创建数组
  • 使用 Arrays.asList()
  • 从该数组创建一个 List<String>
  • 一次跳过三个项目遍历列表,只保留每四个项目(因此:第 4、8、12、16 等)

因为起点是一个字符串数组,并且知道你是 有兴趣只保留每 4 个元素, 你可以:

  • 创建一个新的空 java.util.List<String>
  • 迭代数组的每个元素
  • 每第 4 个、第 8 个等元素,将其添加到最终结果列表中;忽略其他一切

这是执行此操作的代码:

private static List<String> buildListOfEveryFourthElement(String[] array) {
    List<String> everyFourthElement = new ArrayList<>();
    if (array != null) {
        // start from "1", a bit easier to reason about "every 4th element"?
        int current = 1;
        
        for (String s : array) {
            if (current > 1 && current % 4 == 0) {
                everyFourthElement.add(s);
            }
            current++;
        }
    }
    return everyFourthElement;
}

我省略了输入是否能被 4 整除的检查,但是你可以很容易地编辑第一个 if 语句 包括:if (array != null && array.length % 4 == 0) { .. }

这种“随手构建列表”方法的好处(相对于使用起始数组调用 Arrays.asList()) 是原始输入数组不会以任何方式与结果列表相关联。

那又怎样?正如您在其中一条评论中提到的那样,您发现这是不允许的 修改列表——调用 .remove() 将抛出 java.lang.UnsupportedOperationException。 请注意,如果您尝试 add() 某些内容到列表中,也会发生这种情况。

为什么会抛出异常? 因为 asList() returns a java.util.List 由输入数组支持,这意味着列表和数组是 有点绑在一起。如果它允许您从(或向) list 然后它还必须自动更新支持 array,他们没有那样实现。 这是来自 asList() Javadoc 的简短片段:

Returns a fixed-size list backed by the specified array. (Changes to the returned list "write through" to the array.)

通过创建一个新列表并沿途填充它,您可以稍后在代码中自由修改该列表 通过删除或添加元素,对整个事物进行排序等。您还将避免对数组进行任何更改 在列表中显示为(可能令人惊讶的)变化——因为 该列表由数组支持,数组元素的更改将在关联列表中可见。