Print an Array in Java: toString(), deepToString() and Loops

Learn how to print an array in Java with Arrays.toString() and deepToString(), why println shows [I@ codes, custom formats, records and char[] output.

Decision chart for printing a Java array with Arrays.toString, Arrays.deepToString, a loop or String.join

To print an array in Java, we pass it to Arrays.toString() for a one-dimensional array or to Arrays.deepToString() for a 2D or nested array, because printing the array variable itself shows only its type and hash code, such as [I@1b6d3586. Both methods return a String like [90, 75, 82] that we can print, log or put in an exception message.

We print arrays mostly while debugging, in log statements and in test failure messages, where we need to see the actual values. For screens and reports, we build our own format with a loop, a stream or String.join().

The following example compares printing the array variable with the two Arrays methods.

int[] scores = {90, 75, 82};
String raw = String.valueOf(scores);             // "[I@" followed by a hash code
String flat = Arrays.toString(scores);           // "[90, 75, 82]"
int[][] board = {{1, 0}, {0, 1}};
String nested = Arrays.deepToString(board);      // "[[1, 0], [0, 1]]"

We can see that only the Arrays methods show the values. The sections explain where the [I@ text comes from, cover 2D arrays and custom formats, and fix arrays inside records and char[] output.

1. Why Printing an Array Shows [I@1b6d3586

Arrays are objects, but they do not override toString(). They inherit Object.toString(), which returns the class name, an @ sign and the hash code in hex. The class name of an array is a short JVM code, so System.out.println(scores) prints something like [I@1b6d3586 instead of the numbers.

Decision chart for printing a Java array with Arrays.toString, Arrays.deepToString, a loop or String.join
Pick the method by the shape of the array and the output format we need

The text before the @ tells us the array type. One [ means one dimension, and the letter after it names the element type.

Array typePrinted class nameExample output
int[][I[I@1b6d3586
double[][D[D@4554617c
boolean[][Z[Z@74a14482
String[][Ljava.lang.String;[Ljava.lang.String;@1540e19d
int[][][[I[[I@677327b6
String intType = new int[0].getClass().getName();         // "[I"
String textType = new String[0].getClass().getName();     // "[Ljava.lang.String;"
String gridType = new int[0][0].getClass().getName();     // "[[I"

The hash code part changes on every run, so it tells us nothing about the contents. Whenever we see this pattern in a log, an array reached string conversion without Arrays.toString().

2. Printing a One-Dimensional Array With Arrays.toString()

The Arrays.toString() method has an overload for every primitive array type and one for Object[]. It puts the elements in square brackets, separated by a comma and a space, and it handles null and empty arrays without an exception.

double[] prices = {9.99, 4.5};
String p = Arrays.toString(prices);                // "[9.99, 4.5]"
boolean[] flags = {true, false};
String f = Arrays.toString(flags);                 // "[true, false]"
String[] fruits = {"apple", null, "kiwi"};
String s = Arrays.toString(fruits);                // "[apple, null, kiwi]"
int[] none = null;
String n = Arrays.toString(none);                  // "null"
String empty = Arrays.toString(new int[0]);        // "[]"

For object arrays, each element is printed with its own toString(). Records and most JDK classes such as LocalDate already have a readable toString(), so an array of them prints well.

record Item(String name, int qty) {}
Item[] cart = {new Item("milk", 2), new Item("bread", 1)};
String items = Arrays.toString(cart);   // "[Item[name=milk, qty=2], Item[name=bread, qty=1]]"

In a shopping cart service, a log line such as log.debug(“Cart items {}”, Arrays.toString(cart)) shows every item in one line. Printing cart without the call would log only [LItem;@ and a hash code, which is useless when we investigate a wrong order total.

3. Printing a 2D Array With Arrays.deepToString()

A 2D array is an array of arrays, so Arrays.toString() prints the outer array and calls toString() on each row, which brings back the hash codes. Arrays.deepToString() goes into every nested array, at any depth.

int[][] seats = {{1, 0, 1}, {0, 0, 1}};
String shallow = Arrays.toString(seats);       // "[[I@..., [I@...]", row hash codes
String deep = Arrays.deepToString(seats);      // "[[1, 0, 1], [0, 0, 1]]"
String[][] menu = {{"tea", "coffee"}, {"cake"}};
String jagged = Arrays.deepToString(menu);     // "[[tea, coffee], [cake]]"

The deepToString() method accepts only an Object[], so it works for int[][] but not for a one-dimensional int[]. An int[][] is an array of int[] objects, which makes it an Object[], whereas an int[] holds primitives.

// does not compile: int[] cannot be converted to Object[]
int[] ids = {1, 2};
String text = Arrays.deepToString(ids);

For a grid shown on the console, one line per row reads better than the single-line format. We loop over the rows and print each row with Arrays.toString().

int[][] seats = {{1, 0, 1}, {0, 0, 1}};
for (int[] row : seats) {
    System.out.println(Arrays.toString(row));
}
[1, 0, 1]
[0, 0, 1]

4. Printing Elements in a Custom Format

When the brackets and commas do not fit, for example in a CSV line, a report or a UI label, we format the elements ourselves. The right tool depends on the element type.

  • A classic for loop gives us the index, which we need for output such as 1. apple.
  • The enhanced for loop is shorter when we need only the values.
  • String.join() joins a String[] with any separator, and the string array join guide covers it in detail.
  • Arrays.stream() with mapToObj() and Collectors.joining() formats primitive arrays, which String.join() does not accept.
String[] tasks = {"build", "test", "deploy"};
for (int i = 0; i < tasks.length; i++) {
    System.out.println((i + 1) + ". " + tasks[i]);
}
1. build
2. test
3. deploy
String[] tasks = {"build", "test", "deploy"};
String pipeline = String.join(" -> ", tasks);       // "build -> test -> deploy"
int[] ports = {8080, 8443, 9000};
String csv = Arrays.stream(ports).mapToObj(String::valueOf).collect(Collectors.joining(","));   // "8080,8443,9000"
String[] steps = {"build", "test", "deploy"};
Arrays.stream(steps).forEach(System.out::println);
build
test
deploy

Printing one element per line with forEach() is fine for a quick check, but for logs we build one String and log it once, so the values stay together in one log entry.

5. Arrays Inside Records and Classes

The array problem comes back when an array is a field. A record generates toString() for us, but for an array component it prints the hash code, because the generated method calls the array’s own toString().

record Student(String name, int[] marks) {}
Student s = new Student("Lokesh", new int[] {80, 92});
String text = s.toString();          // "Student[name=Lokesh, marks=[I@...]"

We fix it by overriding toString() in the record and calling Arrays.toString() for the array field. The same applies to classes, and the ToStringBuilder from Apache Commons Lang also prints the contents of array fields.

record Report(String title, int[] totals) {
    @Override
    public String toString() {
        return "Report[title=" + title + ", totals=" + Arrays.toString(totals) + "]";
    }
}
Report r = new Report("Q1", new int[] {120, 95});
String line = r.toString();          // "Report[title=Q1, totals=[120, 95]]"

6. Printing char[] and byte[] Arrays

Two array types behave differently. PrintStream has a println(char[]) overload that prints the characters as text, but string concatenation does not use it, so the same array prints as text in one line and as a hash code in the next.

char[] code = {'J', 'a', 'v', 'a'};
System.out.println(code);                     // prints Java
String joined = "Code: " + code;              // "Code: [C@" followed by a hash code
String asText = "Code: " + new String(code);  // "Code: Java"
String asList = Arrays.toString(code);        // "[J, a, v, a]"

A byte[] holds either encoded text or binary data such as a hash. For text, we decode it with a charset, and the article on converting a byte array to a String covers the details. For binary data, HexFormat (Java 17) prints the bytes in hex, which is how checksums are shown.

byte[] data = "Hi".getBytes(StandardCharsets.UTF_8);
String numbers = Arrays.toString(data);                       // "[72, 105]"
String text = new String(data, StandardCharsets.UTF_8);       // "Hi"
String hex = HexFormat.of().formatHex(data);                  // "4869"

7. Print an Array in Java, Method by Method

All the options return or print the same values, and they differ in the output format and in the array shapes they accept.

NeedUseExample output
Quick look at a 1D arrayArrays.toString(a)[1, 2]
2D or nested arraysArrays.deepToString(a)[[1, 2], [3]]
Custom separator for String[]String.join(“, “, a)1, 2
Custom separator for int[]Arrays.stream(a) with Collectors.joining()1|2
Index or extra text per elementfor loop1. …
Apache Commons Lang already in the projectArrayUtils.toString(a){1,2}

The last row prints braces and no spaces, which differs from the JDK format. We use it only when the project already depends on Commons Lang, because the JDK methods cover the same needs.

int[] pair = {1, 2};
String commons = ArrayUtils.toString(pair);          // "{1,2}"

8. Printing Arrays FAQs

Most questions about printing arrays start with output that does not look as expected, such as brackets we do not want or values in the wrong order.

8.1. How do we print an array without brackets and commas?

We join the elements with the separator we want. String.join(” “, words) works for a String[], and Arrays.stream(nums).mapToObj(String::valueOf).collect(Collectors.joining(” “)) works for an int[].

8.2. Why does a List print its values but an array does not?

Because ArrayList and the other collections override toString(), and arrays do not. Arrays.asList(stringArray) wraps an object array in a list, so printing the list also shows the values.

8.3. How do we print a 2D array as a matrix?

We loop over the rows and print each row on its own line with Arrays.toString(row), as in section 3. For aligned columns, we format each value with String.format(“%4d”, value).

8.4. How do we print an array in reverse order?

We loop from length – 1 down to 0, or we reverse a copy first and print it with Arrays.toString(). The article on how to reverse an array shows both ways.

9. Conclusion

Printing an array variable calls Object.toString(), so we get a type code such as [I and a hash code. Arrays.toString() prints a one-dimensional array of any type, and Arrays.deepToString() prints 2D and nested arrays.

For our own formats, loops, String.join() and Collectors.joining() give full control. Records and classes with array fields need a toString() that calls Arrays.toString(), and char[] and byte[] print best after we turn them into a String.

10. References

Happy Learning !!

Source Code on Github

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.