现在位置: 首页 > Julia 教程 > 正文

Julia 数组

数组,就是相同数据类型的元素按一定顺序排列的集合,可以是一维数组和多维数组。

Julia 数组是可变类型集合,用于列表、向量、表格和矩阵。

Julia 数组的索引从 1 开始(不是 0),数组的大小不是固定的,可以动态增长。

Julia 数组用方括号 [...] 指定,多个元素使用逗号 , 分隔。创建一个一维数组(即一个向量)语法为:

[A, B, C, ...]

创建一维数组

下面实例创建了一个简单的一维数组:

实例

julia> arr = [1, 2, 3]
3-element Vector{Int64}:
 1
 2
 3

上面的实例中我们创建了一个包含 3 个元素的一维数组,每个元素都是一个 64 位整数。

数组元素的类型也可以不一样,此时元素类型会是 Any:

实例

julia> arr = [1, "RUNOOB", 2.5, pi]
4-element Vector{Any}:
 1
  "RUNOOB"
 2.5
 π = 3.1415926535897...

注意:元素类型为 Any 的数组性能较差(每个元素都是指针,无法紧凑存储),实际开发中应尽量让数组元素类型一致,详见性能技巧章节。

当然也可以强制指定类型:

实例

julia> arr = Int64[1, 2, 3]
3-element Vector{Int64}:
 1
 2
 3

julia> arr2 = String["Taobao", "RUNOOB", "GOOGLE"]
3-element Vector{String}:
 "Taobao"
 "RUNOOB"
 "GOOGLE"

我们也可以创建一个空数组:

实例

julia> arr = Int64[]
Int64[]

julia> arr2 = String[]
String[]

索引访问

创建的数组可以直接使用索引值来访问,第一个值的索引为 1(不是 0),最后一个可以使用 end 表示:

实例

julia> arr = Int64[1, 2, 3];

julia> arr[2]
2

julia> arr[end]
3

# 范围索引取出子数组
julia> arr[1:2]
2-element Vector{Int64}:
 1
 2

指定数组类型及维度

我们还可以使用以下语法指定数组的类型和维度:

Array{type}(undef, dims...)

undef 表示数组未初始化。

dims... 可以是维度的元组,也可以是多个整数参数。

实例

julia> array = Array{Int64}(undef, 3)    # 一维数组,3 个元素
3-element Vector{Int64}:
 4834342704
 4377305096
          0

julia> array = Array{Int64}(undef, 3, 3, 3)    # 3 维数组,每个维度 3 个元素
3×3×3 Array{Int64, 3}:
[:, :, 1] =
 4562265712  0  0
 ...

undef 用于设置数组未初始化为任何已知值,这就是我们在输出中得到随机数的原因。未初始化的数组必须在赋值后才能安全读取。


创建二维数组和矩阵

我们可以将数组元素中的逗号省略掉或者使用两个分号 ;;,这样就可以创建一个二维数组了:

实例

julia> [1 2 3 4]
1×4 Matrix{Int64}:
 1  2  3  4

julia> [1;; 2;; 3;; 4]
1×4 Matrix{Int64}:
 1  2  3  4

虽然只有一行,也是二维数组,因为 Julia 只认可列向量,而不认可所谓的行向量。

要添加另一行,只需添加分号 ;:

实例

julia> [1 2; 3 4]
2×2 Matrix{Int64}:
 1  2
 3  4

也可以使用冒号 : 和空格来实现:

实例

julia> [1:2  3:4]
2×2 Matrix{Int64}:
 1  3
 2  4

我们也可以在方括号中嵌入多个长度相同的一维数组,并用空格分隔来创建二维数组:

实例

julia> [[1, 2] [3, 4] [5, 6]]
2×3 Matrix{Int64}:
 1  3  5
 2  4  6

julia> [[1 2]; [3 4]; [5 6]]
3×2 Matrix{Int64}:
 1  2
 3  4
 5  6

拼接函数 hcat / vcat / cat

上面方括号里的空格与分号其实是 hcat(水平拼接)和 vcat(垂直拼接)的语法糖,更一般化的拼接用 cat 函数:

语法等价函数说明
[A; B; C]vcat(A, B, C)沿第一维(垂直)拼接
[A B C]hcat(A, B, C)沿第二维(水平)拼接
[A B; C D]hvcat(A, B, C, D)同时垂直和水平拼接
cat(A, B; dims=k)沿第 k 维拼接,最通用

实例

julia> vcat([1, 2], [3, 4])
4-element Vector{Int64}:
 1
 2
 3
 4

julia> hcat([1, 2], [3, 4])
2×2 Matrix{Int64}:
 1  3
 2  4

julia> cat([1 2], [3 4]; dims=3)     # 沿第三维拼接成 3 维数组
2×2×2 Array{Int64, 3}:
[:, :, 1] =
 1  2
 ...

使用范围函数来创建数组

省略号 ...

可以使用省略号 ... 来展开一个范围创建数组:

实例

julia> [0:10...]
11-element Vector{Int64}:
  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10

collect() 函数

collect() 函数语法格式如下:

collect(start:step:stop)

start 为开始值,step 为步长,stop 为结束值,该函数返回数组:

实例

julia> collect(1:2:13)
7-element Vector{Int64}:
  1
  3
  5
  7
  9
 11
 13

collect() 函数也可以指定元素类型:

实例

julia> collect(Float64, 1:2:5)
3-element Vector{Float64}:
 1.0
 3.0
 5.0

range() 函数

range() 函数可以生成一个区间范围并指定步长,可以方便 collect() 函数调用:

实例

julia> range(1, length=100)
1:100

julia> range(1, stop=100)
1:100

julia> range(1, step=5, length=100)
1:5:496

julia> range(1, 10, length=101)
1.0:0.09:10.0

julia> range(start=1, step=1, stop=10)
1:1:10

如果未指定长度 length,且 stop - start 不是 step 的整数倍,则将生成在 stop 之前结束的范围:

julia> range(1, 3.5, step=2)
1.0:2.0:3.0

使用 range() 和 collect() 创建数组:

实例

julia> collect(range(1, stop=10))
10-element Vector{Int64}:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10

注意:range 返回的是惰性的 Range 对象而不是数组,它只存储起点、步长、长度三个数,不管区间多大都不占内存。大多数接受数组的地方也接受 Range,只有在确实需要数组时才 collect。

使用推导式和生成器创建数组

创建数组的另一种有用方法是使用推导式,语法格式如下:

A = [ F(x,y,...) for x=rx, y=ry, ... ]

实例

julia> [n^2 for n in 1:5]
5-element Vector{Int64}:
  1
  4
  9
 16
 25

# 双变量推导创建二维数组
julia> [n*m for n in 1:3, m in 1:3]
3×3 Matrix{Int64}:
 1  2  3
 2  4  6
 3  6  9

也可以在没有方括号的情况下编写推导,从而产生称为生成器的对象:

实例

julia> collect(n^2 for n in 1:5)
5-element Vector{Int64}:
  1
  4
  9
 16
 25

# 生成器不分配中间数组,直接对序列求和
julia> sum(1/n^2 for n=1:1000)
1.6439345666815615

数组基本函数

函数描述
eltype(A)A 中元素的类型
length(A)A 中元素的数量
ndims(A)A 的维数
size(A)一个包含 A 各个维度上元素数量的元组
size(A, n)A 第 n 维中的元素数量
axes(A)一个包含 A 有效索引的元组
eachindex(A)一个访问 A 中每一个位置的高效迭代器
stride(A, k)在第 k 维上相邻元素间的索引距离

实例

julia> A = [1 2 3; 4 5 6];

julia> size(A)
(2, 3)

julia> ndims(A)
2

julia> length(A)
6

julia> eltype(A)
Int64

构造和初始化函数

Julia 提供了许多用于构造和初始化数组的函数。大部分函数的第一个参数表示数组的元素类型 T,如果省略则默认为 Float64:

函数描述
Array{T}(undef, dims...)一个没有初始化的密集 Array
zeros(T, dims...)一个全零 Array
ones(T, dims...)一个元素均为 1 的 Array
trues(dims...)一个每个元素都为 true 的 BitArray
falses(dims...)一个每个元素都为 false 的 BitArray
reshape(A, dims...)一个包含跟 A 相同数据但维数不同的数组
copy(A)拷贝 A
deepcopy(A)深拷贝,递归地拷贝其元素
similar(A, T, dims...)与 A 相同类型的未初始化数组,但具有指定的元素类型和维数
reinterpret(T, A)与 A 具有相同二进制数据的数组,但元素类型为 T
rand(T, dims...)随机数组,元素均匀分布于 [0, 1) 区间(浮点类型)
randn(T, dims...)随机数组,元素为标准正态分布
Matrix{T}(I, m, n)m 行 n 列的单位矩阵(需要先 using LinearAlgebra)
fill!(A, x)用值 x 填充数组 A(原地修改)
fill(x, dims...)一个被值 x 填充的新 Array

zeros() 创建数组的实例:

实例

julia> zeros(Int8, 2, 3)
2×3 Matrix{Int8}:
 0  0  0
 0  0  0

julia> zeros((2, 3))     # 不指定类型默认为 Float64
2×3 Matrix{Float64}:
 0.0  0.0  0.0
 0.0  0.0  0.0

添加、删除与修改元素

Julia 数组大小可变,最常用的增删函数都带感叹号后缀(表示原地修改):

函数描述
push!(A, x)在数组末尾添加元素 x
pop!(A)移除并返回数组末尾的元素
pushfirst!(A, x)在数组开头插入元素 x
popfirst!(A)移除并返回数组开头的元素
append!(A, B)把集合 B 的所有元素追加到 A 末尾
insert!(A, i, x)在第 i 个位置插入元素 x
deleteat!(A, i)删除第 i 个位置的元素
splice!(A, i)删除并返回第 i 个位置的元素
resize!(A, n)调整数组长度为 n
empty!(A)清空数组

实例

julia> arr = Int64[];

julia> push!(arr, 1)          # 末尾添加
1-element Vector{Int64}:
 1

julia> append!(arr, [2, 3])   # 追加多个
3-element Vector{Int64}:
 1
 2
 3

julia> pushfirst!(arr, 0)     # 开头插入
4-element Vector{Int64}:
 0
 1
 2
 3

julia> pop!(arr)              # 弹出末尾
3

julia> insert!(arr, 2, 99)    # 第 2 个位置插入
4-element Vector{Int64}:
  0
 99
  1
  2

julia> deleteat!(arr, 2)      # 删除第 2 个元素
3-element Vector{Int64}:
 0
 1
 2

注意:不要用 arr = push!(arr, 1) 这种写法(虽然结果正确),push! 已经原地修改了 arr,直接调用即可。也不要写 arr = [arr; 1] 来添加元素,它每次都会复制整个数组,性能差。

修改元素与广播

修改单个元素直接用索引赋值,批量修改用点赋值 .=:

实例

julia> A = [1, 2, 3];

julia> A[1] = 100;

julia> A
3-element Vector{Int64}:
 100
   2
   3

# .= 广播赋值:给所有元素加 10(原地修改,不新建数组)
julia> A .+= 10
3-element Vector{Int64}:
 110
  12
  13

# 也可以对部分元素广播赋值
julia> A[1:2] .= 0
2-element view(::Vector{Int64}, 1:2) with eltype Int64:
 0
 0

broadcast(广播)机制可以把不同尺寸的数组或标量按元素组合起来,标量会自动"扩展":

实例

julia> a = [1.0, 2.0]; A = [1.0 2.0; 3.0 4.0];

# 向量 a 逐列加到矩阵 A 上
julia> a .+ A
2×2 Matrix{Float64}:
 2.0  3.0
 5.0  6.0

# 任意函数都可以广播
julia> string.("item", 1:3)
3-element Vector{String}:
 "item1"
 "item2"
 "item3"

性能提示:嵌套的点运算(如 2 .* A .+ B)会被融合成单次循环,不会产生临时数组,因此比 2*A + B(产生两个临时数组)快得多,推荐优先使用点语法。

数组运算

数组支持的运算符:

类别运算符说明
一元算术-、+逐元素取负等
二元算术- + * / \ ^* 是矩阵乘法,其余为线性代数运算
点运算.+ .* .- .^逐元素运算
比较==、.==== 比较整个数组,.== 逐元素比较

实例

julia> A = [1 2; 3 4]; B = [5 6; 7 8];

# * 是矩阵乘法
julia> A * B
2×2 Matrix{Int64}:
 19  22
 43  50

# .* 是逐元素乘法
julia> A .* B
2×2 Matrix{Int64}:
  5  12
 21  32

# == 比较整个数组是否相等,返回一个布尔值
julia> A == [1 2; 3 4]
true

# .== 逐元素比较,返回布尔数组
julia> A .== B
2×2 BitMatrix:
 0  0
 0  0

常用聚合函数

实例

julia> A = [1, 2, 3, 4];

julia> sum(A), prod(A)
(10, 24)

julia> maximum(A), minimum(A)
(4, 1)

julia> argmax(A), argmin(A)     # 最大值/最小值的位置
(4, 1)

julia> mean(A)                  # 平均值需要 using Statistics
2.5

julia> sort([3, 1, 2])
3-element Vector{Int64}:
 1
 2
 3

# 沿维度聚合,dims=1 表示按列求和
julia> sum([1 2; 3 4], dims=1)
1×2 Matrix{Int64}:
 4  6