How to Print an Array in Java: 5 Ways (With Examples)

how to print an array in java

If you have ever typed System.out.println(myArray) and gotten something like [I@659e0bfd instead of your numbers, you are in the right place. Printing an array is one of the first things that trips up Java students, and it is only the start. Right after that, assignments ask you to search an array, reverse it, copy it, or switch to an ArrayList.

Arrays are usually the first data structure you meet in Java. Once they click, the rest of data structures (lists, stacks, queues, trees) gets a lot easier. This guide covers 5 ways to print an array in Java (including 2D arrays), how to check if an array contains a value, how to reverse an array or list, when to use an array versus an ArrayList, how to build your own dynamic array, and how to copy a list without surprises.

We compiled and ran every example on Java 21, and the output shown under each one is the real output.

Quick answers:

  • To print a 1D array in one line, use Arrays.toString(arr). For a 2D array, use Arrays.deepToString(arr).
  • Printing an array directly with println(arr) shows its type and a hash code, not its values.
  • An array holds one type of data, and its size is fixed once you create it. An ArrayList can grow and shrink.
  • To check for a value, loop through the array, or use Arrays.asList(arr).contains(x) for object arrays like String[].
  • To reverse a list, use Collections.reverse(list). To reverse an array, swap values from both ends.
  • new ArrayList<>(original) is a shallow copy. A deep copy also copies every object inside.

What Is an Array in Java?

An array is a container that holds a fixed number of values of the same type. If you declare an int array, it can only hold int values. You cannot put a String or a decimal number like 3.5 in it. Every value sits at a numbered position called an index, and the first index is always 0.

Array In Java

int[] scores = new int[5];          // 5 slots, all start at 0
int[] ages = {18, 19, 21};          // size 3, filled right away
String[] names = new String[3];     // 3 slots, all start at null
scores[0] = 95;                     // put 95 in the first slot
System.out.println(ages.length);    // prints 3

Java has two main kinds of arrays. A one-dimensional array stores a single row of values, like the ones above. A multidimensional array stores rows and columns, like a grid or a table. The most common is the 2D array: int[][] grid = {{1, 2}, {3, 4}};. Oracle's Java tutorial on arrays covers the syntax in more detail.

The one thing to remember: once you create an array, its size never changes. If you need a collection that grows, you want an ArrayList, which we compare with arrays later in this guide.

Why Does println(array) Print Something Like [I@659e0bfd?

This is one of the most common array questions Java students ask. Here is what happens when you print an array directly:

public class Main {
    public static void main(String[] args) {
        int[] numbers = {10, 20, 30};
        String[] names = {"Ana", "Ben"};

        System.out.println(numbers);
        System.out.println(names);
    }
}

Output:

[I@659e0bfd
[Ljava.lang.String;@2a139a55

Arrays in Java do not have their own way of turning into text. When you print one, Java falls back to the default: a type code, an @ sign, and a hash code. [I means "array of int," and [Ljava.lang.String; means "array of String." The part after the @ is a hash code, and you may see a different one on your computer. Your values are fine. You are just printing the array object instead of what is inside it.

The fix is any of the methods in the next section. The quickest is Arrays.toString(numbers).

The one exception is char arrays. println has a special version for char[], so it prints the letters. But as soon as you join the array with a String, you get the hash code again:

public class Main {
    public static void main(String[] args) {
        char[] letters = {'J', 'a', 'v', 'a'};

        System.out.println(letters);              // prints the characters
        System.out.println("Word: " + letters);   // prints the memory-style text
    }
}
Java
Word: [C@659e0bfd

Use new String(letters) when you need a char array inside a sentence. "Word: " + new String(letters) prints Word: Java.

Now it is time to print. Some methods work for one-dimensional arrays, and some are made for multidimensional arrays. Here are the five you will use most:

MethodBest forOutput looks like
1. For-each loopSimple printing, custom format10 20 30
2. For loop with an indexWhen you need the index, or commas between values10, 20, 30
3. Arrays.toString()Quick one-line printing and debugging[10, 20, 30]
4. Arrays.deepToString() or nested loops2D arrays[[1, 2], [3, 4]]
5. String.join() or a streamString arrays, or one value per lineapple, banana

The for-each loop picks up each element from the array, one at a time, from first to last. You do not need to worry about indexes at all.

public class Main {
    public static void main(String[] args) {
        // Declaring one array
        int[] a = {10, 20, 30, 40, 50};

        // Running the for-each loop
        for (int i : a) {
            // Printing each value with a space
            System.out.print(i + " ");
        }
    }
}

Output:

10 20 30 40 50

How it works:

  1. We declare a one-dimensional array with five values.
  2. The for-each loop reads as "for each int i in a." On every pass, i holds the next value.
  3. We print each value followed by a space, so the numbers do not run together.

A normal for loop uses the index to reach each element. It is a little more typing than for-each, but you get the index, which you need for things like numbering the output or skipping the last comma.

public class Main {
    public static void main(String[] args) {
        // Declaring one array
        int[] a = {10, 20, 30, 40, 50};

        // a.length gives the number of elements
        for (int i = 0; i < a.length; i++) {
            // Printing each value with a space
            System.out.print(a[i] + " ");
        }
    }
}

Output:

10 20 30 40 50

How it works:

  1. We declare an array with five values.
  2. The loop starts at index 0 and runs while i is less than a.length, the number of elements. Using < and not <= matters here. a[5] does not exist, and asking for it throws an ArrayIndexOutOfBoundsException.
  3. On each pass, we print a[i] followed by a space.

Many assignments want the values separated by commas, with no comma after the last one. The index makes that easy:

public class Main {
    public static void main(String[] args) {
        int[] a = {10, 20, 30, 40, 50};

        for (int i = 0; i < a.length; i++) {
            System.out.print(a[i]);
            if (i < a.length - 1) {
                System.out.print(", "); // no comma after the last value
            }
        }
        System.out.println();
    }
}
10, 20, 30, 40, 50

This is the fastest way to print a whole array. Arrays.toString() turns the array into a String with square brackets and commas, and you print that String.

General syntax: Arrays.toString(arrayName)

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        // Declaring one array
        int[] a = {10, 20, 30, 40, 50};

        // Converting the array to a String and printing it
        System.out.println(Arrays.toString(a));
    }
}

Output:

[10, 20, 30, 40, 50]

How it works:

  1. We import java.util.Arrays, the class that holds helper methods for arrays.
  2. We declare a one-dimensional array.
  3. Arrays.toString(a) converts it to the String "[10, 20, 30, 40, 50]", and println prints it.

The brackets and commas are part of the output. That is perfect for debugging, but if your assignment asks for a specific format, use a loop instead.

A 2D array is an array of arrays: one big array where every element is a smaller array (a row). Arrays.toString() does not work on it. It only goes one level deep, so it prints the hash codes of the rows:

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[][] a = {{10, 20}, {30, 40}, {50}};
        System.out.println(Arrays.toString(a)); // wrong method for 2D arrays
    }
}
[[I@659e0bfd, [I@2a139a55, [I@15db9742]

Use Arrays.deepToString() instead. It goes into every row and prints the values.

General syntax: Arrays.deepToString(arrayName)

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        // Declaring one 2D array
        int[][] a = {{10, 20}, {30, 40}, {50}};

        // Converting the whole 2D array to a String
        System.out.println(Arrays.deepToString(a));
    }
}

Output:

[[10, 20], [30, 40], [50]]

Notice that the rows do not have to be the same length. The last row here has only one value, and deepToString() handles that fine.

If you want the array to look like a grid, with each row on its own line, use a nested loop. The outer loop goes through the rows, and the inner loop goes through the columns of each row:

public class Main {
    public static void main(String[] args) {
        int[][] grid = {
            {1, 2, 3},
            {4, 5, 6},
            {7, 8, 9}
        };

        for (int row = 0; row < grid.length; row++) {
            for (int col = 0; col < grid[row].length; col++) {
                System.out.print(grid[row][col] + " ");
            }
            System.out.println(); // move to the next line after each row
        }
    }
}

Output:

1 2 3
4 5 6
7 8 9

Using grid[row].length for the inner loop, instead of a fixed number, keeps the code safe for rows of different lengths. If you work with matrices a lot, our guide on multiplying matrices in Java uses the same nested loop pattern.

For String arrays, String.join() lets you pick the separator yourself, with no brackets. For object arrays and int, long, or double arrays, a stream can print one value per line in a single statement:

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        String[] fruits = {"apple", "banana", "cherry"};
        int[] scores = {88, 92, 75};

        // String arrays: join with any separator you like
        System.out.println(String.join(", ", fruits));

        // Object, int, long, and double arrays: stream and print one value per line
        Arrays.stream(scores).forEach(System.out::println);
    }
}

Output:

apple, banana, cherry
88
92
75

String.join() only works with text, so it will not accept an int array. For numbers, use the stream or a loop. Streams do not work on char[] or boolean[], so use a loop for those.

Printing a String array works exactly like printing an int array. Every method above works. The only thing that fails is printing the array directly with println, which shows [Ljava.lang.String;@... as we saw earlier. Here is the Arrays.toString() version:

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        String[] a = {"Coding", "Zap", "Tech"}; // one-dimensional String array

        System.out.println("Array values: " + Arrays.toString(a));
    }
}

Output:

Array values: [Coding, Zap, Tech]

Don't get confused by the heading. We are not talking about a big array, but an array of the long data type, which holds bigger whole numbers than int. The same loop works. Just make sure the loop variable is long too:

public class Main {
    public static void main(String[] args) {
        // Declaring one long array
        long[] a = {101L, 202L, 303L, 404L};

        // The loop variable must be long too
        for (long i : a) {
            System.out.print(i + " ");
        }
    }
}

Output:

101 202 303 404

Arrays.toString() has a version for every primitive type, so double, boolean, char, and the rest all print the same way:

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        double[] prices = {9.99, 14.5, 3.0};
        boolean[] answers = {true, false, true};

        System.out.println(Arrays.toString(prices));
        System.out.println(Arrays.toString(answers));
    }
}
[9.99, 14.5, 3.0]
[true, false, true]

Good news: an ArrayList already knows how to print itself. You can pass it straight to println:

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> cities = new ArrayList<>();
        cities.add("Boston");
        cities.add("Denver");
        cities.add("Austin");

        System.out.println(cities); // ArrayList prints nicely on its own
    }
}

Output:

[Boston, Denver, Austin]

How to Check if an Array Contains a Value in Java

Checking whether an array holds a certain value comes up all the time. Picture a college's student system: before you enroll a student in a course section, you need to confirm that the student is actually in your records array. Skip that check and you risk enrolling the wrong person or crashing on missing data. Here are four ways to do it.

Method 1: Use a Loop (Linear Search)

public class Main {
    public static boolean containsValue(int[] digits, int value) {
        for (int element : digits) {
            if (element == value) {
                return true; // found it, stop looking
            }
        }
        return false; // checked every element, no match
    }

    public static void main(String[] args) {
        int[] digits = {1, 2, 3, 4, 5};
        int targetValue = 3;

        if (containsValue(digits, targetValue)) {
            System.out.println("Array contains the value " + targetValue);
        } else {
            System.out.println("Array does not contain the value " + targetValue);
        }
    }
}

Output:

Array contains the value 3

The containsValue() method checks every element, one by one, and returns true as soon as it finds a match. If the loop finishes without a match, it returns false. This is called a linear search. It works on any array, sorted or not, and it is the version most intro courses expect you to write yourself.

For a String array, compare with .equals() instead of ==. The == operator checks whether two variables point to the same object, not whether the text matches. Our guide on comparing strings in Java explains why.

Method 2: Use Arrays.asList() and contains()

Arrays.asList() turns an object array into a List, and every List has a built-in contains() method:

import java.util.Arrays;
import java.util.List;

public class Main {
    public static void containsValue(String[] array, String value) {
        List<String> list = Arrays.asList(array);

        if (list.contains(value)) {
            System.out.println("Array contains the value " + value);
        } else {
            System.out.println("Array does not contain the value " + value);
        }
    }

    public static void main(String[] args) {
        String[] fruits = {"apple", "banana", "jackfruit", "avocado"};

        containsValue(fruits, "cherry");
        containsValue(fruits, "banana");
    }
}

Output:

Array does not contain the value cherry
Array contains the value banana

Watch out: this does not work with int[]. Look at this:

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] numbers = {1, 2, 3, 4, 5};
        Integer[] boxed = {1, 2, 3, 4, 5};

        System.out.println(Arrays.asList(numbers).contains(3)); // int[]
        System.out.println(Arrays.asList(boxed).contains(3));   // Integer[]
    }
}

Output:

false
true

The first line prints false even though 3 is right there. Arrays.asList() cannot unpack a primitive array, so it builds a list with just one item: the whole int[]. Then it looks for 3 in that one-item list and fails. It works on Integer[] because that is an array of objects. For primitive arrays, use a loop, or Method 3 for int, long, and double arrays.

Method 3: Use Arrays.stream() and anyMatch()

Since Java 8, you can turn an array into a stream and ask whether any element matches a condition. This works for object arrays and for int, long, and double arrays (not char[] or boolean[]):

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        String[] colors = {"red", "green", "blue", "yellow"};
        int[] numbers = {1, 2, 3, 4, 5};

        // Objects like String: compare with equals()
        boolean hasBlue = Arrays.stream(colors).anyMatch(c -> c.equals("blue"));

        // Primitives like int: compare with ==
        boolean hasThree = Arrays.stream(numbers).anyMatch(n -> n == 3);

        System.out.println("Contains blue: " + hasBlue);
        System.out.println("Contains 3: " + hasThree);
    }
}

Output:

Contains blue: true
Contains 3: true

For the String array, the lambda uses .equals() because Strings are objects. For the int array, it uses == because int is a primitive type.

One catch: if the array might contain null, c.equals("blue") throws a NullPointerException when it reaches the null. Use Objects.equals(c, "blue") instead, which handles null safely. And if upper and lower case should not matter, use equalsIgnoreCase().

Method 4: Use Arrays.binarySearch() on a Sorted Array

If your array is sorted, Arrays.binarySearch() is much faster than checking every element. It returns the index of the value if it finds it, and a negative number if it does not:

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] ids = {42, 7, 19, 88, 3};

        Arrays.sort(ids); // binarySearch only works on a sorted array
        System.out.println(Arrays.toString(ids));

        int index = Arrays.binarySearch(ids, 19);
        System.out.println("19 found at index " + index);

        int missing = Arrays.binarySearch(ids, 50);
        System.out.println("Search for 50 returned " + missing); // negative means not found
    }
}

Output:

[3, 7, 19, 42, 88]
19 found at index 2
Search for 50 returned -5

The array must be sorted first. On an unsorted array, binarySearch() can give wrong answers without any error. Also, sorting changes the order of your original array, so make a copy first if the order matters. If you want to see how sorting works under the hood, our guide to bubble sort in Java walks through one of the simplest sorting algorithms.

Which Method Should You Use?

SituationBest method
Intro assignment that says "write a method"A loop (Method 1)
String or other object arrayArrays.asList().contains() or a stream
int, long, or double arrayA loop or Arrays.stream().anyMatch()
char, boolean, or other primitive arrayA loop
Large sorted array, many searchesArrays.binarySearch()
It is already an ArrayListlist.contains(value)

How to Reverse an Array or List in Java

Picture your gym class lined up from shortest to tallest. Then the principal walks in and wants the tallest student first. The easy way to flip the line is to swap the first and last students, then the second and second-to-last, and keep going until you meet in the middle. Reversing a list or an array in Java works the same way. Java can do the swapping for you, or you can write it yourself.

Reverse an ArrayList With Collections.reverse()

Import the list classes from java.util, add your elements, and call Collections.reverse():

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> languages = new ArrayList<>(); // declaring an ArrayList

        // Adding elements to the list
        languages.add("Java Program");
        languages.add("C++ Program");
        languages.add("C Program");

        Collections.reverse(languages); // reverses the list in place

        System.out.println("Reversed list: " + languages);
    }
}

Output:

Reversed list: [C Program, C++ Program, Java Program]

Collections.reverse() changes the list itself. It does not return a new list, so there is nothing to store. Just print the same list afterward. One catch: a fixed list made with List.of() cannot be changed, so reversing it throws an UnsupportedOperationException. Copy it into an ArrayList first, as in the next examples.

Reverse a LinkedList

The same method works for any List you can change, including LinkedList:

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> words = new LinkedList<>(); // declaring a LinkedList

        words.add("ZapOne");
        words.add("Hello");
        words.add("Java");

        Collections.reverse(words); // same method works for LinkedList

        System.out.println("Reversed list: " + words);
    }
}

Output:

Reversed list: [Java, Hello, ZapOne]

Reverse a List Without Changing the Original

Sometimes you need the reversed order but also want to keep the original list as it was. There are two ways:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Integer> scores = new ArrayList<>(List.of(70, 85, 92));

        // Option 1: copy first, then reverse the copy
        List<Integer> reversedCopy = new ArrayList<>(scores);
        Collections.reverse(reversedCopy);

        // Option 2 (Java 21+): a reversed view of the same list
        List<Integer> reversedView = scores.reversed();

        System.out.println("Original: " + scores);
        System.out.println("Reversed copy: " + reversedCopy);
        System.out.println("Reversed view: " + reversedView);
    }
}

Output:

Original: [70, 85, 92]
Reversed copy: [92, 85, 70]
Reversed view: [92, 85, 70]

Option 1 makes a real, separate reversed list. Option 2 uses reversed(), which was added in Java 21. It does not copy anything. It gives you a reversed view of the same list, so if the original changes, the view changes too. If your course uses an older version of Java, stick with Option 1.

Reverse an Array in Java

Arrays do not have a built-in reverse method, so you write the swap yourself. This is also a classic homework and interview question. Use two positions, one at each end, and move them toward the middle:

import java.util.Arrays;

public class Main {
    public static void reverse(int[] arr) {
        int left = 0;
        int right = arr.length - 1;

        while (left < right) {
            // swap the two ends
            int temp = arr[left];
            arr[left] = arr[right];
            arr[right] = temp;

            // move both ends toward the middle
            left++;
            right--;
        }
    }

    public static void main(String[] args) {
        int[] heights = {150, 158, 163, 170, 181};
        reverse(heights);
        System.out.println(Arrays.toString(heights));
    }
}

Output:

[181, 170, 163, 158, 150]

The loop stops when left and right meet, so each pair is swapped exactly once. If you kept going to the end of the array, you would swap everything back to where it started.

You may see Collections.reverse(Arrays.asList(arr)) suggested online. That works for object arrays like Integer[], but it quietly does nothing for int[], for the same reason as the contains() trap above:

import java.util.Arrays;
import java.util.Collections;

public class Main {
    public static void main(String[] args) {
        Integer[] boxed = {1, 2, 3, 4};
        Collections.reverse(Arrays.asList(boxed)); // works: Integer[] is an object array
        System.out.println(Arrays.toString(boxed));

        int[] primitive = {1, 2, 3, 4};
        Collections.reverse(Arrays.asList(primitive)); // compiles, but does nothing useful
        System.out.println(Arrays.toString(primitive));
    }
}

Output:

[4, 3, 2, 1]
[1, 2, 3, 4]

Reverse a List Manually (When Built-in Methods Are Not Allowed)

Some assignments ban Collections.reverse(). The same two-position swap works on a List, using get() and set():

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void reverseList(List<String> list) {
        int left = 0;
        int right = list.size() - 1;

        while (left < right) {
            String temp = list.get(left);
            list.set(left, list.get(right));
            list.set(right, temp);
            left++;
            right--;
        }
    }

    public static void main(String[] args) {
        List<String> steps = new ArrayList<>(List.of("wake up", "shower", "breakfast", "class"));
        reverseList(steps);
        System.out.println(steps);
    }
}

Output:

[class, breakfast, shower, wake up]

Array vs ArrayList in Java

Arrays and ArrayLists both store items in order, and you reach each item by its index. The big difference is size. An array's size is fixed when you create it. An ArrayList grows and shrinks as you add and remove items.

Here is where that matters. Say you are keeping a guest list for a party, but you do not know how many people will show up. With an array, you have to guess a size up front. Guess too small and you run out of room. Guess too big and you waste space. With an ArrayList, you just keep adding guests.

This code shows the basic differences side by side:

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        // 1. Declaring and creating
        int[] array = new int[5];                        // size is fixed at 5
        ArrayList<Integer> arrayList = new ArrayList<>(); // no size needed

        // 2. Adding elements
        for (int i = 0; i < array.length; i++) {
            array[i] = i * 10;              // arrays: assign by index
        }
        for (int i = 100; i <= 110; i++) {
            arrayList.add(i);               // ArrayList: add() puts it at the end
        }

        // 3. Reading elements
        System.out.print("Array: ");
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i] + " ");          // square brackets
        }

        System.out.print("\nArrayList: ");
        for (int i = 0; i < arrayList.size(); i++) {
            System.out.print(arrayList.get(i) + " ");  // get(index)
        }

        // 4. Removing: only an ArrayList can shrink
        arrayList.remove(2); // removes the element at index 2 (the value 102)

        System.out.print("\nUpdated ArrayList: ");
        for (int i = 0; i < arrayList.size(); i++) {
            System.out.print(arrayList.get(i) + " ");
        }
        System.out.println();
    }
}

Output:

Array: 0 10 20 30 40
ArrayList: 100 101 102 103 104 105 106 107 108 109 110
Updated ArrayList: 100 101 103 104 105 106 107 108 109 110

What the code shows:

  • Declaring: The array holds the primitive type int. The ArrayList holds Integer, the wrapper class for int, because an ArrayList can only hold objects.
  • Creating: Both use new, but only the array needs a size.
  • Adding: An array gets values by index with =. An ArrayList uses add(), which puts the new value at the end.
  • Reading: An array uses square brackets, like array[i]. An ArrayList uses get(i).
  • Removing: remove(2) took out the element at index 2 (the value 102), and everything after it shifted left. You cannot shrink an array this way.

Key Differences Between Arrays and ArrayLists

FeatureArrayArrayList
SizeFixed when createdGrows and shrinks automatically
What it can holdPrimitives (int, double) and objectsObjects only (Integer, not int)
Where it comes fromBuilt into the Java languageThe Collections Framework in java.util
Get an elementarr[i]list.get(i)
Change an elementarr[i] = xlist.set(i, x)
Number of elementsarr.length (no parentheses)list.size()
PrintingNeeds Arrays.toString()println(list) works directly
Type safetyFixed type from the startSet with generics, like ArrayList<String>
Speed and memoryA bit faster and smaller, especially with primitivesSlightly slower, because each number is stored as an object
Built-in toolsArrays.sort(), Arrays.copyOf(), and a few moreMany methods, plus everything in Collections

Why ArrayList Uses Integer Instead of int (Autoboxing)

An ArrayList can only store objects, so Java gives every primitive type a wrapper class: Integer for int, Double for double, and so on. Since Java 5, Java converts between them automatically. Turning an int into an Integer is called autoboxing, and turning it back is unboxing:

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        int num = 5;                                   // a primitive int
        ArrayList<Integer> list = new ArrayList<>();   // an ArrayList of Integer objects

        list.add(num);          // autoboxing: int 5 becomes an Integer object
        int x = list.get(0);    // unboxing: the Integer turns back into an int

        System.out.println(x + 1);
    }
}

Output:

6

Wrapper classes also come with handy methods like Integer.parseInt() and Integer.compare(). Our guide to Java data types covers primitives and their wrapper classes in more detail.

How an ArrayList Grows

An ArrayList is not magic. Inside, it stores its elements in a regular array. Here is what happens as you add items:

  1. When you add the first element, the ArrayList creates an internal array with room for 10.
  2. New elements go into that array, one after another.
  3. When the array is full and you add one more, the ArrayList creates a new array about 1.5 times bigger, copies every element over, and drops the old one.

That copy step takes time, but it happens rarely, so adding to an ArrayList is still fast on average. If you know roughly how many items you will have, you can skip most of the resizing by giving a starting capacity: new ArrayList<>(500). The list can still grow past that if it needs to.

The internal array keeps its references side by side in memory, which is why get(i) takes the same time for any index, just like arr[i]. The objects themselves live elsewhere in memory, which is one reason a list of Integer uses more memory than an int[].

List vs ArrayList: Which Type Should You Declare?

List is an interface, a set of rules for what a list can do. ArrayList is a class that follows those rules, and LinkedList is another. Most Java code declares the variable as List and creates an ArrayList:

List<String> names = new ArrayList<>();        // the common style
ArrayList<String> names2 = new ArrayList<>();  // also works

Both work the same way. Declaring the variable as List lets you switch to a LinkedList later by changing one word, and it is the style you will see in most Java code.

When to Use an Array and When to Use an ArrayList

  • Use an array when the size is known and will not change (days of the week, a 3x3 game board), when you work with lots of primitive numbers and speed matters, or when the assignment says to.
  • Use an ArrayList when you do not know how many items you will have, when you need to add or remove items, or when you want built-in methods like contains(), remove(), and indexOf().

How to Create a Dynamic Array in Java

A classic data structures assignment asks you to build your own resizable array, basically a mini ArrayList. It is a great way to understand what ArrayList does for you behind the scenes. Here is the plan:

  1. Create a wrapper class, DynamicArray, that stores its elements in a regular array inside it.
  2. Start that internal array at a default size, like 10, and keep adding elements until it is full.
  3. When it is full, create a new array with double the size.
  4. Copy every element from the old array into the new one, then use the new array from now on.
  5. Repeat whenever the array fills up again.
public class Main {
    public static void main(String[] args) {
        DynamicArray arr = new DynamicArray();

        for (int i = 1; i <= 12; i++) {
            arr.add(i); // the 11th add() triggers a resize
        }

        for (int i = 0; i < arr.size(); i++) {
            System.out.print(arr.get(i) + " ");
        }
        System.out.println();
        System.out.println("Size: " + arr.size() + ", capacity: " + arr.capacity());
    }
}

// Custom dynamic array of ints
class DynamicArray {
    private int[] array; // internal array that holds the elements
    private int size;    // how many elements are actually stored

    public DynamicArray() {
        array = new int[10]; // start with room for 10
        size = 0;
    }

    // Adds an element at the end, growing the array if it is full
    public void add(int element) {
        if (size == array.length) {
            resizeArray();
        }
        array[size] = element;
        size++;
    }

    // Returns the element at the given index
    public int get(int index) {
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException("Invalid index: " + index);
        }
        return array[index];
    }

    public int size() {
        return size;
    }

    public int capacity() {
        return array.length;
    }

    // Creates a bigger array and copies the old elements into it
    private void resizeArray() {
        int[] newArray = new int[array.length * 2];         // double the capacity
        System.arraycopy(array, 0, newArray, 0, size);     // copy the old elements
        array = newArray;                                  // use the new array from now on
    }
}

Output:

1 2 3 4 5 6 7 8 9 10 11 12
Size: 12, capacity: 20

Twelve elements went into an array that started with room for 10. On the 11th add(), resizeArray() doubled the capacity to 20 and copied the old values across with System.arraycopy(). The get() method also checks the index, so asking for a spot that was never filled throws a clear IndexOutOfBoundsException instead of quietly returning 0.

We double the size here because it keeps the math simple. Java's own ArrayList grows by about 1.5 times. Either way works, as long as the array grows by a multiple. Growing by just one slot at a time would mean copying the whole array on every single add, which gets slow fast.

Make Your Dynamic Array Work With Any Type (Generics)

The version above only holds int. With generics, one class can hold any object type you choose, like Integer or String, and the compiler makes sure you only add that type. That check at compile time is called type safety. It is the same thing that stops you from adding a String to an ArrayList<Integer>.

public class Main {
    public static void main(String[] args) {
        // A DynamicArray of Integers
        DynamicArray<Integer> nums = new DynamicArray<>();
        for (int i = 1; i <= 10; i++) nums.add(i * 10);
        nums.print();

        for (int i = 11; i <= 15; i++) nums.add(i * 10); // grows past 10
        nums.print();

        // The same class works for Strings
        DynamicArray<String> words = new DynamicArray<>();
        words.add("Using");
        words.add("generics");
        words.add("is easier");
        words.add("than expected.");
        words.print();
    }
}

class DynamicArray<T> {
    private T[] array;
    private int size;

    @SuppressWarnings("unchecked")
    public DynamicArray() {
        array = (T[]) new Object[10]; // Java cannot do new T[10], so we cast
        size = 0;
    }

    public void add(T element) {
        if (size == array.length) {
            resizeArray();
        }
        array[size] = element;
        size++;
    }

    public T get(int index) {
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException("Invalid index: " + index);
        }
        return array[index];
    }

    public int size() {
        return size;
    }

    @SuppressWarnings("unchecked")
    private void resizeArray() {
        T[] newArray = (T[]) new Object[array.length * 2];
        System.arraycopy(array, 0, newArray, 0, size);
        array = newArray;
    }

    public void print() {
        System.out.print("Dynamic array: ");
        for (int i = 0; i < size; i++) {
            System.out.print(array[i] + " ");
        }
        System.out.println();
    }
}

Output:

Dynamic array: 10 20 30 40 50 60 70 80 90 100
Dynamic array: 10 20 30 40 50 60 70 80 90 100 110 120 130 140 150
Dynamic array: Using generics is easier than expected.

One line needs explaining. Java does not let you write new T[10], so we create an Object array and cast it to T[]. The compiler warns that this cast is "unchecked," and @SuppressWarnings("unchecked") tells it we know what we are doing. This is the standard workaround, and Java's own ArrayList also stores its elements in an Object array.

Handling resizing and copying by hand is easy to get wrong. If you are stuck on a custom data structure assignment, our Java homework help team can walk you through it.

How to Copy an ArrayList in Java: Shallow Copy vs Deep Copy

Copying a list sounds simple, but it is one of the sneakiest sources of bugs in Java assignments. There are three different things people mean by "copy," and they behave very differently.

Using = Does Not Copy Anything

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> original = new ArrayList<>(List.of("Java", "Python"));
        List<String> copy = original;   // NOT a copy: both names point to one list

        copy.add("C++");

        System.out.println("Original: " + original);
        System.out.println("Copy: " + copy);
    }
}

Output:

Original: [Java, Python, C++]
Copy: [Java, Python, C++]

We added "C++" to copy, and it showed up in original too. That is because = does not create a new list. It just gives the same list a second name.

Shallow Copy: A New List With the Same Objects

A shallow copy creates a new list, but the elements inside are not copied. Both lists point to the same objects.

Java Shallow Copy Technique

You get a shallow copy with new ArrayList<>(original) or addAll(). The clone() method of ArrayList does the same, but only on a variable declared as ArrayList, and it needs a cast. Here is what a shallow copy means with a list of students:

import java.util.ArrayList;
import java.util.List;

class Student {
    String name;
    int grade;

    Student(String name, int grade) {
        this.name = name;
        this.grade = grade;
    }

    @Override
    public String toString() {
        return name + "=" + grade;
    }
}

public class Main {
    public static void main(String[] args) {
        List<Student> original = new ArrayList<>();
        original.add(new Student("Ana", 85));
        original.add(new Student("Ben", 72));

        // Shallow copy: a new list, but the SAME Student objects inside
        List<Student> copy = new ArrayList<>(original);

        copy.add(new Student("Cam", 90)); // only changes the copy's list
        copy.get(0).grade = 100;          // changes Ana in BOTH lists

        System.out.println("Original: " + original);
        System.out.println("Copy: " + copy);
    }
}

Output:

Original: [Ana=100, Ben=72]
Copy: [Ana=100, Ben=72, Cam=90]

Adding Cam to the copy did not touch the original, because they are two separate lists. But changing Ana's grade through the copy changed it in the original too, because both lists hold the exact same Ana object.

Deep Copy: A New List With New Objects

A deep copy creates a new list and a new copy of every object inside it. Changes to one list never affect the other.

Java Deep Copy Technique

The cleanest way is a copy constructor: a constructor that takes another object of the same class and copies its values. Then loop through the original list and copy each element:

import java.util.ArrayList;
import java.util.List;

class Student {
    String name;
    int grade;

    Student(String name, int grade) {
        this.name = name;
        this.grade = grade;
    }

    // Copy constructor: builds a brand-new Student with the same values
    Student(Student other) {
        this.name = other.name;
        this.grade = other.grade;
    }

    @Override
    public String toString() {
        return name + "=" + grade;
    }
}

public class Main {
    public static void main(String[] args) {
        List<Student> original = new ArrayList<>();
        original.add(new Student("Ana", 85));
        original.add(new Student("Ben", 72));

        // Deep copy: a new list AND a new Student for every element
        List<Student> copy = new ArrayList<>();
        for (Student s : original) {
            copy.add(new Student(s));
        }

        copy.get(0).grade = 100; // only the copy changes

        System.out.println("Original: " + original);
        System.out.println("Copy: " + copy);
    }
}

Output:

Original: [Ana=85, Ben=72]
Copy: [Ana=100, Ben=72]

This time, only the copy changed. Ana still has 85 in the original.

You may also see deep copies made by overriding clone() with the Cloneable interface. It works, but it is easy to get wrong, and many Java developers prefer copy constructors because they are simpler to read and test.

When a Shallow Copy Is Enough

If the list holds immutable objects, meaning objects that can never change after they are created, a shallow copy is all you need. String, Integer, Double, and the other wrapper classes are all immutable. Nobody can change the "Java" String inside your list, so it does not matter that both lists share it. For a list of Strings, new ArrayList<>(original) is the right tool.

Copying Arrays

Arrays have their own copy tools: Arrays.copyOf(), clone(), and System.arraycopy(). For a one-dimensional array of primitives, each of them makes a fully independent copy:

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] scores = {70, 85, 92};

        int[] a = Arrays.copyOf(scores, scores.length); // copy of the whole array
        int[] b = scores.clone();                        // same result
        int[] c = Arrays.copyOf(scores, 5);              // longer copy, extra slots are 0

        a[0] = 0;
        System.out.println(Arrays.toString(scores));
        System.out.println(Arrays.toString(b));
        System.out.println(Arrays.toString(c));

        // Careful: clone() on a 2D array copies only the outer array
        int[][] grid = {{1, 2}, {3, 4}};
        int[][] gridCopy = grid.clone();
        gridCopy[0][0] = 99;
        System.out.println(Arrays.deepToString(grid)); // the original changed too
    }
}

Output:

[70, 85, 92]
[70, 85, 92]
[70, 85, 92, 0, 0]
[[99, 2], [3, 4]]

The last line is the trap. A 2D array is an array of rows, and clone() only copies the outer array. Both grids still share the same rows, so changing gridCopy[0][0] changed the original too. To copy a 2D array properly, copy each row:

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[][] grid = {{1, 2}, {3, 4}};

        int[][] gridCopy = new int[grid.length][];
        for (int i = 0; i < grid.length; i++) {
            gridCopy[i] = grid[i].clone(); // copy each row
        }

        gridCopy[0][0] = 99;
        System.out.println(Arrays.deepToString(grid));
        System.out.println(Arrays.deepToString(gridCopy));
    }
}

Output:

[[1, 2], [3, 4]]
[[99, 2], [3, 4]]

Shallow Copy vs Deep Copy at a Glance

CriteriaShallow copyDeep copy
What gets copiedThe list only. The objects are shared.The list and every object inside it
Are the two independent?No. Changing an object shows up in both.Yes
Memory useLowHigher
SpeedFastSlower, especially for big lists
Risk of surprise changesHigh with mutable objectsLow
EffortOne lineNeeds a copy constructor or copy method
Good forImmutable elements like String and IntegerMutable objects like Student

Deep copies show up in real software more than you might think. Features like undo in text editors, "duplicate" buttons in apps, and snapshots of data shared between threads often use deep copies.

Challenges of Deep Copying

  • Nested objects: if a Student holds a list of Course objects, a true deep copy has to copy the courses too, all the way down.
  • Objects that point to each other: if two objects refer to each other, a copy method that follows every reference can loop forever. You have to keep track of what you already copied.
  • Cost: copying thousands of objects takes time and memory. Only deep copy when the copy really must be independent.

Why We Use Arrays in Java

Before we wrap up, it is worth asking why arrays matter so much.

Uses Of Array

  • To store items: An array keeps many values of the same type under one name. You do not need a separate variable for every score or every name. When you need a collection that grows, a dynamic array or an ArrayList builds on the same idea.
  • Easy access: You can reach any element directly by its index. The first element is index 0, so the second is index 1, and so on. Reaching an element by index takes the same time no matter how big the array is.
  • Performance: Arrays have almost no extra overhead, especially for primitive types. That is why so many other data structures, including ArrayList, are built on top of arrays.
  • They are the foundation: Stacks, queues, heaps, and matrices are all commonly built with arrays, so getting comfortable with them now makes data structures much easier later.

Common Array Mistakes Students Make

1. Going one past the end. Valid indexes run from 0 to length - 1. Writing i <= arr.length in a loop, or using an index equal to the length, crashes the program:

public class Main {
    public static void main(String[] args) {
        int[] scores = new int[3];
        scores[3] = 90; // valid indexes are 0, 1, 2
    }
}
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
    at Main.main(Main.java:4)

Our guide to common Java errors covers this exception and how to fix it in more detail.

2. Printing the array directly. System.out.println(arr) prints the type and a hash code. Use Arrays.toString(arr) or a loop, as shown above.

3. Mixing up length, length(), and size(). Arrays use a field, Strings use a method, and ArrayLists use a different method:

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3};
        String word = "hello";
        List<String> list = new ArrayList<>(List.of("a", "b"));

        System.out.println(arr.length);    // array: a field, no parentheses
        System.out.println(word.length()); // String: a method
        System.out.println(list.size());   // ArrayList: size()
    }
}
3
5
2

4. Comparing arrays with ==. The == operator checks whether two variables point to the same array, not whether the values match:

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] a = {1, 2, 3};
        int[] b = {1, 2, 3};

        System.out.println(a == b);              // compares memory locations
        System.out.println(Arrays.equals(a, b)); // compares the contents
    }
}
false
true

Use Arrays.equals() for 1D arrays and Arrays.deepEquals() for 2D arrays.

5. Calling Arrays.asList() on an int[]. It builds a list with a single item (the whole array), so contains() and Collections.reverse() quietly give the wrong result. Use Integer[], a loop, or a stream.

6. Removing a number from an ArrayList of Integers. remove(2) removes the element at index 2, not the value 2. To remove a value, wrap it in Integer.valueOf():

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Integer> nums = new ArrayList<>(List.of(10, 2, 20, 30));
        nums.remove(2);                   // removes INDEX 2, which holds 20
        System.out.println(nums);

        nums = new ArrayList<>(List.of(10, 2, 20, 30));
        nums.remove(Integer.valueOf(2));  // removes the VALUE 2
        System.out.println(nums);
    }
}
[10, 2, 30]
[10, 20, 30]

7. Thinking = copies an array or list. It only creates a second name for the same object. Use one of the copy methods from the copy section.

Conclusion

Printing an array is usually the first array task you face in Java, and now you have five ways to do it: a for-each loop, a normal for loop, Arrays.toString(), Arrays.deepToString() for 2D arrays, and String.join() or streams. Use a loop when you need a custom format, and Arrays.toString() when you just want to see what is inside.

Takeaways:

  • println(arr) prints the array's type and hash code, not its values.
  • Loops give you full control over the format, and they work for every type, including long.
  • Use Arrays.toString() for 1D arrays and Arrays.deepToString() for 2D arrays.
  • To check for a value, use a loop or a stream. Arrays.asList().contains() only works on object arrays, and binarySearch() needs a sorted array.
  • Reverse a list with Collections.reverse(), and reverse an array by swapping from both ends.
  • Arrays have a fixed size and can hold primitives. ArrayLists grow as needed and hold only objects.
  • new ArrayList<>(list) makes a shallow copy. A deep copy also copies every object inside.

Still stuck on an array assignment? Our online Java tutors can go through your code with you one-on-one.

Get Programming Help Now

Frequently Asked Questions

1. Why does my Java array print as [I@ followed by random characters?

You are printing the array object itself, not its values. Java shows a type code ([I means int array), an @ sign, and a hash code. Wrap the array in Arrays.toString() or print it with a loop.

2. What is the easiest way to print an array in Java?

Import java.util.Arrays and write System.out.println(Arrays.toString(arr));. It prints the whole array in one line, like [10, 20, 30]. For a 2D array, use Arrays.deepToString(arr).

3. How do I print a 2D array in Java?

Use Arrays.deepToString(arr) to print it in one line. To print it as a grid, use a nested loop: the outer loop goes through the rows, the inner loop prints each value in the row, and you print a new line after each row.

4. How do I print an array without brackets and commas?

Use a loop and print each value followed by a space. For a String array, String.join(" ", arr) also works and lets you choose any separator.

5. How do I check if an array contains a value in Java?

Loop through the array and compare each element, or use Arrays.stream(arr).anyMatch() for object, int, long, or double arrays. For object arrays like String[], Arrays.asList(arr).contains(value) also works. If the array is sorted, Arrays.binarySearch() is the fastest option.

6. Why does Arrays.asList(myIntArray).contains() always return false?

Arrays.asList() cannot unpack a primitive array like int[]. It creates a list with one item, the whole array, so the value is never found. Use an Integer[], a loop, or Arrays.stream() instead.

7. How do I reverse an array in Java?

Swap the first and last elements, then the second and second-to-last, moving inward until the two positions meet. For a List, just call Collections.reverse(list).

8. What is the difference between an array and an ArrayList in Java?

An array has a fixed size and can hold primitives like int. An ArrayList grows and shrinks automatically but can only hold objects like Integer. Arrays use arr[i] and arr.length. ArrayLists use get(i) and size().

9. Can you change the size of an array in Java?

No. Once an array is created, its size is fixed. To get a bigger one, create a new array and copy the values over, for example with Arrays.copyOf(arr, newLength), or use an ArrayList instead.

10. What is the difference between length, length(), and size() in Java?

length (no parentheses) is a field that gives the size of an array. length() is a method that gives the number of characters in a String. size() is a method that gives the number of elements in an ArrayList or other collection.

11. Does new ArrayList<>(oldList) make a deep copy?

No. It makes a shallow copy: a new list that holds the same objects. That is fine for Strings and Integers, which cannot change. For mutable objects, create a new copy of each element, for example with a copy constructor.

12. How do I compare two arrays in Java?

Use Arrays.equals(a, b) to compare the values of two 1D arrays, and Arrays.deepEquals(a, b) for 2D arrays. The == operator only checks whether both variables point to the same array.