Array Basics

Su Suriya Ravichandran Updated 14 Nov 2026
7 min read ·Lesson 64 of 133

An array is a fixed-size, ordered container that holds elements of a single type, stored in one contiguous block of memory and accessed by numeric index. Arrays are the most basic data structure in Java: ArrayList, String, HashMap, and many other classes are built on arrays internally. This lesson looks closely at how arrays are declared, created, stored, and copied, building on the introduction in the Java Arrays lesson.

How an Array Is Stored

int[] scores = {72, 85, 90, 64};
scores (reference, on the stack)
   │
   ▼  heap
┌────────┬────┬────┬────┬────┐
│length=4│ 72 │ 85 │ 90 │ 64 │
└────────┴────┴────┴────┴────┘
  header   [0]  [1]  [2]  [3]
  • The array is an object on the heap. The variable scores holds a reference to it.
  • Elements sit next to each other in memory. To find scores[i], the JVM computes "start of the data + i × element size". That is why access by index takes the same time for any position, and why arrays are very fast.
  • The length is stored with the array and never changes.

Declaring Array Variables

int[] numbers;          // preferred: the type is "int array"
String[] names;
double[][] grid;        // array of arrays

int legacy[];           // C-style: legal, but splits the type across the line
int[] a, b;             // both a and b are int[]
int c[], d;             // c is int[], d is a plain int: a classic trap

Declaring a variable creates no array. Until it is assigned, a local array variable cannot be used, and an array field is null.

Creating Arrays

With new and a size

int[] counts = new int[5];          // 5 ints
String[] labels = new String[3];    // 3 String references
boolean[] seen = new boolean[100];

The size can be any int expression evaluated at runtime:

int n = Integer.parseInt(args[0]);
double[] samples = new double[n];

A negative size throws NegativeArraySizeException. A size of zero is legal and produces an empty array.

With an initializer

int[] primes = {2, 3, 5, 7, 11};
String[] days = {"Mon", "Tue", "Wed"};

The size is inferred from the number of values. The short brace form is allowed only in a declaration. Elsewhere, use new Type[]{...}:

int[] values;
values = new int[]{1, 2, 3};             // correct
printAll(new String[]{"a", "b"});        // passing an array literal to a method

You cannot give both a size and values: new int[3]{1, 2, 3} is a compile error.

Default Values

Every element of an array created with new starts with the default value of its type:

Element typeDefault
byte, short, int, long0
float, double0.0
char'\u0000' (the null character)
booleanfalse
Any reference typenull

This differs from local variables, which have no defaults. An array of objects holds references, and they all start as null:

String[] names = new String[3];
System.out.println(names[0]);              // null
System.out.println(names[0].length());     // NullPointerException

StringBuilder[] buffers = new StringBuilder[2];
buffers[0] = new StringBuilder();          // each element must be created separately
buffers[1] = new StringBuilder();

new StringBuilder[2] creates an array with room for two references. It does not create any StringBuilder objects.

Reading and Writing Elements

double[] temps = new double[7];
temps[0] = 31.5;
temps[6] = 29.0;
double first = temps[0];
temps[3] += 1.5;           // compound assignment works on elements
temps[4]++;                // so does increment

Valid indexes run from 0 to length - 1. Any other index throws ArrayIndexOutOfBoundsException, whose message names both the bad index and the length:

java.lang.ArrayIndexOutOfBoundsException: Index 7 out of bounds for length 7

The index must be an int (or a type that widens to int: byte, short, char). A long index requires a cast.

Filling and Generating Arrays

import java.util.Arrays;

int[] zeros = new int[5];
Arrays.fill(zeros, -1);                         // [-1, -1, -1, -1, -1]

int[] squares = new int[6];
Arrays.setAll(squares, i -> i * i);             // [0, 1, 4, 9, 16, 25]

String[] seats = new String[4];
Arrays.setAll(seats, i -> "A" + (i + 1));       // [A1, A2, A3, A4]

Arrays.fill puts one value in every slot. Arrays.setAll computes each element from its index using a lambda.

Note that Arrays.fill with an object puts the same reference in every slot:

List<String>[] buckets = new List[3];
Arrays.fill(buckets, new ArrayList<>());   // all three slots share ONE list
buckets[0].add("x");
System.out.println(buckets[2]);            // [x]

Arrays.setAll(buckets, i -> new ArrayList<>());   // a separate list per slot

(Arrays of generic types such as List<String>[] produce an unchecked warning; prefer List<List<String>> in real code.)

Arrays Are Objects

Every array is an object, so it has a class, can be stored in an Object variable, and inherits Object's methods:

int[] a = {1, 2, 3};
Object o = a;
System.out.println(a.getClass().getSimpleName());   // int[]

Array types are not written as classes; they are created by the JVM for each element type (int[], String[], int[][]).

Assignment copies the reference

int[] original = {1, 2, 3};
int[] alias = original;      // both variables refer to one array
alias[0] = 100;
System.out.println(original[0]);   // 100

Copying the contents

int[] copy1 = original.clone();                         // full copy
int[] copy2 = Arrays.copyOf(original, original.length); // full copy
int[] bigger = Arrays.copyOf(original, 5);              // [100, 2, 3, 0, 0]
int[] middle = Arrays.copyOfRange(original, 1, 3);      // [2, 3]

int[] target = new int[5];
System.arraycopy(original, 0, target, 2, 3);            // [0, 0, 100, 2, 3]

System.arraycopy(src, srcPos, dest, destPos, length) copies into an existing array and is what most copy methods use internally.

All of these are shallow copies. For an array of objects, the new array holds the same references:

StringBuilder[] src = { new StringBuilder("a") };
StringBuilder[] cp = src.clone();
cp[0].append("!");
System.out.println(src[0]);     // a!   (same StringBuilder object)

Comparing and printing

== and equals on arrays compare identity, not contents. Arrays provides content-based versions:

int[] x = {1, 2, 3};
int[] y = {1, 2, 3};

System.out.println(x == y);                    // false
System.out.println(x.equals(y));               // false (Object.equals)
System.out.println(Arrays.equals(x, y));       // true
System.out.println(Arrays.toString(x));        // [1, 2, 3]
System.out.println(x);                         // [I@1b6d3586  (type code + hash)
System.out.println(Arrays.hashCode(x));        // content-based hash

For nested arrays, use Arrays.deepEquals and Arrays.deepToString.

Array Covariance and ArrayStoreException

Because a String is an Object, Java allows a String[] to be assigned to an Object[] variable. This is called array covariance:

Object[] things = new String[2];   // compiles
things[0] = "text";                // fine
things[1] = 42;                    // compiles, but throws ArrayStoreException at runtime

The array remembers that it was created as a String[] and checks every store. This runtime check is one reason generic collections like List<String>, which are checked at compile time, are generally safer than arrays of objects.

Arrays Versus ArrayList

ArrayArrayList
SizeFixed at creationGrows and shrinks automatically
PrimitivesStored directly (int[])Must be boxed (List<Integer>)
MemoryMinimal overheadExtra per object; boxed numbers cost much more
Type safetyRuntime store check for object arraysCompile-time generics
Syntaxa[i], a.lengthlist.get(i), list.size()
Utility methodsStatic methods in ArraysInstance methods: add, remove, contains, ...

Use arrays for fixed-size data, numeric computation, performance-critical code, and APIs that require them (main(String[] args), String.split, byte buffers). Use ArrayList for most collections whose size changes. Converting between them:

String[] arr = {"b", "a", "c"};
List<String> list = new ArrayList<>(Arrays.asList(arr));   // array → modifiable list
String[] back = list.toArray(new String[0]);              // list → array

Practical Example: Exam Results

import java.util.Arrays;

public class ExamResults {
    public static void main(String[] args) {
        String[] students = {"Ada", "Linus", "Grace", "Alan", "Margaret"};
        int[] marks = new int[students.length];          // one slot per student, all 0

        int[] entered = {78, 92, 45, 66, 88};
        System.arraycopy(entered, 0, marks, 0, entered.length);

        char[] grades = new char[marks.length];
        for (int i = 0; i < marks.length; i++) {
            grades[i] = marks[i] >= 85 ? 'A' : marks[i] >= 70 ? 'B' : marks[i] >= 50 ? 'C' : 'F';
        }

        int[] sorted = marks.clone();                    // keep the original order intact
        Arrays.sort(sorted);
        int median = sorted[sorted.length / 2];

        System.out.println("Marks:  " + Arrays.toString(marks));
        System.out.println("Grades: " + Arrays.toString(grades));
        System.out.println("Sorted: " + Arrays.toString(sorted));
        System.out.println("Median: " + median);
        System.out.println("Top:    " + students[indexOfMax(marks)]);
    }

    static int indexOfMax(int[] values) {
        int best = 0;
        for (int i = 1; i < values.length; i++) {
            if (values[i] > values[best]) best = i;
        }
        return best;
    }
}

Output:

Marks:  [78, 92, 45, 66, 88]
Grades: [B, A, F, C, A]
Sorted: [45, 66, 78, 88, 92]
Median: 78
Top:    Linus

marks is sized from students.length, so the two arrays always match. System.arraycopy loads the entered values into it. The grades go into a char[], whose elements start at '\u0000' until assigned. The program sorts a clone, because Arrays.sort rearranges the array in place; sorting marks directly would break the link between each mark and its student. indexOfMax returns a position rather than a value, so the same index can look up the student's name in the parallel array.

0 Comments

Reviewed before they appear

No comments yet.

Java
Java Introduction Java Features Java Syntax Java Comments Java Statements Java Output Java Variables Java Data Types Java Type Casting Java Operators Java Strings Java Numbers Java Math Java Booleans Java User Input Java Conditions Java If Else Java Switch Java While Loop Java For Loop Java Break Java Continue Java Arrays Java Methods Java Parameters Java Return Values Java Scope Java Recursion Install Java Install JDK Java IDEs Java JDK Java JRE Java JVM Java Compiler Java File Compile Java Run Java JShell Basics Java Packages Java OOP Java Classes Java Objects Class Methods Constructors Constructor Overload this Keyword static Keyword final Keyword Encapsulation Inheritance Polymorphism Abstraction Access Modifiers super Keyword Method Overload Method Override Instanceof Operator Nested Classes Inner Classes Anonymous Classes Enums Records Array Basics Array Length Loop Arrays Multidimensional Arrays Array Copy Array Sorting Array Search ArrayList LinkedList HashSet TreeSet HashMap TreeMap Queue Interface Deque Interface Stack Class Iterator List Interface Set Interface Map Interface Collections Class Comparable Comparator Generics Basics Generic Classes Generic Methods Wildcards String Basics String Length String Methods String Concatenation String Compare String Search String Replace String Split String Format StringBuilder StringBuffer Text Blocks Regular Expressions Number Types Integer Class Float Class Double Class BigInteger BigDecimal Math Class Random Numbers Number Format Date Basics LocalDate LocalTime LocalDateTime ZonedDateTime Date Formatting Date Parsing Period Class Duration Class Java Instant Class Java Exception Basics Java try...catch Java Multiple Catch Blocks Java finally Block Java throw Keyword Java throws Keyword Java Checked Exceptions Java Unchecked Exceptions Java Custom Exceptions Java try-with-resources Java Exception Chaining
Ask about this post
AI Ask about this post

Ask questions about Array Basics and get answers drawn from it.

Signed-in readers only.