Julia, like most technical computing languages, provides a first-class array implementation. Most technical computing languages pay a lot of attention to their array implementation at the expense of other containers. Julia does not treat arrays in any special way. The array library is implemented almost completely in Julia itself, and derives its performance from the compiler, just like any other code written in Julia. As such, it's also possible to define custom array types by inheriting fromAbstractArray
. See themanual section on the AbstractArray interface for more details on implementing a custom array type.
An array is a collection of objects stored in a multi-dimensional grid. Zero-dimensional arrays are allowed, seethis FAQ entry. In the most general case, an array may contain objects of typeAny
. For most computational purposes, arrays should contain objects of a more specific type, such asFloat64
orInt32
.
In general, unlike many other technical computing languages, Julia does not expect programs to be written in a vectorized style for performance. Julia's compiler uses type inference and generates optimized code for scalar array indexing, allowing programs to be written in a style that is convenient and readable, without sacrificing performance, and using less memory at times.
In Julia, all arguments to functions arepassed by sharing (i.e. by pointers). Some technical computing languages pass arrays by value, and while this prevents accidental modification by callees of a value in the caller, it makes avoiding unwanted copying of arrays difficult. By convention, a function name ending with a!
indicates that it will mutate or destroy the value of one or more of its arguments (compare, for example,sort
andsort!
). Callees must make explicit copies to ensure that they don't modify inputs that they don't intend to change. Many non-mutating functions are implemented by calling a function of the same name with an added!
at the end on an explicit copy of the input, and returning that copy.
Function | Description |
---|---|
eltype(A) | the type of the elements contained inA |
length(A) | the number of elements inA |
ndims(A) | the number of dimensions ofA |
size(A) | a tuple containing the dimensions ofA |
size(A,n) | the size ofA along dimensionn |
axes(A) | a tuple containing the valid indices ofA |
axes(A,n) | a range expressing the valid indices along dimensionn |
eachindex(A) | an efficient iterator for visiting each position inA |
stride(A,k) | the stride (linear index distance between adjacent elements) along dimensionk |
strides(A) | a tuple of the strides in each dimension |
Many functions for constructing and initializing arrays are provided. In the following list of such functions, calls with adims...
argument can either take a single tuple of dimension sizes or a series of dimension sizes passed as a variable number of arguments. Most of these functions also accept a first inputT
, which is the element type of the array. If the typeT
is omitted it will default toFloat64
.
Function | Description |
---|---|
Array{T}(undef, dims...) | an uninitialized denseArray |
zeros(T, dims...) | anArray of all zeros |
ones(T, dims...) | anArray of all ones |
trues(dims...) | aBitArray with all valuestrue |
falses(dims...) | aBitArray with all valuesfalse |
reshape(A, dims...) | an array containing the same data asA , but with different dimensions |
copy(A) | copyA |
deepcopy(A) | copyA , recursively copying its elements |
similar(A, T, dims...) | an uninitialized array of the same type asA (dense, sparse, etc.), but with the specified element type and dimensions. The second and third arguments are both optional, defaulting to the element type and dimensions ofA if omitted. |
reinterpret(T, A) | an array with the same binary data asA , but with element typeT |
rand(T, dims...) | anArray with random, iid[1] and uniformly distributed values. For floating point typesT , the values lie in the half-open interval$[0, 1)$. |
randn(T, dims...) | anArray with random, iid and standard normally distributed values |
Matrix{T}(I, m, n) | m -by-n identity matrix. Requiresusing LinearAlgebra forI . |
range(start, stop, n) | a range ofn linearly spaced elements fromstart tostop |
fill!(A, x) | fill the arrayA with the valuex |
fill(x, dims...) | anArray filled with the valuex . In particular,fill(x) constructs a zero-dimensionalArray containingx . |
To see the various ways we can pass dimensions to these functions, consider the following examples:
julia> zeros(Int8, 2, 3)2×3 Matrix{Int8}: 0 0 0 0 0 0julia> zeros(Int8, (2, 3))2×3 Matrix{Int8}: 0 0 0 0 0 0julia> zeros((2, 3))2×3 Matrix{Float64}: 0.0 0.0 0.0 0.0 0.0 0.0
Here,(2, 3)
is aTuple
and the first argument — the element type — is optional, defaulting toFloat64
.
Arrays can also be directly constructed with square braces; the syntax[A, B, C, ...]
creates a one-dimensional array (i.e., a vector) containing the comma-separated arguments as its elements. The element type (eltype
) of the resulting array is automatically determined by the types of the arguments inside the braces. If all the arguments are the same type, then that is itseltype
. If they all have a commonpromotion type then they get converted to that type usingconvert
and that type is the array'seltype
. Otherwise, a heterogeneous array that can hold anything — aVector{Any}
— is constructed; this includes the literal[]
where no arguments are given.Array literal can be typed with the syntaxT[A, B, C, ...]
whereT
is a type.
julia> [1, 2, 3] # An array of `Int`s3-element Vector{Int64}: 1 2 3julia> promote(1, 2.3, 4//5) # This combination of Int, Float64 and Rational promotes to Float64(1.0, 2.3, 0.8)julia> [1, 2.3, 4//5] # Thus that's the element type of this Array3-element Vector{Float64}: 1.0 2.3 0.8julia> Float32[1, 2.3, 4//5] # Specify element type manually3-element Vector{Float32}: 1.0 2.3 0.8julia> []Any[]
If the arguments inside the square brackets are separated by single semicolons (;
) or newlines instead of commas, then their contents arevertically concatenated together instead of the arguments being used as elements themselves.
julia> [1:2, 4:5] # Has a comma, so no concatenation occurs. The ranges are themselves the elements2-element Vector{UnitRange{Int64}}: 1:2 4:5julia> [1:2; 4:5]4-element Vector{Int64}: 1 2 4 5julia> [1:2 4:5 6]5-element Vector{Int64}: 1 2 4 5 6
Similarly, if the arguments are separated by tabs or spaces or double semicolons, then their contents arehorizontally concatenated together.
julia> [1:2 4:5 7:8]2×3 Matrix{Int64}: 1 4 7 2 5 8julia> [[1,2] [4,5] [7,8]]2×3 Matrix{Int64}: 1 4 7 2 5 8julia> [1 2 3] # Numbers can also be horizontally concatenated1×3 Matrix{Int64}: 1 2 3julia> [1;; 2;; 3;; 4]1×4 Matrix{Int64}: 1 2 3 4
Single semicolons (or newlines) and spaces (or tabs) can be combined to concatenate both horizontally and vertically at the same time.
julia> [1 2 3 4]2×2 Matrix{Int64}: 1 2 3 4julia> [zeros(Int, 2, 2) [1; 2] [3 4] 5]3×3 Matrix{Int64}: 0 0 1 0 0 2 3 4 5julia> [[1 1]; 2 3; [4 4]]3×2 Matrix{Int64}: 1 1 2 3 4 4
Spaces (and tabs) have a higher precedence than semicolons, performing any horizontal concatenations first and then concatenating the result. Using double semicolons for the horizontal concatenation, on the other hand, performs any vertical concatenations before horizontally concatenating the result.
julia> [zeros(Int, 2, 2) ; [3 4] ;; [1; 2] ; 5]3×3 Matrix{Int64}: 0 0 1 0 0 2 3 4 5julia> [1:2; 4;; 1; 3:4]3×2 Matrix{Int64}: 1 1 2 3 4 4
Just as;
and;;
concatenate in the first and second dimension, using more semicolons extends this same general scheme. The number of semicolons in the separator specifies the particular dimension, so;;;
concatenates in the third dimension,;;;;
in the 4th, and so on. Fewer semicolons take precedence, so the lower dimensions are generally concatenated first.
julia> [1; 2;; 3; 4;; 5; 6;;; 7; 8;; 9; 10;; 11; 12]2×3×2 Array{Int64, 3}:[:, :, 1] = 1 3 5 2 4 6[:, :, 2] = 7 9 11 8 10 12
Like before, spaces (and tabs) for horizontal concatenation have a higher precedence than any number of semicolons. Thus, higher-dimensional arrays can also be written by specifying their rows first, with their elements textually arranged in a manner similar to their layout:
julia> [1 3 5 2 4 6;;; 7 9 11 8 10 12]2×3×2 Array{Int64, 3}:[:, :, 1] = 1 3 5 2 4 6[:, :, 2] = 7 9 11 8 10 12julia> [1 2;;; 3 4;;;; 5 6;;; 7 8]1×2×2×2 Array{Int64, 4}:[:, :, 1, 1] = 1 2[:, :, 2, 1] = 3 4[:, :, 1, 2] = 5 6[:, :, 2, 2] = 7 8julia> [[1 2;;; 3 4];;;; [5 6];;; [7 8]]1×2×2×2 Array{Int64, 4}:[:, :, 1, 1] = 1 2[:, :, 2, 1] = 3 4[:, :, 1, 2] = 5 6[:, :, 2, 2] = 7 8
Although they both mean concatenation in the second dimension, spaces (or tabs) and;;
cannot appear in the same array expression unless the double semicolon is simply serving as a "line continuation" character. This allows a single horizontal concatenation to span multiple lines (without the line break being interpreted as a vertical concatenation).
julia> [1 2 ;; 3 4]1×4 Matrix{Int64}: 1 2 3 4
Terminating semicolons may also be used to add trailing length 1 dimensions.
julia> [1;;]1×1 Matrix{Int64}: 1julia> [2; 3;;;]2×1×1 Array{Int64, 3}:[:, :, 1] = 2 3
More generally, concatenation can be accomplished through thecat
function. These syntaxes are shorthands for function calls that themselves are convenience functions:
Syntax | Function | Description |
---|---|---|
cat | concatenate input arrays along dimension(s)k | |
[A; B; C; ...] | vcat | shorthand forcat(A...; dims=1) |
[A B C ...] | hcat | shorthand forcat(A...; dims=2) |
[A B; C D; ...] | hvcat | simultaneous vertical and horizontal concatenation |
[A; C;; B; D;;; ...] | hvncat | simultaneous n-dimensional concatenation, where number of semicolons indicate the dimension to concatenate |
An array with a specific element type can be constructed using the syntaxT[A, B, C, ...]
. This will construct a 1-d array with element typeT
, initialized to contain elementsA
,B
,C
, etc. For example,Any[x, y, z]
constructs a heterogeneous array that can contain any values.
Concatenation syntax can similarly be prefixed with a type to specify the element type of the result.
julia> [[1 2] [3 4]]1×4 Matrix{Int64}: 1 2 3 4julia> Int8[[1 2] [3 4]]1×4 Matrix{Int8}: 1 2 3 4
Comprehensions provide a general and powerful way to construct arrays. Comprehension syntax is similar to set construction notation in mathematics:
A = [ F(x, y, ...) for x=rx, y=ry, ... ]
The meaning of this form is thatF(x,y,...)
is evaluated with the variablesx
,y
, etc. taking on each value in their given list of values. Values can be specified as any iterable object, but will commonly be ranges like1:n
or2:(n-1)
, or explicit arrays of values like[1.2, 3.4, 5.7]
. The result is an N-d dense array with dimensions that are the concatenation of the dimensions of the variable rangesrx
,ry
, etc. and eachF(x,y,...)
evaluation returns a scalar.
The following example computes a weighted average of the current element and its left and right neighbor along a 1-d grid. :
julia> x = rand(8)8-element Array{Float64,1}: 0.843025 0.869052 0.365105 0.699456 0.977653 0.994953 0.41084 0.809411julia> [ 0.25*x[i-1] + 0.5*x[i] + 0.25*x[i+1] for i=2:length(x)-1 ]6-element Array{Float64,1}: 0.736559 0.57468 0.685417 0.912429 0.8446 0.656511
The resulting array type depends on the types of the computed elements just likearray literals do. In order to control the type explicitly, a type can be prepended to the comprehension. For example, we could have requested the result in single precision by writing:
Float32[ 0.25*x[i-1] + 0.5*x[i] + 0.25*x[i+1] for i=2:length(x)-1 ]
Comprehensions can also be written without the enclosing square brackets, producing an object known as a generator. This object can be iterated to produce values on demand, instead of allocating an array and storing them in advance (seeIteration). For example, the following expression sums a series without allocating memory:
julia> sum(1/n^2 for n=1:1000)1.6439345666815615
When writing a generator expression with multiple dimensions inside an argument list, parentheses are needed to separate the generator from subsequent arguments:
julia> map(tuple, 1/(i+j) for i=1:2, j=1:2, [1:4;])ERROR: syntax: invalid iteration specification
All comma-separated expressions afterfor
are interpreted as ranges. Adding parentheses lets us add a third argument tomap
:
julia> map(tuple, (1/(i+j) for i=1:2, j=1:2), [1 3; 2 4])2×2 Matrix{Tuple{Float64, Int64}}: (0.5, 1) (0.333333, 3) (0.333333, 2) (0.25, 4)
Generators are implemented via inner functions. Just like inner functions used elsewhere in the language, variables from the enclosing scope can be "captured" in the inner function. For example,sum(p[i] - q[i] for i=1:n)
captures the three variablesp
,q
andn
from the enclosing scope. Captured variables can present performance challenges; seeperformance tips.
Ranges in generators and comprehensions can depend on previous ranges by writing multiplefor
keywords:
julia> [(i, j) for i=1:3 for j=1:i]6-element Vector{Tuple{Int64, Int64}}: (1, 1) (2, 1) (2, 2) (3, 1) (3, 2) (3, 3)
In such cases, the result is always 1-d.
Generated values can be filtered using theif
keyword:
julia> [(i, j) for i=1:3 for j=1:i if i+j == 4]2-element Vector{Tuple{Int64, Int64}}: (2, 2) (3, 1)
The general syntax for indexing into an n-dimensional arrayA
is:
X = A[I_1, I_2, ..., I_n]
where eachI_k
may be a scalar integer, an array of integers, or any othersupported index. This includesColon
(:
) to select all indices within the entire dimension, ranges of the forma:c
ora:b:c
to select contiguous or strided subsections, and arrays of booleans to select elements at theirtrue
indices.
If all the indices are scalars, then the resultX
is a single element from the arrayA
. Otherwise,X
is an array with the same number of dimensions as the sum of the dimensionalities of all the indices.
If all indicesI_k
are vectors, for example, then the shape ofX
would be(length(I_1), length(I_2), ..., length(I_n))
, with locationi_1, i_2, ..., i_n
ofX
containing the valueA[I_1[i_1], I_2[i_2], ..., I_n[i_n]]
.
Example:
julia> A = reshape(collect(1:16), (2, 2, 2, 2))2×2×2×2 Array{Int64, 4}:[:, :, 1, 1] = 1 3 2 4[:, :, 2, 1] = 5 7 6 8[:, :, 1, 2] = 9 11 10 12[:, :, 2, 2] = 13 15 14 16julia> A[1, 2, 1, 1] # all scalar indices3julia> A[[1, 2], [1], [1, 2], [1]] # all vector indices2×1×2×1 Array{Int64, 4}:[:, :, 1, 1] = 1 2[:, :, 2, 1] = 5 6julia> A[[1, 2], [1], [1, 2], 1] # a mix of index types2×1×2 Array{Int64, 3}:[:, :, 1] = 1 2[:, :, 2] = 5 6
Note how the size of the resulting array is different in the last two cases.
IfI_1
is changed to a two-dimensional matrix, thenX
becomes ann+1
-dimensional array of shape(size(I_1, 1), size(I_1, 2), length(I_2), ..., length(I_n))
. The matrix adds a dimension.
Example:
julia> A = reshape(collect(1:16), (2, 2, 2, 2));julia> A[[1 2; 1 2]]2×2 Matrix{Int64}: 1 2 1 2julia> A[[1 2; 1 2], 1, 2, 1]2×2 Matrix{Int64}: 5 6 5 6
The locationi_1, i_2, i_3, ..., i_{n+1}
contains the value atA[I_1[i_1, i_2], I_2[i_3], ..., I_n[i_{n+1}]]
. All dimensions indexed with scalars are dropped. For example, ifJ
is an array of indices, then the result ofA[2, J, 3]
is an array with sizesize(J)
. Itsj
th element is populated byA[2, J[j], 3]
.
As a special part of this syntax, theend
keyword may be used to represent the last index of each dimension within the indexing brackets, as determined by the size of the innermost array being indexed. Indexing syntax without theend
keyword is equivalent to a call togetindex
:
X = getindex(A, I_1, I_2, ..., I_n)
Example:
julia> x = reshape(1:16, 4, 4)4×4 reshape(::UnitRange{Int64}, 4, 4) with eltype Int64: 1 5 9 13 2 6 10 14 3 7 11 15 4 8 12 16julia> x[2:3, 2:end-1]2×2 Matrix{Int64}: 6 10 7 11julia> x[1, [2 3; 4 1]]2×2 Matrix{Int64}: 5 9 13 1
The general syntax for assigning values in an n-dimensional arrayA
is:
A[I_1, I_2, ..., I_n] = X
where eachI_k
may be a scalar integer, an array of integers, or any othersupported index. This includesColon
(:
) to select all indices within the entire dimension, ranges of the forma:c
ora:b:c
to select contiguous or strided subsections, and arrays of booleans to select elements at theirtrue
indices.
If all indicesI_k
are integers, then the value in locationI_1, I_2, ..., I_n
ofA
is overwritten with the value ofX
,convert
ing to theeltype
ofA
if necessary.
If any indexI_k
is itself an array, then the right hand sideX
must also be an array with the same shape as the result of indexingA[I_1, I_2, ..., I_n]
or a vector with the same number of elements. The value in locationI_1[i_1], I_2[i_2], ..., I_n[i_n]
ofA
is overwritten with the valueX[i_1, i_2, ..., i_n]
, converting if necessary. The element-wise assignment operator.=
may be used tobroadcastX
across the selected locations:
A[I_1, I_2, ..., I_n] .= X
Just as inIndexing, theend
keyword may be used to represent the last index of each dimension within the indexing brackets, as determined by the size of the array being assigned into. Indexed assignment syntax without theend
keyword is equivalent to a call tosetindex!
:
setindex!(A, X, I_1, I_2, ..., I_n)
Example:
julia> x = collect(reshape(1:9, 3, 3))3×3 Matrix{Int64}: 1 4 7 2 5 8 3 6 9julia> x[3, 3] = -9;julia> x[1:2, 1:2] = [-1 -4; -2 -5];julia> x3×3 Matrix{Int64}: -1 -4 7 -2 -5 8 3 6 -9
In the expressionA[I_1, I_2, ..., I_n]
, eachI_k
may be a scalar index, an array of scalar indices, or an object that represents an array of scalar indices and can be converted to such byto_indices
:
CartesianIndex{N}
s, which behave like anN
-tuple of integers spanning multiple dimensions (see below for more details)[]
, which select no elements e.g.A[[]]
(not to be confused withA[]
)a:c
ora:b:c
, which select contiguous or strided subsections froma
toc
(inclusive)AbstractArray
CartesianIndex{N}
(see below for more details)to_indices
. By default this includes:Colon()
(:
), which represents all indices within an entire dimension or across the entire arraytrue
indices (see below for more details)Some examples:
julia> A = reshape(collect(1:2:18), (3, 3))3×3 Matrix{Int64}: 1 7 13 3 9 15 5 11 17julia> A[4]7julia> A[[2, 5, 8]]3-element Vector{Int64}: 3 9 15julia> A[[1 4; 3 8]]2×2 Matrix{Int64}: 1 7 5 15julia> A[[]]Int64[]julia> A[1:2:5]3-element Vector{Int64}: 1 5 9julia> A[2, :]3-element Vector{Int64}: 3 9 15julia> A[:, 3]3-element Vector{Int64}: 13 15 17julia> A[:, 3:3]3×1 Matrix{Int64}: 13 15 17
The specialCartesianIndex{N}
object represents a scalar index that behaves like anN
-tuple of integers spanning multiple dimensions. For example:
julia> A = reshape(1:32, 4, 4, 2);julia> A[3, 2, 1]7julia> A[CartesianIndex(3, 2, 1)] == A[3, 2, 1] == 7true
Considered alone, this may seem relatively trivial;CartesianIndex
simply gathers multiple integers together into one object that represents a single multidimensional index. When combined with other indexing forms and iterators that yieldCartesianIndex
es, however, this can produce very elegant and efficient code. SeeIteration below, and for some more advanced examples, seethis blog post on multidimensional algorithms and iteration.
Arrays ofCartesianIndex{N}
are also supported. They represent a collection of scalar indices that each spanN
dimensions, enabling a form of indexing that is sometimes referred to as pointwise indexing. For example, it enables accessing the diagonal elements from the first "page" ofA
from above:
julia> page = A[:, :, 1]4×4 Matrix{Int64}: 1 5 9 13 2 6 10 14 3 7 11 15 4 8 12 16julia> page[[CartesianIndex(1, 1), CartesianIndex(2, 2), CartesianIndex(3, 3), CartesianIndex(4, 4)]]4-element Vector{Int64}: 1 6 11 16
This can be expressed much more simply withdot broadcasting and by combining it with a normal integer index (instead of extracting the firstpage
fromA
as a separate step). It can even be combined with a:
to extract both diagonals from the two pages at the same time:
julia> A[CartesianIndex.(axes(A, 1), axes(A, 2)), 1]4-element Vector{Int64}: 1 6 11 16julia> A[CartesianIndex.(axes(A, 1), axes(A, 2)), :]4×2 Matrix{Int64}: 1 17 6 22 11 27 16 32
CartesianIndex
and arrays ofCartesianIndex
are not compatible with theend
keyword to represent the last index of a dimension. Do not useend
in indexing expressions that may contain eitherCartesianIndex
or arrays thereof.
Often referred to as logical indexing or indexing with a logical mask, indexing by a boolean array selects elements at the indices where its values aretrue
. Indexing by a boolean vectorB
is effectively the same as indexing by the vector of integers that is returned byfindall(B)
. Similarly, indexing by aN
-dimensional boolean array is effectively the same as indexing by the vector ofCartesianIndex{N}
s where its values aretrue
. A logical index must be a array of the same shape as the dimension(s) it indexes into, or it must be the only index provided and match the shape of the one-dimensional reshaped view of the array it indexes into. It is generally more efficient to use boolean arrays as indices directly instead of first callingfindall
.
julia> x = reshape(1:12, 2, 3, 2)2×3×2 reshape(::UnitRange{Int64}, 2, 3, 2) with eltype Int64:[:, :, 1] = 1 3 5 2 4 6[:, :, 2] = 7 9 11 8 10 12julia> x[:, [true false; false true; true false]]2×3 Matrix{Int64}: 1 5 9 2 6 10julia> mask = map(ispow2, x)2×3×2 Array{Bool, 3}:[:, :, 1] = 1 0 0 1 1 0[:, :, 2] = 0 0 0 1 0 0julia> x[mask]4-element Vector{Int64}: 1 2 4 8julia> x[vec(mask)] == x[mask] # we can also index with a single Boolean vectortrue
The ordinary way to index into anN
-dimensional array is to use exactlyN
indices; each index selects the position(s) in its particular dimension. For example, in the three-dimensional arrayA = rand(4, 3, 2)
,A[2, 3, 1]
will select the number in the second row of the third column in the first "page" of the array. This is often referred to ascartesian indexing.
When exactly one indexi
is provided, that index no longer represents a location in a particular dimension of the array. Instead, it selects thei
th element using the column-major iteration order that linearly spans the entire array. This is known aslinear indexing. It essentially treats the array as though it had been reshaped into a one-dimensional vector withvec
.
julia> A = [2 6; 4 7; 3 1]3×2 Matrix{Int64}: 2 6 4 7 3 1julia> A[5]7julia> vec(A)[5]7
A linear index into the arrayA
can be converted to aCartesianIndex
for cartesian indexing withCartesianIndices(A)[i]
(seeCartesianIndices
), and a set ofN
cartesian indices can be converted to a linear index withLinearIndices(A)[i_1, i_2, ..., i_N]
(seeLinearIndices
).
julia> CartesianIndices(A)[5]CartesianIndex(2, 2)julia> LinearIndices(A)[2, 2]5
It's important to note that there's a very large asymmetry in the performance of these conversions. Converting a linear index to a set of cartesian indices requires dividing and taking the remainder, whereas going the other way is just multiplies and adds. In modern processors, integer division can be 10-50 times slower than multiplication. While some arrays — likeArray
itself — are implemented using a linear chunk of memory and directly use a linear index in their implementations, other arrays — likeDiagonal
— need the full set of cartesian indices to do their lookup (seeIndexStyle
to introspect which is which).
When iterating over all the indices for an array, it is better to iterate overeachindex(A)
instead of1:length(A)
. Not only will this be faster in cases whereA
isIndexCartesian
, but it will also support arrays with custom indexing, such asOffsetArrays. If only the values are needed, then is better to just iterate the array directly, i.e.for a in A
.
In addition to linear indexing, anN
-dimensional array may be indexed with fewer or more thanN
indices in certain situations.
Indices may be omitted if the trailing dimensions that are not indexed into are all length one. In other words, trailing indices can be omitted only if there is only one possible value that those omitted indices could be for an in-bounds indexing expression. For example, a four-dimensional array with size(3, 4, 2, 1)
may be indexed with only three indices as the dimension that gets skipped (the fourth dimension) has length one. Note that linear indexing takes precedence over this rule.
julia> A = reshape(1:24, 3, 4, 2, 1)3×4×2×1 reshape(::UnitRange{Int64}, 3, 4, 2, 1) with eltype Int64:[:, :, 1, 1] = 1 4 7 10 2 5 8 11 3 6 9 12[:, :, 2, 1] = 13 16 19 22 14 17 20 23 15 18 21 24julia> A[1, 3, 2] # Omits the fourth dimension (length 1)19julia> A[1, 3] # Attempts to omit dimensions 3 & 4 (lengths 2 and 1)ERROR: BoundsError: attempt to access 3×4×2×1 reshape(::UnitRange{Int64}, 3, 4, 2, 1) with eltype Int64 at index [1, 3]julia> A[19] # Linear indexing19
When omittingall indices withA[]
, this semantic provides a simple idiom to retrieve the only element in an array and simultaneously ensure that there was only one element.
Similarly, more thanN
indices may be provided if all the indices beyond the dimensionality of the array are1
(or more generally are the first and only element ofaxes(A, d)
whered
is that particular dimension number). This allows vectors to be indexed like one-column matrices, for example:
julia> A = [8, 6, 7]3-element Vector{Int64}: 8 6 7julia> A[2, 1]6
The recommended ways to iterate over a whole array are
for a in A # Do something with the element aendfor i in eachindex(A) # Do something with i and/or A[i]end
The first construct is used when you need the value, but not index, of each element. In the second construct,i
will be anInt
ifA
is an array type with fast linear indexing; otherwise, it will be aCartesianIndex
:
julia> A = rand(4, 3);julia> B = view(A, 1:3, 2:3);julia> for i in eachindex(B) @show i endi = CartesianIndex(1, 1)i = CartesianIndex(2, 1)i = CartesianIndex(3, 1)i = CartesianIndex(1, 2)i = CartesianIndex(2, 2)i = CartesianIndex(3, 2)
In contrast withfor i = 1:length(A)
, iterating witheachindex
provides an efficient way to iterate over any array type. Besides, this also supports generic arrays with custom indexing such asOffsetArrays.
If you write a customAbstractArray
type, you can specify that it has fast linear indexing using
Base.IndexStyle(::Type{<:MyArray}) = IndexLinear()
This setting will causeeachindex
iteration over aMyArray
to use integers. If you don't specify this trait, the default valueIndexCartesian()
is used.
The following operators are supported for arrays:
-
,+
-
,+
,*
,/
,\
,^
==
,!=
,≈
(isapprox
),≉
To enable convenient vectorization of mathematical and other operations, Juliaprovides the dot syntaxf.(args...)
, e.g.sin.(x)
ormin.(x, y)
, for elementwise operations over arrays or mixtures of arrays and scalars (aBroadcasting operation); these have the additional advantage of "fusing" into a single loop when combined with other dot calls, e.g.sin.(cos.(x))
.
Also,every binary operator supports adot version that can be applied to arrays (and combinations of arrays and scalars) in suchfused broadcasting operations, e.g.z .== sin.(x .* y)
.
Note that comparisons such as==
operate on whole arrays, giving a single boolean answer. Use dot operators like.==
for elementwise comparisons. (For comparison operations like<
,only the elementwise.<
version is applicable to arrays.)
Also notice the difference betweenmax.(a,b)
, whichbroadcast
smax
elementwise overa
andb
, andmaximum(a)
, which finds the largest value withina
. The same relationship holds formin.(a, b)
andminimum(a)
.
It is sometimes useful to perform element-by-element binary operations on arrays of different sizes, such as adding a vector to each column of a matrix. An inefficient way to do this would be to replicate the vector to the size of the matrix:
julia> a = rand(2, 1); A = rand(2, 3);julia> repeat(a, 1, 3) + A2×3 Array{Float64,2}: 1.20813 1.82068 1.25387 1.56851 1.86401 1.67846
This is wasteful when dimensions get large, so Julia providesbroadcast
, which expands singleton dimensions in array arguments to match the corresponding dimension in the other array without using extra memory, and applies the given function elementwise:
julia> broadcast(+, a, A)2×3 Array{Float64,2}: 1.20813 1.82068 1.25387 1.56851 1.86401 1.67846julia> b = rand(1,2)1×2 Array{Float64,2}: 0.867535 0.00457906julia> broadcast(+, a, b)2×2 Array{Float64,2}: 1.71056 0.847604 1.73659 0.873631
Dotted operators such as.+
and.*
are equivalent tobroadcast
calls (except that they fuse, asdescribed above). There is also abroadcast!
function to specify an explicit destination (which can also be accessed in a fusing fashion by.=
assignment). In fact,f.(args...)
is equivalent tobroadcast(f, args...)
, providing a convenient syntax to broadcast any function (dot syntax). Nested "dot calls"f.(...)
(including calls to.+
etcetera)automatically fuse into a singlebroadcast
call.
Additionally,broadcast
is not limited to arrays (see the function documentation); it also handles scalars, tuples and other collections. By default, only some argument types are considered scalars, including (but not limited to)Number
s,String
s,Symbol
s,Type
s,Function
s and some common singletons likemissing
andnothing
. All other arguments are iterated over or indexed into elementwise.
julia> convert.(Float32, [1, 2])2-element Vector{Float32}: 1.0 2.0julia> ceil.(UInt8, [1.2 3.4; 5.6 6.7])2×2 Matrix{UInt8}: 0x02 0x04 0x06 0x07julia> string.(1:3, ". ", ["First", "Second", "Third"])3-element Vector{String}: "1. First" "2. Second" "3. Third"
Sometimes, you want a container (like an array) that would normally participate in broadcast to be "protected" from broadcast's behavior of iterating over all of its elements. By placing it inside another container (like a single elementTuple
) broadcast will treat it as a single value.
julia> ([1, 2, 3], [4, 5, 6]) .+ ([1, 2, 3],)([2, 4, 6], [5, 7, 9])julia> ([1, 2, 3], [4, 5, 6]) .+ tuple([1, 2, 3])([2, 4, 6], [5, 7, 9])
The base array type in Julia is the abstract typeAbstractArray{T,N}
. It is parameterized by the number of dimensionsN
and the element typeT
.AbstractVector
andAbstractMatrix
are aliases for the 1-d and 2-d cases. Operations onAbstractArray
objects are defined using higher level operators and functions, in a way that is independent of the underlying storage. These operations generally work correctly as a fallback for any specific array implementation.
TheAbstractArray
type includes anything vaguely array-like, and implementations of it might be quite different from conventional arrays. For example, elements might be computed on request rather than stored. However, any concreteAbstractArray{T,N}
type should generally implement at leastsize(A)
(returning anInt
tuple),getindex(A, i)
andgetindex(A, i1, ..., iN)
; mutable arrays should also implementsetindex!
. It is recommended that these operations have nearly constant time complexity, as otherwise some array functions may be unexpectedly slow. Concrete types should also typically provide asimilar(A, T=eltype(A), dims=size(A))
method, which is used to allocate a similar array forcopy
and other out-of-place operations. No matter how anAbstractArray{T,N}
is represented internally,T
is the type of object returned byinteger indexing (A[1, ..., 1]
, whenA
is not empty) andN
should be the length of the tuple returned bysize
. For more details on defining customAbstractArray
implementations, see thearray interface guide in the interfaces chapter.
DenseArray
is an abstract subtype ofAbstractArray
intended to include all arrays where elements are stored contiguously in column-major order (seeadditional notes in Performance Tips). TheArray
type is a specific instance ofDenseArray
;Vector
andMatrix
are aliases for the 1-d and 2-d cases. Very few operations are implemented specifically forArray
beyond those that are required for allAbstractArray
s; much of the array library is implemented in a generic manner that allows all custom arrays to behave similarly.
SubArray
is a specialization ofAbstractArray
that performs indexing by sharing memory with the original array rather than by copying it. ASubArray
is created with theview
function, which is called the same way asgetindex
(with an array and a series of index arguments). The result ofview
looks the same as the result ofgetindex
, except the data is left in place.view
stores the input index vectors in aSubArray
object, which can later be used to index the original array indirectly. By putting the@views
macro in front of an expression or block of code, anyarray[...]
slice in that expression will be converted to create aSubArray
view instead.
BitArray
s are space-efficient "packed" boolean arrays, which store one bit per boolean value. They can be used similarly toArray{Bool}
arrays (which store one byte per boolean value), and can be converted to/from the latter viaArray(bitarray)
andBitArray(array)
, respectively.
An array is "strided" if it is stored in memory with well-defined spacings (strides) between its elements. A strided array with a supported element type may be passed to an external (non-Julia) library like BLAS or LAPACK by simply passing itspointer
and the stride for each dimension. Thestride(A, d)
is the distance between elements along dimensiond
. For example, the builtinArray
returned byrand(5,7,2)
has its elements arranged contiguously in column major order. This means that the stride of the first dimension — the spacing between elements in the same column — is1
:
julia> A = rand(5, 7, 2);julia> stride(A, 1)1
The stride of the second dimension is the spacing between elements in the same row, skipping as many elements as there are in a single column (5
). Similarly, jumping between the two "pages" (in the third dimension) requires skipping5*7 == 35
elements. Thestrides
of this array is the tuple of these three numbers together:
julia> strides(A)(1, 5, 35)
In this particular case, the number of elements skippedin memory matches the number oflinear indices skipped. This is only the case for contiguous arrays likeArray
(and otherDenseArray
subtypes) and is not true in general. Views with range indices are a good example ofnon-contiguous strided arrays; considerV = @view A[1:3:4, 2:2:6, 2:-1:1]
. This viewV
refers to the same memory asA
but is skipping and re-arranging some of its elements. The stride of the first dimension ofV
is3
because we're only selecting every third row from our original array:
julia> V = @view A[1:3:4, 2:2:6, 2:-1:1];julia> stride(V, 1)3
This view is similarly selecting every other column from our originalA
— and thus it needs to skip the equivalent of two five-element columns when moving between indices in the second dimension:
julia> stride(V, 2)10
The third dimension is interesting because its order is reversed! Thus to get from the first "page" to the second one it must gobackwards in memory, and so its stride in this dimension is negative!
julia> stride(V, 3)-35
This means that thepointer
forV
is actually pointing into the middle ofA
's memory block, and it refers to elements both backwards and forwards in memory. See theinterface guide for strided arrays for more details on defining your own strided arrays.StridedVector
andStridedMatrix
are convenient aliases for many of the builtin array types that are considered strided arrays, allowing them to dispatch to select specialized implementations that call highly tuned and optimized BLAS and LAPACK functions using just the pointer and strides.
It is worth emphasizing that strides are about offsets in memory rather than indexing. If you are looking to convert between linear (single-index) indexing and cartesian (multi-index) indexing, seeLinearIndices
andCartesianIndices
.
Settings
This document was generated withDocumenter.jl version 1.8.0 onWednesday 9 July 2025. Using Julia version 1.11.6.