Java:从现有的字符串数组中删除一个项目

3
我已经查看了几个SOF线程,但似乎找不到我需要的答案。大部分提供的代码超出了我目前所学范围的答案。
我尝试了很多不同的方法,但无法使它按照我的需求工作。
该程序应该读取给定的数组,查找给定的toRemove项,并重新打印不包含toRemove项的数组。
我认为我的问题在于removeFromArray方法。
public static void main(String[] args) 
{

    String[] test = {"this", "is", "the", "example", "of", "the", "call"};
    String[] result = removeFromArray(test, "the");
    System.out.println(Arrays.toString(result));
}

public static String[] removeFromArray(String[] arr, String toRemove)
{
    int newLength = 0;
    for(int i = 0; i < arr.length; i++)
    {    
        if(arr[i].contains(toRemove))
        {
            newLength++;
        }
    }
    String[] result = new String[arr.length-newLength];
    for(int i = 0; i < (result.length); i++)
    {
        if(arr[i].contains(toRemove))
        {

        }
        else
        {
            result[i] = arr[i];
        }
    }
    return result;
}

这是我Java课程上的一个作业,我们还没有学习List(在我的谷歌搜索中找到的答案之一),所以这对我来说不是一个选项。目前应该输出: [this, is, example, of, call],但实际上它输出的是: [this, is, null, example, of]。非常感谢任何形式的帮助!

你可能想使用.equals而不是.contains。如果你使用.contains,你的方法也会从数组中删除“the dog”,“absinthe”等内容。 - Andy
5个回答

5
在第二个循环中需要使用2个索引,因为你正在遍历两个长度不同的数组(输入数组和输出数组)。
此外,“newLength”是一个令人困惑的名称,因为它并没有包含新长度,而是包含了输入数组长度与输出数组长度之间的差异。你可以改变它的值,使其与名称匹配。
int newLength = arr.length;
for(int i = 0; i < arr.length; i++)
{    
    if(arr[i].contains(toRemove))
    {
        newLength--;
    }
}
String[] result = new String[newLength];
int count = 0; // count tracks the current index of the output array
for(int i = 0; i < arr.length; i++) // i tracks the current index of the input array
{
    if(!arr[i].contains(toRemove)) {
        result[count] = arr[i]; 
        count++;
    }
}
return result;

0
以下代码删除所有提供的字符串的出现。
请注意,我添加了几行代码来验证输入,因为如果我们将空数组传递给您的程序,它将失败。您应该始终在代码中验证输入。
public static String[] removeFromArray(String[] arr, String toRemove) {

    // It is important to validate the input
    if (arr == null) {
        throw new IllegalArgumentException("Invalid input ! Please try again.");
    }

    // Count the occurrences of toRemove string.
    // Use Objects.equals in case array elements or toRemove is null.
    int counter = 0;
    for (int i = 0; i < arr.length; i++) {
        if (Objects.equals(arr[i], toRemove)) {
            counter++;
        }
    }

    // We don't need any extra space in the new array
    String[] result = new String[arr.length - counter]; 
    int resultIndex = 0; 

    for (int i = 0; i < arr.length; i++) {
        if (!Objects.equals(arr[i], toRemove)) {
            result[resultIndex] = arr[i];
            resultIndex++;
        }
    }

    return result;
}

Objects.isNull 主要用作过滤谓词(例如 stream.filter(Objects::isNull).count())。最好使用 arr == nulltoRemove == null,因为 Objects.isNull 只执行 == null - Andy
此外,当 arr.length == 0 时没有理由抛出错误。毕竟,当集合为空时,Collection.remove 也能正常工作。 - Andy
调用者可能希望通过调用 removeFromArray(arr, null) 来删除 null 元素。最好使用 Objects.equals(arr[i], toRemove) 来容忍数组和 toRemove 中的 null 值。 - Andy
我已经相应地修改了答案。感谢您的建议。虽然我只是看了一下isNull方法的源代码。它只是检查元素是否为空,没有什么花哨的东西。 - Lakshmikant Deshpande
1
谢谢!我加入了我所说的Objects.equals检查。 - Andy

0

你的代码中有@Eran指出的错误,可以解决你的问题。但是我将讨论另一种方法。

目前,你首先要迭代整个数组来查找要删除的元素的数量,然后再迭代数组来删除它们。为什么不只是迭代数组来删除它们呢?(我知道,你的第一个循环有助于确定输出数组的大小,但如果你使用一些类似于List的东西,如ArrayList等,你就不需要它了。)

List<String> resultList = new ArrayList<String>();
for(int i = 0; i < arr.length; i++)
{
    if(!arr[i].contains(toRemove))
    {
        resultList.add(arr[i]);
    }
}

你可以返回 resultList,但如果你真的需要返回一个数组,你可以像这样将 resultList 转换为数组:

String [] resultArray = resultList.toArray(new String[resultList.size()]);

然后返回这个数组。在ideone上查看此方法的实现。


其实差别不大,尽管这种方法更简单。根据在特定机器上读写所需的相对时间,操作系统的方法可能会更快。 - Andy
我认为不是这样的。因为在OP的方法中,有两个循环中的contains操作。总共所需的时间是(2 * N * contains所需的时间),其中N是输入数组的长度。但在这种方法中,它是(N * contains所需的时间) + (N * 复制所需的时间),而contains操作比仅仅复制操作要昂贵得多。 - Ahmad Khan
其实,是的,你说得对。而且,实际上,OP 可能不应该使用 contains,而应该使用 equals - Andy
请注意,复制所需的时间实际上是O(N),而不是真正的N,因为定期调整ArrayList大小会产生一些成本。 - Andy
当然,我们不能忽略调整ArrayList大小的时间。 - Ahmad Khan
并不是说,在 OP 的方法中没有复制操作。因此,我认为这种方法具有相当大的优势。 - Ahmad Khan

0

尝试使用Java8版本

    List<String> test = Arrays.asList("this", "is", "the", "example", "of", "the", "call");

    test.stream()
        .filter(string -> !string.equals("the"))
        .collect(Collectors.toList())
        .forEach(System.out::println);

0
你可以使用Java Stream代替,它将给你期望的结果,而且你的代码会更清晰、更简洁。
请看下面我写的解决你问题的方法。
public static String[] removeFromArray(String[] arr, String toRemove) {
    return Arrays.stream(arr)
      .filter(obj -> !obj.equals(toRemove))
      .toArray(String[]::new);
}

如果您不熟悉Java Stream,请参阅此处的文档


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接