Move Zeroes in Java DSA: Push Every Zero to the End Without Losing Order

  • Last Updated: July 29, 2026
  • By: javahandson
  • Series
img

Move Zeroes in Java DSA: Push Every Zero to the End Without Losing Order

Learn move zeroes in java DSA three ways, from a helper list to the one-pass two-pointer swap, with full step-by-step dry runs and clean code.

1. Introduction

Move Zeroes in Java is one of those problems that looks tiny but hides a neat trick. You get an array of numbers. Your job is to push every zero to the end. The catch is the order of the other numbers must stay the same.

So [0, 1, 0, 3, 12, 0, 5] should turn into [1, 3, 12, 5, 0, 0, 0]. Notice how 1, 3, 12, and 5 kept their old order. Only the zeros slid to the back.

One more rule makes it fun. You should do this in place, without building a brand new array. That means moving things around inside the same array you were given.

We build the answer in three steps, from clumsy to clean. First a brute-force version that copies non-zeros into a helper list. Then a leaner one that overwrites in place and fills zeros after. Last comes the one-pass two-pointer swap, which is the version interviewers hope to see.

Every approach gets a full, step-by-step dry run on the same seven numbers. Nothing is skipped. You can watch each line of code fire and see exactly what changes at every move.

2. Understanding the Problem

Let us pin down the rules before we write any code.

  • You get an array of integers, like [0, 1, 0, 3, 12, 0, 5].
  • Move every zero to the end of the array.
  • Keep the non-zero numbers in their original order.
  • Change the array in place, so no fresh array is returned.

For our array the result is [1, 3, 12, 5, 0, 0, 0]. There are three zeros, and they all end up parked at the back. The four non-zeros stay in the order they first appeared. Holding that order while sweeping zeros away is the real work here.

3. Concepts You Need Here

3.1 In-Place Means No New Array

In place means we reuse the same array instead of allocating another one. We can read a value, overwrite a slot, or swap two slots. What we cannot do is create a second array of the same size just to hold the answer.

Why bother? A huge array might not have room for a second copy. Doing the work in place keeps extra memory near zero.

3.2 A Write Pointer That Lags Behind

The key idea is two indexes moving at different speeds. One index reads every slot from left to right. The other index marks the next free spot where a non-zero belongs.

  • Think of the read index as visiting every number, one by one.
  • Meanwhile the write index only steps forward after a non-zero is placed.

Because the write index moves slower, it always sits at or behind the read index. That gap is exactly the space the zeros used to take.

💡 Interview Insight
A common opener is “can you do it in one pass with O(1) extra space?” The answer is yes: one scan, two indexes, a swap when you meet a non-zero. Say that up front and you have already described the optimal solution.

4. Approach 1: Brute Force With a Helper List

The simplest idea is to sort the numbers into two buckets by hand. First collect every non-zero into a helper list, in order. Then add enough zeros to fill the rest. Finally copy that list back into the original array.

4.1 Pseudocode

temp = empty list

for i from 0 to n-1:          // collect non-zeros
    if nums[i] != 0:
        add nums[i] to temp

while size of temp < n:       // pad with zeros
    add 0 to temp

for i from 0 to n-1:          // copy back into nums
    nums[i] = temp[i]

4.2 Pseudocode Explained

  • First, the collecting loop grabs only the non-zeros and keeps their order.
  • Then the while loop tops up temp with zeros until it is full length.
  • Finally, the copy-back loop rewrites temp into nums, because we must change nums itself.

That copy-back step is easy to forget. Without it the original array never changes.

4.3 Java Code

import java.util.*;

public class MoveZeroesBrute {

    public static void moveZeroes(int[] nums) {
        List<Integer> temp = new ArrayList<>();
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] != 0) {
                temp.add(nums[i]);
            }
        }
        while (temp.size() < nums.length) {
            temp.add(0);
        }
        for (int i = 0; i < nums.length; i++) {
            nums[i] = temp.get(i);
        }
    }

    public static void main(String[] args) {
        int[] nums = { 0, 1, 0, 3, 12, 0, 5 };
        moveZeroes(nums);
        System.out.println(Arrays.toString(nums)); // [1, 3, 12, 5, 0, 0, 0]
    }
}

4.4 Java Code Explained

  • Line 6 makes an empty helper list called temp.
  • Lines 7 to 11 add every non-zero into temp, keeping order.
  • Lines 12 to 14 pad temp with zeros up to the full length.
  • Then lines 15 to 17 copy temp back into nums, slot by slot.

4.5 Dry Run of the Brute Force

Array: [0, 1, 0, 3, 12, 0, 5], length 7. We trace all three phases: collecting, padding, then copying back. Nothing is hidden.

Legend for the phases below:

  • i (read index) is the slot we are currently looking at.
  • nums[i] (value there) is the number sitting in that slot.
  • temp after is the helper list once this step finishes.

Phase A — collect non-zeros into temp:

i (read index)nums[i] (value there)zero?actiontemp after
00yesskip[ ]
11noadd 1[1]
20yesskip[1]
33noadd 3[1, 3]
412noadd 12[1, 3, 12]
50yesskip[1, 3, 12]
65noadd 5[1, 3, 12, 5]

Next, Phase B — pad temp with zeros until its length is 7:

pad steptemp size beforeactiontemp after
14add 0[1, 3, 12, 5, 0]
25add 0[1, 3, 12, 5, 0, 0]
36add 0[1, 3, 12, 5, 0, 0, 0]

Finally, Phase C — copy temp back into nums, one slot at a time:

i (write index)temp[i] (value copied)nums after this copy
01[1, 1, 0, 3, 12, 0, 5]
13[1, 3, 0, 3, 12, 0, 5]
212[1, 3, 12, 3, 12, 0, 5]
35[1, 3, 12, 5, 12, 0, 5]
40[1, 3, 12, 5, 0, 0, 5]
50[1, 3, 12, 5, 0, 0, 5]
60[1, 3, 12, 5, 0, 0, 0]

4.6 Reading the Dry Run

There are three phases to follow: collect, pad, then copy back. We take them one at a time, and the only thing worth tracking is the temp list on the right.

Phase A — collecting non-zeros.

We walk every slot and grab only the non-zeros, keeping their order.

  • i = 0: the value is 0, so we skip it. temp stays empty, [ ].
  • i = 1: the value is 1, a non-zero, so we add it. Now temp is [1].
  • i = 2: another 0, so we skip. temp does not change, still [1].
  • i = 3: the value is 3, added. temp grows to [1, 3].
  • i = 4: the value is 12, added. temp is now [1, 3, 12].
  • i = 5: the last 0, so we skip. temp holds at [1, 3, 12].
  • i = 6: the value is 5, added. temp ends as [1, 3, 12, 5].

Notice the order. We added 1, then 3, then 12, then 5, exactly as they appeared. That is how the non-zero order survives.

Phase B — padding with zeros.

temp holds four numbers, but the array has room for seven. So we add zeros until it is full.

  • pad 1: the list is size 4, so we add a 0. Now temp is [1, 3, 12, 5, 0].
  • pad 2: the list is size 5, so we add another 0. Now temp is [1, 3, 12, 5, 0, 0].
  • pad 3: the list is size 6, so we add one more 0. It reaches size 7 as [1, 3, 12, 5, 0, 0, 0], and the loop stops.

temp is now exactly the answer we want. All that is left is to move it into the real array.

Phase C — copying back into nums.

We overwrite nums one slot at a time with temp. The middle rows look messy on purpose, so read them slowly.

  • i = 0: write temp[0]=1 into nums[0]. The array becomes [1, 1, 0, 3, 12, 0, 5].
  • i = 1: write temp[1]=3 into nums[1]. The array becomes [1, 3, 0, 3, 12, 0, 5].
  • i = 2: write temp[2]=12 into nums[2]. The array becomes [1, 3, 12, 3, 12, 0, 5].
  • i = 3: write temp[3]=5 into nums[3]. The array becomes [1, 3, 12, 5, 12, 0, 5].
  • i = 4: write temp[4]=0 into nums[4]. The array becomes [1, 3, 12, 5, 0, 0, 5].
  • i = 5: write temp[5]=0 into nums[5]. The array is unchanged here, [1, 3, 12, 5, 0, 0, 5], since that slot was already 0.
  • i = 6: write temp[6]=0 into nums[6]. The array finishes at [1, 3, 12, 5, 0, 0, 0].

The stale leftovers like 12 and 5 in the tail get overwritten as we go. Once the last slot is written, the array matches temp exactly.

4.7 Time and Space Cost

  • Time is O(n), since each phase walks the array once.
  • Space is O(n), because temp holds a full copy of the numbers.

It works and it is easy to read. The problem is that helper list. Interviewers want the zeros moved without spending O(n) extra memory, so we tighten it next.

5. Approach 2: Overwrite In Place, Then Fill Zeros

We can drop the helper list. Use one write index that marks the next free front slot. Walk the array, and each time you meet a non-zero, drop it at the write index and step that index forward. After the walk, everything from the write index onward gets set to zero.

5.1 Pseudocode

pos = 0                       // next free front slot

for i from 0 to n-1:          // overwrite non-zeros to the front
    if nums[i] != 0:
        nums[pos] = nums[i]
        pos = pos + 1

for j from pos to n-1:        // fill the rest with zeros
    nums[j] = 0

5.2 Pseudocode Explained

  • pos is a bookmark for the next front slot a non-zero can take.
  • The first loop copies each non-zero forward and bumps pos.
  • The second loop zeroes out every slot from pos to the end.

Since pos only moves on a non-zero, it lags behind i. That lag is where the leftover zeros will go.

5.3 Java Code

import java.util.*;

public class MoveZeroesOverwrite {

    public static void moveZeroes(int[] nums) {
        int pos = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] != 0) {
                nums[pos] = nums[i];
                pos++;
            }
        }
        for (int j = pos; j < nums.length; j++) {
            nums[j] = 0;
        }
    }

    public static void main(String[] args) {
        int[] nums = { 0, 1, 0, 3, 12, 0, 5 };
        moveZeroes(nums);
        System.out.println(Arrays.toString(nums)); // [1, 3, 12, 5, 0, 0, 0]
    }
}

5.4 Java Code Explained

  • Line 6 starts pos at 0, the first free front slot.
  • Lines 7 to 12 copy each non-zero to nums[pos], then bump pos.
  • Lines 13 to 15 fill from pos to the end with zeros.
  • Note pos only advances on a non-zero, so it trails behind i.

5.5 Dry Run of the Overwrite Approach

Array: [0, 1, 0, 3, 12, 0, 5], length 7. We trace every i in the overwrite loop, then every j in the fill loop. No step is skipped.

Legend for the tables below:

  • i (read index) is the slot being scanned right now.
  • pos (next free slot) is where the next non-zero will land.
  • nums after shows the whole array once this step is done.

Phase A — overwrite non-zeros to the front:

i (read index)nums[i] (value there)zero?actionpos afternums after
00yesskip0[0, 1, 0, 3, 12, 0, 5]
11nonums[0] = 11[1, 1, 0, 3, 12, 0, 5]
20yesskip1[1, 1, 0, 3, 12, 0, 5]
33nonums[1] = 32[1, 3, 0, 3, 12, 0, 5]
412nonums[2] = 123[1, 3, 12, 3, 12, 0, 5]
50yesskip3[1, 3, 12, 3, 12, 0, 5]
65nonums[3] = 54[1, 3, 12, 5, 12, 0, 5]

Phase B — fill from pos=4 to the end with zeros:

j (fill index)actionnums after
4nums[4] = 0[1, 3, 12, 5, 0, 0, 5]
5nums[5] = 0[1, 3, 12, 5, 0, 0, 5]
6nums[6] = 0[1, 3, 12, 5, 0, 0, 0]

5.6 Reading the Dry Run

The one thing to watch is pos, the next free front slot. It only moves on a non-zero, so it lags behind i. Read each step and keep an eye on that gap.

Phase A — overwriting to the front.

  • i = 0: the value is 0, so we skip. pos stays 0 and nothing moves. Array is [0, 1, 0, 3, 12, 0, 5].
  • i = 1: the value is 1. We write it at nums[pos]=nums[0], then pos becomes 1. Array is [1, 1, 0, 3, 12, 0, 5].
  • i = 2: another 0, so we skip. pos holds at 1 and the array is unchanged.
  • i = 3: the value is 3. We write it at nums[1], then pos climbs to 2. Array is [1, 3, 0, 3, 12, 0, 5].
  • i = 4: the value is 12. We write it at nums[2], then pos moves to 3. Array is [1, 3, 12, 3, 12, 0, 5].
  • i = 5: a 0, so we skip. pos stays 3 and the array is unchanged.
  • i = 6: the value is 5. We write it at nums[3], then pos ends at 4. Array is [1, 3, 12, 5, 12, 0, 5].

See the gap. When i reached 6, pos was only 4. That difference of 2 is exactly the count of zeros we passed. The front four slots now hold 1, 3, 12, 5 in order. The tail still has stale leftovers like 12 and 5, but we clean those next.

Phase B — filling the tail.

pos stopped at 4, which means slots 4, 5, and 6 are free for zeros.

  • j = 4: write 0 into nums[4]. Array becomes [1, 3, 12, 5, 0, 0, 5].
  • j = 5: write 0 into nums[5]. The array is unchanged here since that slot was already 0.
  • j = 6: write 0 into nums[6], overwriting the stale 5. Array finishes at [1, 3, 12, 5, 0, 0, 0].

The stale 12 and 5 from the tail get overwritten, and the answer is complete.

5.7 Time and Space Cost

  • Time is O(n), since we walk the array twice at most.
  • Space is O(1) extra, because we only use the pos index.

This is a big win over the helper list. It touches the array at most twice, though. The next version does the whole job in a single pass.

6. Approach 3: One-Pass Two-Pointer Swap

Here is the cleanest version. Keep one write index for the next free front slot. Scan with a read index across the array. Every time the read index lands on a non-zero, swap it into the write slot and bump write. Zeros drift to the back on their own, and it all happens in a single pass.

6.1 Pseudocode

write = 0                     // next free front slot

for read from 0 to n-1:
    if nums[read] != 0:
        swap nums[write] and nums[read]
        write = write + 1

6.2 Pseudocode Explained

  • write marks the front slot waiting for the next non-zero.
  • read scans every slot from left to right.
  • On a non-zero, the swap places it up front and sends whatever was there back.

When write equals read, the swap just trades a slot with itself, which is harmless. When they differ, the swap pushes a zero toward the back for free.

6.3 Java Code

import java.util.*;

public class MoveZeroesTwoPointer {

    public static void moveZeroes(int[] nums) {
        int write = 0;
        for (int read = 0; read < nums.length; read++) {
            if (nums[read] != 0) {
                int t = nums[write];
                nums[write] = nums[read];
                nums[read] = t;
                write++;
            }
        }
    }

    public static void main(String[] args) {
        int[] nums = { 0, 1, 0, 3, 12, 0, 5 };
        moveZeroes(nums);
        System.out.println(Arrays.toString(nums)); // [1, 3, 12, 5, 0, 0, 0]
    }
}

6.4 Java Code Explained

  • Line 6 starts write at 0, the first free front slot.
  • Line 7 scans the array with the read index.
  • Lines 9 to 11 swap the non-zero at read into the write slot.
  • Then line 12 bumps write only after a real placement.

6.5 Dry Run of the Two-Pointer Sweep

Array: [0, 1, 0, 3, 12, 0, 5], length 7. We trace every read step, including the zeros that trigger no swap. Watch write climb only on a non-zero.

Legend for the table below:

  • read (scan index) is the slot we are checking right now.
  • nums[read] (value there) is the number at that slot before any swap.
  • write (free slot) is where a non-zero will be swapped to.
  • nums after shows the array once this step finishes.
read (scan index)nums[read] (value there)zero?write beforeactionwrite afternums after
00yes0skip0[0, 1, 0, 3, 12, 0, 5]
11no0swap idx0 & idx11[1, 0, 0, 3, 12, 0, 5]
20yes1skip1[1, 0, 0, 3, 12, 0, 5]
33no1swap idx1 & idx32[1, 3, 0, 0, 12, 0, 5]
412no2swap idx2 & idx43[1, 3, 12, 0, 0, 0, 5]
50yes3skip3[1, 3, 12, 0, 0, 0, 5]
65no3swap idx3 & idx64[1, 3, 12, 5, 0, 0, 0]

6.6 Reading the Dry Run

Read each read step with one question in mind: is this a zero we skip, or a non-zero we swap forward? Every swap does two jobs at once, so watch both the value that lands up front and the zero that drops back.

  • read = 0, value 0. A zero, so we do nothing. write stays at 0, still waiting for its first non-zero. The array is untouched, [0, 1, 0, 3, 12, 0, 5].
  • read = 1, value 1. A non-zero. We swap nums[write]=nums[0] with nums[1]. The 1 moves to the front and the 0 drops back to index 1. write becomes 1. The array is [1, 0, 0, 3, 12, 0, 5].
  • read = 2, value 0. Another zero, so we skip. write holds at 1. The zero we just parked stays put for now.
  • read = 3, value 3. A non-zero. We swap nums[1] with nums[3]. The 3 slides up to index 1, and the 0 that sat there goes to index 3. write climbs to 2. The array is [1, 3, 0, 0, 12, 0, 5].
  • read = 4, value 12. A non-zero. We swap nums[2] with nums[4]. The 12 lands at index 2, and a 0 moves to index 4. write is now 3. The array is [1, 3, 12, 0, 0, 0, 5].
  • read = 5, value 0. A zero, so we skip. write stays at 3. All the zeros are now pooling in the middle, ready to be pushed further back.
  • read = 6, value 5. The last non-zero. We swap nums[3] with nums[6]. The 5 takes index 3, and a 0 drops to index 6. write ends at 4. The array is [1, 3, 12, 5, 0, 0, 0], which is the answer.

Two things make this work. First, every swap places a non-zero and evicts a zero at the same time, so one move does double duty. Second, write only moves after a placement, so it always points at the earliest zero. That is why non-zeros stay in order and zeros end up neatly at the back.

💡 Interview Insight
If asked “does swapping break the order of the non-zeros?” the answer is no. The write slot always holds either a zero or the very value you are about to place, so a non-zero never jumps ahead of an earlier non-zero. Order is preserved by construction.

6.7 Comparing the Three Traces

ApproachHow it moves zerosExtra memoryPasses over array
Helper listCopy non-zeros out, pad, copy backA full listThree
Overwrite + fillPush non-zeros forward, zero the tailJust one indexTwo
Two-pointer swapSwap each non-zero to the frontJust one indexOne

7. The Dry Run on Paper

Tables are exact, but a sketch often lands faster. Here is the same two-pointer trace drawn by hand.

Move Zeroes two-pointer approach in Java DSA
  • Each row shows the read index checking one slot and the array right after.
  • A green block marks the final swap that completes the answer.
  • At the bottom, the finished array reads [1, 3, 12, 5, 0, 0, 0].

8. Comparing the Three Approaches

All three give the same result. They just pay different prices.

ApproachTimeSpaceNote
Helper listO(n)O(n)Easy to read, but copies the whole array
Overwrite + fillO(n)O(1)In place, walks the array twice
Two-pointer swapO(n)O(1)In place, single pass, the expected answer

All three run in linear time. What separates them is memory and passes. The two-pointer swap carries almost nothing and finishes in one sweep, so it is the version to reach for.

In an interview, start with the helper list, point out the wasted O(n) memory, then tighten it into the one-pass swap. That climb from clumsy to clean is the story interviewers want to hear.

💡 Interview Insight
If pushed on edge cases, mention an array that is all zeros or has no zeros at all. The swap handles both without any special code: all zeros means write never moves, and no zeros means every swap is a slot trading with itself.

9. Common Mistakes and Edge Cases

A few small traps catch beginners on Move Zeroes. Keep them in mind.

  • Forgetting the copy-back step in the helper version leaves the original array unchanged.
  • Building and returning a new array breaks the in-place rule the problem asks for.
  • Bumping the write index on a zero scrambles the order and leaves gaps.
  • An array of all zeros, like [0, 0, 0], should stay [0, 0, 0] with no swaps at all.
  • An array with no zeros, like [1, 2, 3], should come out unchanged as [1, 2, 3].

Run those last two cases through your code before you call it done. They catch more bugs than any ordinary input will.

10. Interview Questions

Q: What is the most efficient way to move zeroes in Java?

A: The one-pass two-pointer swap is best. It uses O(n) time and O(1) extra space, scanning once and swapping each non-zero to the front while zeros drift to the back.

Q: Does moving zeroes in place preserve the order of non-zero numbers?

A: Yes. The write slot always holds a zero or the value being placed, so a non-zero never jumps ahead of an earlier one. Order is preserved by construction.

Q: What edge cases should I test for Move Zeroes?

A: Test an all-zeros array like [0, 0, 0] and a no-zeros array like [1, 2, 3]. The two-pointer swap handles both with no special code.

11. Conclusion

Move Zeroes in Java looks simple, and with the right idea it truly is. The trick is a write index that lags behind a read index. That single gap is where all the zeros quietly collect.

Our seven-number trace showed the payoff clearly. The helper list copied everything twice over. The one-pass swap fixed each non-zero into place and let the zeros drift back on their own.

So take the pattern, not just the answer. A slow write pointer and a fast read pointer solve a whole family of array problems. You will see the same two-index move again and again as the series goes on.

12. Further Reading

Leave a Comment