从数组中删除所有零

18

我有一个数组:

[0, 5, 6, 0, 0, 2, 5]
我想将其中所有的零删除,以便返回以下结果(保持相同顺序):
[5, 6, 2, 5]

有没有比下面更简单的方法来删除所有零?

int[] array = {0, 5, 6, 0, 0, 2, 5};
        int len = 0;
        for (int i=0; i<array.length; i++){
            if (array[i] != 0)
                len++;
        }
        int [] newArray = new int[len];
        for (int i=0, j=0; i<array.length; i++){
            if (array[i] != 0) {
                newArray[j] = array[i];
                j++;
            }
        }

我找不到Arrays类中的任何方法,Google/SO搜索也没有给我好的答案。


6
避免在一开始就添加它们是最简单的解决方案。 - Peter Lawrey
1
你需要一个新的长度不同的数组,因此你仍然需要进行复制。使用集合将节省你需要提前找到最终大小的步骤。 - Thorbjørn Ravn Andersen
我的情况是:数组是一个游戏的棋盘。当一个或多个洞中不存在“物品”时,有许多可能性。因此,零将存在那里,这就是我在问的原因... - Hidde
如果可以避免添加零,Peter建议一开始就避免添加零是最好的方法。在无法避免添加零的情况下,例如客户端输出等情况下,您的算法是最优的。 - Zéychin
2
这个特定的问题是在 Java 中,那就是为什么我要用 Java 标签来询问它。 - Hidde
显示剩余2条评论
11个回答

25

无法避免使用一个循环来计数或过滤零;但是,使用System.arraycopy()可以避免第二个循环。

此函数将现有数组复制到不同长度的数组中,这意味着在使用它之前,我们的数组必须已经没有零。因此,在第一个循环中,我们不能仅仅计算零的数量,还必须将其过滤掉。以下是实际操作的方法:

int targetIndex = 0;
for( int sourceIndex = 0;  sourceIndex < array.length;  sourceIndex++ )
    if( array[sourceIndex] != 0 )
        array[targetIndex++] = array[sourceIndex];
int[] newArray = new int[targetIndex];
System.arraycopy( array, 0, newArray, 0, targetIndex );
return newArray;

1
但是最终你得到的是一个Integer[]而不是一个int[] - Robin
3
不,你不需要。这句话的意思是什么? - Mike Nakis
性能在这里并不是真正的问题,我正在寻找更短的编写上述内容的方法。我喜欢ArrayLists,但希望只使用数组有更短的方式。 - Hidde
Hidde,我修改了我的答案。我认为这个解决方案是最好的。 - Mike Nakis
2
@Robin 我看了一下文档,你说得对。自从我上次写Java代码以来已经很长时间了,我一直在使用C#,在其中可以有一个原始int数组列表。我会更正我的答案,谢谢。 - Mike Nakis
显示剩余5条评论

13

这个怎么样:

Integer[] numbers = {1, 3, 6, 0, 4, 0, 3};
List<Integer> list = new ArrayList<Integer>(Arrays.asList(numbers));
list.removeAll(Arrays.asList(Integer.valueOf(0)));
numbers = list.toArray(new Integer[list.size()]);
System.out.println(Arrays.toString(numbers));

输出:

[1, 3, 6, 4, 3]

1
我理解了这个答案的大部分内容,除了在 numbers = list.toArray(new Integer[0]); 这一行中, new Integer[0] 有什么作用?我知道它会初始化一个大小为0的新Integer数组,但它有什么帮助呢? - Manish Giri
2
@ManishGiri 对六年前的评论进行回复,很抱歉,您可能已经解决了问题。也许对其他人有用。创建一个大小为零的数组允许toArray()方法使用反射找到返回数组的类型。您必须这样做才能在编译时明确确定返回数组的类型。顺便说一句,在最新版本的JVM中,这种数组创建方式被优化得非常好,速度很快。 - Nikita Kobtsev

5

使用Java 8,您可以将数组转换为流,应用.filter()方法,然后将其转换回数组:

int[] array = {0, 5, 6, 0, 0, 2, 5};

int[] filteredArray = Arrays.stream(array).filter(num -> num != 0).toArray();    

// filteredArray = {5, 6, 2, 5};

4
您只需要使用一个循环即可实现此功能。但是,这是否更好或更清晰,我恐怕这取决于个人口味。
int[] array = {0, 5, 6, 0, 0, 2, 5};
int[] temp = new int[array.length];
int numberOfZeros = 0;
for (int i=0; i<array.length; i++){
  if (array[i] != 0){
    temp[i-numberOfZeros] = array[i];
  } else {
    numberOfZeros++;
  }
}
int[] result = new int[temp.length-numberOfZeros];
System.arraycopy(temp, 0, result, 0, result.length);

另一种选择是使用像ArrayList这样的List实现,您可以从中删除元素,但这时您必须使用Integer实例而不是int。
List<Integer> originalList = ....;
Iterator<Integer> iterator = originalList.iterator();
while ( iterator.hasNext() ) {
  Integer next = iterator.next();
  if ( next == 0 ){
    iterator.remove();
  }
}
//convert to array if needed
Integer[] result = originalList.toArray( new Integer[originalList.size()]);

有点重复了,类似迈克的回答。 - Hidde
是的,尽管在他最初的回答中没有提到这一点,但那是我在开始回答之前看到的唯一一个。 - Robin

3
这个例子使用了Apache Commons库,希望对你有用。
import org.apache.commons.lang.ArrayUtils;

public class Test {
    public static void main(String args[]) {
        int[] array = {0, 5, 6, 0, 0, 2, 5};

        // this loop is to remove all zeros
        while(ArrayUtils.contains(array, 0))
            array = ArrayUtils.removeElement(array, 0);

        // this loop will print the array elemnents
        for(int i : array)
            System.out.println(i);

    }
}

1
Apache Commons Lang 3.5现在包含了removeAllOccurences(T[] array, T element)方法,它正是OP想要的并且你建议的功能,但不需要循环。 - GriffinG

2

您使用的编程语言是否使用.map或.reduce函数,或者是否有扩展可以实现这一功能?

在Swift中,您可以通过.filter来实现此功能;请注意:

var orders = [0, 5, 6, 0, 0, 2, 5]

orders = orders.filter({ $0 != 0 })

print (orders)

这将返回[5, 6, 2, 5],保留您的顺序。

也许你使用的编程语言有一个Underscore库或Lo-Dash。 - zardon

2
您可以在O(1)额外空间中删除零。不需要将元素复制到另一个数组中,只需返回大小并打印相同的数组即可:
public class RemoveZeros {
    
    static int removeZeros(int[] a){
        int j =0;
        
        for(int i =0;i<a.length;i++) {
            if(a[i] !=0) {
                a[j] = a[i];
                j++;
            }
            
        }
        
        return j;
    }
    public static void main(String[] args) {
        int[] a = new int[]{0, 5, 6, 0, 0, 2, 5};
        int val = removeZeros(a);
        for(int i =0;i<val;i++)
            System.out.println(a[i]);
    }
}

1

如果您可以使用List而不是数组,实际上您可以做的只是创建一个新的Iteratable接口并像google-collections Collections2.filter()一样对其应用方法,您可以查看它。


1
你可以使用一个 Vector:
Vector vec = new Vector();
for (int i=0; i<array.length; i++){
   if (array[i] != 0)
      vec.add(array[i]);
}
vec.toArray()

(这不是精确的语法,但你会明白思路...)


我更喜欢使用ArrayList,因为它使用泛型。 - Martijn Courteaux
@MartijnCourteaux Vector 也可以使用泛型。选择 ArrayList 而不是 Vector 的更好理由有很多(例如,请参见 这个 SO 上的问题)。 - Robin

1
尝试基本方法:

public int[] convert(int[] data) {
    int count =0;
    for (int i =0; i< data.length; i++) {
        if(data[i]==0)
            count++;
    }
    int[] nonZero = new int[data.length-count];
    int j =0;
    for(int i = 0; i<data.length; i++) {
        if(data[i]!=0) {
            nonZero[j] = data[i];
            j++;
        }
    }
    return nonZero;
}

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