For the complete documentation index, see llms.txt. This page is also available as Markdown.

Tensors and NDArrays

INDArray fundamentals — shape, rank, stride, DataType, creating arrays, views vs copies, and off-heap memory in ND4J

What is an INDArray?

INDArray is the fundamental data structure in ND4J. It represents an N-dimensional array — a tensor — that can hold numerical data in any number of dimensions. Every operation in ND4J, and by extension all of DL4J, produces and consumes INDArray instances.

The Java interface is org.nd4j.linalg.api.ndarray.INDArray. You never instantiate this directly; instead you use the static factory methods on org.nd4j.linalg.factory.Nd4j.

Key Properties

Every INDArray has four defining properties:

Rank — the number of dimensions. A scalar has rank 0, a vector rank 1, a matrix rank 2, and so on. There is no hard upper limit on rank.

Shape — a long[] giving the size of each dimension. A 3x4x5 array has shape [3, 4, 5]. The shape determines what indices are valid when you access elements.

Length — the total number of elements, which is the product of the shape dimensions. A 3x4x5 array has length 60.

Stride — a long[] giving the separation in the underlying flat buffer between consecutive elements along each dimension. Stride is what makes views (slices, transposes, sub-arrays) possible without copying data. For a C-order (row-major) 3x4 matrix the strides are [4, 1] — moving one row costs 4 positions in the buffer, moving one column costs 1.

Indexing Convention

ND4J uses zero-based indexing throughout. For a 2D array (matrix):

  • Dimension 0 is the row dimension

  • Dimension 1 is the column dimension

This generalises for higher ranks: dimension 0 is the outermost (slowest-varying) dimension in C order.

DataType

Every INDArray has a numeric type, represented by the org.nd4j.linalg.api.buffer.DataType enum. Choosing the right type matters for precision, memory use, and hardware compatibility.

Available Types in M2.1

Floating point:

Preferred Name
Bits
Notes

DOUBLE

64

Double-precision float

FLOAT

32

Single-precision float (default)

FLOAT16

16

Half-precision float

BFLOAT16

16

Brain float, wider exponent range than FLOAT16

Signed integer:

Preferred Name
Bits

INT64

64

INT32

32

INT16

16

INT8

8

Unsigned integer:

Preferred Name
Bits

UINT64

64

UINT32

32

UINT16

16

UINT8

8

Other: BOOL, UTF8

Deprecated Aliases

The following names still compile but you should prefer the names above in new code:

Old name
Preferred name

HALF

FLOAT16

LONG

INT64

INT

INT32

SHORT

INT16

BYTE

INT8

UBYTE

UINT8

FLOAT16 and HALF are the same enum constant — FLOAT16 is a static alias for HALF. The same relationship holds for the integer pairs (INT32/INT, INT64/LONG, etc.). Both names are interchangeable at the JVM level, but the preferred names make code easier to read.

Migration Note from beta4

If you are migrating from an earlier beta release, note that DataBuffer.Type.DOUBLE and DataBuffer.Type.FLOAT no longer exist. Replace all uses of DataBuffer.Type with DataType:

Default DataType and Global Configuration

The default type for new arrays is FLOAT. You can change this globally at application startup:

Calling this once before any array creation ensures consistent types throughout your application.

Creating NDArrays

All array creation goes through static methods on Nd4j. The most commonly used ones are shown below.

Zeros and Ones

Scalar-Filled Arrays

Use zeros combined with an in-place add to fill with any constant:

From Java Arrays

Random Arrays

Linspace

Identity Matrix

Stacking and Concatenation

Shape Introspection

Once you have an array, these methods tell you its structure:

Note that rows() and columns() are only meaningful for 2D arrays. For higher-rank tensors, use shape() and size(dimension).

Views vs. Copies

Understanding the difference between a view and a copy is one of the most important concepts in ND4J. Getting it wrong leads to bugs that are hard to track down.

Views Share Underlying Memory

Many operations return a view — a new INDArray object that refers to the same backing memory buffer as the original. Modifying the view modifies the original:

Operations that commonly return views: getRow, getColumn, reshape, transpose, get with NDArrayIndex, sub-array slicing.

Copies with dup()

If you need an independent copy, call .dup():

In-Place vs. Out-of-Place Operations

ND4J follows a naming convention for element-wise operations:

  • Methods ending in i (e.g., addi, muli, subi) are in-place: they modify the array they are called on and return the same object.

  • Methods without the i suffix (e.g., add, mul, sub) are out-of-place: they allocate a new array and leave the original unchanged.

The in-place variants avoid allocating a new array, which matters when performance is critical. However, be careful when calling in-place operations on views — you will modify the original array's data.

Transpose is a View

transpose() returns a view with modified strides, not a copy:

If you need an independent transposed copy: mat.transpose().dup().

Memory Layout

Off-Heap Storage

NDArrays in ND4J are stored in off-heap memory — outside the JVM's garbage-collected heap. This is managed via JavaCPP and direct native memory. The benefits are significant:

  • BLAS interoperability: Native linear algebra libraries (OpenBLAS, cuBLAS) can operate directly on the data without copying.

  • No 2^31 element limit: Java arrays are limited to Integer.MAX_VALUE elements. Off-heap buffers have no such restriction.

  • No GC pressure: Large arrays do not contribute to GC pauses because they are not on the JVM heap. However, you must be aware of memory lifecycle — see the Memory and Workspaces page for details on workspace-based memory management.

C Order and F Order

ND4J supports two memory layouts for 2D and higher-rank arrays:

  • C order (row-major): Elements within a row are contiguous in memory. This is the default. Consistent with NumPy's default and with C arrays.

  • F order (column-major): Elements within a column are contiguous in memory. This is Fortran order, used internally by some BLAS routines.

Most operations work correctly regardless of order. You can check the order with arr.ordering() which returns 'c' or 'f'. If you need a specific order, use arr.dup('c') or arr.dup('f').

For most users, the default C order is the right choice.

Getting and Setting Values

Individual Elements

Rows and Columns

Sub-Arrays with NDArrayIndex

NDArrayIndex gives you flexible sub-array access:

NDArrayIndex.interval and NDArrayIndex.point return views by default. Use .dup() if you need a copy.

Basic Operations Preview

The full operations reference is covered in the Operations page. Here is a quick overview of the most common patterns.

Element-Wise Arithmetic

Matrix Multiplication

Reductions

Shape Transformations

Scalar Operations


For memory management and workspace configuration, see the Memory and Workspaces page. For the full catalog of mathematical operations including transforms, linear algebra, and reductions, see the Operations page.

Last updated

Was this helpful?