如何在Java中从数组中删除null

5

我编写了一个方法,用于从程序中需要的数组中删除空值。但是,这个方法似乎不起作用,空值没有被删除。以下是我的代码。

public void removeNull(String[] a)
{
       for(int i=0; i<a.length; i++)
    {
        if(a[i] == null)
        {
            fillArray(a, i);
        }
    }
}

public void fillArray(String[] a, int i)
{
    String[] a2 = new String[a.length-1];

    for(int j=0; j<a2.length; j++)
    {
            if(j<i)
            {
                a2[j]=a[j];
            }
        else if(j>i)
        {
            a2[j]=a[j+1];
        }
    }

    a=a2;
}
感谢您的提前帮助!

可能存在相同的问题链接! - Gatekeeper
能否不允许将空值放入数组中? - John Kane
12个回答

7
我建议采用简单的方式,除非性能真的是一个问题:
public String[] removeNull(String[] a) {
   ArrayList<String> removedNull = new ArrayList<String>();
   for (String str : a)
      if (str != null)
         removedNull.add(str);
   return removedNull.toArray(new String[0]);
}

2
+1 - 除非真正需要性能,否则最简单的代码是最好的。 - Stephen C
+1 你真的想给“remove”变量赋予ArrayList类型吗?我考虑使用List,以最小化对实际类型的依赖。 - KLE
如果我要返回ArrayList,这是一个好主意,因为我可能想通过多态性来更改实现,但对于小方法中的类型并不重要。 - Garrett Hall

7

使用Streams API实现的解决方案:

SomeClass[] array = new SomeClass[N];
...
array = Arrays.stream(array).filter(Objects::nonNull).toArray(SomeClass[]::new);

我把这个问题写下来,可能会得到一些关于适用性、相对性能等方面的想法。


5

大家好,首先我要为我的英语道歉,我正在学习中,这是我的第一篇帖子,所以我想在这里尝试分享我的解决方案,以下是我的解决方法:

String[] removeNulls(String[] nullsArray) {
    int countNulls = 0;

    for (int i = 0; i < nullsArray.length; i++) { // count nulls in array
        if (nullsArray[i] == null) {
            countNulls++;
        }
    }
    // creating new array with new length (length of first array - counted nulls)
    String[] nullsRemoved = new String[nullsArray.length - countNulls];

    for (int i = 0, j = 0; i < nullsArray.length; i++) {

        if (nullsArray[i] != null) {
            nullsRemoved[j] = nullsArray[i];
            j++;
        }
    }
    return nullsRemoved;
}

2

在一个方法中更改变量的引用,并期望这种更改能够反映在调用该方法的方法中是不可行的。

您需要返回新数组。

public String[] removeNull(String[] a)
{
    for(int i=0; i<a.length; i++)
    {
        if(a[i] == null)
        {
            a = fillArray(a, i);
        }
    }

    return a;
}

public String[] fillArray(String[] a, int i)
{
    String[] a2 = new String[a.length-1];

    for(int j=0; j<a2.length; j++)
    {
            if(j<i)
            {
                a2[j]=a[j];
            }
        else if(j>i)
        {
            a2[j]=a[j+1];
        }
    }

    return a2;
}

2
这种方式会更快:
private static String[] removeNulls(String[] strs) {
    int i = 0;
    int j = strs.length - 1;
    while (i <= j) {
        if (strs[j] == null) {
            --j;
        } else if (strs[i] != null) {
            ++i;
        } else {
            strs[i] = strs[j];
            strs[j] = null;
            ++i; --j;
        }
    }


    return Arrays.copyOfRange(strs, 0, i);
}

1

我在你的代码中发现了两个错误:

  • 你的方法fillArray没有涵盖i == j的情况
  • 你的赋值语句a = a2;没有产生你想象中的效果。在Java中,参数是按值传递的,你的赋值语句并没有改变第一个方法中a的值。尝试在fillArray中返回一个a2的实例,并将这个值分配给removeNull中的a

0

试试这个(我没有测试过):

public String[] removeNull(String[] a) {
    String[] tmp = new String[a.length];
    int counter = 0;
    for (String s : a) {
        if (s != null) {
            tmp[counter++] = s;
        }
    }
    String[] ret = new String[counter];
    System.arraycopy(tmp, 0, ret, 0, counter);
    return ret;
}

为什么你要把它复制到最后的ret中? - Ashkan Aryan
因为可能并不需要每个字段(这意味着最后可能有空值)。 - 0xJoKe

0

在删除数组中的值时,大小会发生变化,因此您不能保留相同的数组(您可以将null推到末尾)。

与具有自动可调整大小的数组类似的结构是ArrayList。一个选项是:

String[] inputs;
List<String> items = new ArrayList<String>(inputs.length);
for(String input : inputs) {
   if (input != null) {
      items.add(input);
   }
}
String[] outputs = items.toArray(new String[items.size()]);

性能可能会稍微低于直接使用数组,但由于数组具有固定大小,您需要使用两个循环来处理数组:

  • 一个用于计算非空值的数量
  • 构建数组后,相同的循环来复制值。

这也可能不是最理想的性能,而且要正确地实现它真的要复杂得多......


另一种方法是将 null 移至末尾,然后创建一个更短的数组,该数组不包括 null。思路如下:
String[] strings;
int writeIndex = 0;
int max = strings.length;
for(int readIndex = 0; readIndex < max; readIndex++) {
   String read = strings[readIndex];
   if (read != null) {
      strings[writeIndex++] = read;
   }
}
String[] outputs = new String[writeIndex];
System.arraycopy(strings, 0, ouputs, 0, writeIndex);

0

这样你就可以在一个循环中删除 null 值,但它不会调整数组大小:

public static void removeNull(String[] a) {
    int nullCount = 0;
    for (int i = 0; i < a.length; i++) {
        if (a[i] == null) {
            nullCount++;
        } else {
            a[i-nullCount] = a[i];
        }
    }
}

这个会创建一个新的数组,但包含两个循环:

public static String[] removeNull(String[] a) {
    int nullCount = 0;
    for (int i = 0; i < a.length; i++) {
        if (a[i] == null) nullCount++;
    }
    String[] b = new String[a.length-nullCount];
    int j = 0;
    for (int i = 0; i < a.length; i++) {
        if (a[i] != null) b[j++] = a[i];
    }
    return b;
}

你可以考虑使用 System.arraycopy 对代码进行优化。我希望代码能够正常运行。

我认为你不能通过使用arrayCopy进行优化。使用arrayCopy带来的任何好处都会在确定何时可以使用它的开销中丢失。我能想到的唯一优化是测试nullCount是否为零,如果是,则返回原始数组。 - Stephen C

0

你有两个选择:

  1. 创建一个与输入长度相同的新数组,然后将非空值分配给它,并将其减去非空元素的计数。

    示例在0xJoKe的答案中。

  2. 如果您只需要处理这样的数组,可以为其创建适配器。

    public class NullProofIterable<T> implements Iterable<T>{
    
    private final T[] array;
    
    public NullProofIterable(T[] array){
        this.array = array;
    }
    
    @Override
    public Iterator<T> iterator() {
        return new NullProofIterator<T>(this.array);
    }
    
    
    private static class NullProofIterator<T> implements Iterator<T> {
    
        private final T[] array;
        private int index = 0;
    
        private NullProofIterator(T[] array) {
            this.array = array;
        }
    
        @Override
        public boolean hasNext() {
    
            return this.index < this.array.length;
        }
    
        @Override
        public T next() {
            return this.array[this.index++];
        }
    
        @Override
        public void remove() {
            throw new RuntimeException("Remove not allowed in this iterator");
        }
    
    }
    
    }
    

然后在源代码中,你只需要做的事情是:

for(String str : new NullProofIterable<String>(strArray)) {
    //Perform action on not null string         
}

第二个选项是使用“!= null”条件的花哨用法,但当方法需要返回一些数据时可能会很有帮助。

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