Java数组
数组
一维数组
数组是引用数据类型,可以存放多个同一类型的数据,引用数据类型有默认初始值
| 数据类型 | byte | short | char | int | long | float | double | boolean | String |
|---|---|---|---|---|---|---|---|---|---|
| 初始值 | 0 | 0 | \u0000 | 0 | 0 | 0.0 | 0.0 | false | null |
数组的创建
// 先声明后初始化
// 声明数组名
int[] a; // 定义数组数据类型 和 数组名
或者
int a[]; // 推荐使用上面的形式 简洁明了
// 初始化数组
a = new int[5]; // 5 指的是数组长度(a.length)
// 声明 + 初始化
int[] b = new int[5]; // 5 指的是数组长度(a.length)
// 通过下标/索引 [0,array.length)来访问数组值 如:a[1]指的是第二个数组
// 静态初始化 存入数组值
int[] c = {2,3,4,5,6}; // 最后一个值后面的逗号可加可不加
// 注意格式
Integer[] num = new Integer{2,3,4,5,6} // error
Integer[] num = new Integer[]{2,3,4,5,6} // ok
Integer[] num = new Integer[5]{2,3,4,5,6} // error
值传递和引用传递
1、基本类型的变量保存原始值,所以变量就是数据本身(值传递)
常见的基本类型:byte,short,int,long,char,float,double,Boolean
2、引用类型的变量保存引用值,所谓的引用值就是对象所在内存空间的“首地址值”,通过对这个引用值来操作对象。(引用传递)
常见的引用类型:类类型,接口类型和数组
举例说明
内存图(代码如下)
public static void main(String[] args){
// 值传递
int m = 10;
int n = m;
n = 20;
System.out.println("m: "+ m +"n: "+ n); // m: 10 n: 20
// 引用传递
int[] arr1 = {1,2,3};
int[] arr2 = arr1;
arr[0] = 10;
// 结果:arr1: 10 2 3 ; arr2: 10 2 3
}
数组拷贝
Arrays类的copyOf方法:复制指定的数组
注意:数组拷贝 != 数组赋值(引用传递)
// copyOf方法源码分析
public static int[] copyOf(int[] original, int newLength) {
int[] copy = new int[newLength];
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
// 本地方法
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
// jvm.cpp文件
JVM_ENTRY(void, JVM_ArrayCopy(JNIEnv *env, jclass ignored, jobject src, jint src_pos,
jobject dst, jint dst_pos, jint length))
JVMWrapper("JVM_ArrayCopy");
// 检查源数组和目的数组是否为null
if (src == NULL || dst == NULL) {
THROW(vmSymbols::java_lang_NullPointerException());
}
arrayOop s = arrayOop(JNIHandles::resolve_non_null(src));
arrayOop d = arrayOop(JNIHandles::resolve_non_null(dst));
assert(s->is_oop(), "JVM_ArrayCopy: src not an oop");
assert(d->is_oop(), "JVM_ArrayCopy: dst not an oop");
//真正调用复制的方法
s->klass()->copy_array(s, src_pos, d, dst_pos, length, thread);
JVM_END
// 调用copy_array方法
void ObjArrayKlass::copy_array(arrayOop s, int src_pos, arrayOop d,
int dst_pos, int length, TRAPS) {
//检测s是数组
assert(s->is_objArray(), "must be obj array");
//目的数组不是数组对象的话,则抛出ArrayStoreException异常
if (!d->is_objArray()) {
THROW(vmSymbols::java_lang_ArrayStoreException());
}
//检测下标参数非负
if (src_pos < 0 || dst_pos < 0 || length < 0) {
THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException());
}
//检测下标参数是否越界
if ( (((unsigned int) length + (unsigned int) src_pos) > (unsigned int) s->length())
|| (((unsigned int) length + (unsigned int) dst_pos) > (unsigned int) d->length()) ) {
THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException());
}
// 如果长度为0则无需复制
if (length==0) {
return;
}
//UseCompressedOops只是用来区分narrowOop和oop
//调用do_copy函数来复制
if (UseCompressedOops) {
narrowOop* const src = objArrayOop(s)->obj_at_addr<narrowOop>(src_pos);
narrowOop* const dst = objArrayOop(d)->obj_at_addr<narrowOop>(dst_pos);
do_copy<narrowOop>(s, src, d, dst, length, CHECK);
} else {
oop* const src = objArrayOop(s)->obj_at_addr<oop>(src_pos);
oop* const dst = objArrayOop(d)->obj_at_addr<oop>(dst_pos);
do_copy<oop> (s, src, d, dst, length, CHECK);
}
}
// 调用do_copy方法
// Either oop or narrowOop depending on UseCompressedOops.
template <class T> void ObjArrayKlass::do_copy(arrayOop s, T* src,
arrayOop d, T* dst, int length, TRAPS) {
BarrierSet* bs = Universe::heap()->barrier_set();
// 出于性能原因,我们假设我们使用的写屏障具有针对引用数组的优化模式。
// 如果不是这种情况,至少以下断言之一将失败
assert(bs->has_write_ref_array_opt(), "Barrier set must have ref array opt");
assert(bs->has_write_ref_array_pre_opt(), "For pre-barrier as well.");
if (s == d) {
// since source and destination are equal we do not need conversion checks.
assert(length > 0, "sanity check");
bs->write_ref_array_pre(dst, length);
// 复制的函数
Copy::conjoint_oops_atomic(src, dst, length);
} else {
// We have to make sure all elements conform to the destination array
Klass* bound = ObjArrayKlass::cast(d->klass())->element_klass();
Klass* stype = ObjArrayKlass::cast(s->klass())->element_klass();
if (stype == bound || stype->is_subtype_of(bound)) {
// elements are guaranteed to be subtypes, so no check necessary
//stype对象是bound,或者stype是bound的子类抑或stype实现bound接口
bs->write_ref_array_pre(dst, length);
Copy::conjoint_oops_atomic(src, dst, length);
} else {
// slow case: need individual subtype checks
// note: don't use obj_at_put below because it includes a redundant store check
T* from = src;
T* end = from + length;
for (T* p = dst; from < end; from++, p++) {
// XXX this is going to be slow.
T element = *from;
// even slower now
bool element_is_null = oopDesc::is_null(element);
oop new_val = element_is_null ? oop(NULL)
: oopDesc::decode_heap_oop_not_null(element);
if (element_is_null ||
(new_val->klass())->is_subtype_of(bound)) {
bs->write_ref_field_pre(p, new_val);
*p = element;
} else {
// We must do a barrier to cover the partial copy.
const size_t pd = pointer_delta(p, dst, (size_t)heapOopSize);
// pointer delta is scaled to number of elements (length field in
// objArrayOop) which we assume is 32 bit.
assert(pd == (size_t)(int)pd, "length field overflow");
bs->write_ref_array((HeapWord*)dst, pd);
THROW(vmSymbols::java_lang_ArrayStoreException());
return;
}
}
}
}
bs->write_ref_array((HeapWord*)dst, length);
}
二维数组
数组的创建
// 先声明后初始化
// 声明方式
int[][] a; int[] a[]; int a[][];
int[] x,y[]; // x 是一维数组 y 是二维数组
// 初始化二维数组
a[n] new int[5]; //n 的取值范围 [0,5) ,表示的是某一行的一维数组元素
// 动态初始化
int[][] twoArray = new int[5][]; // 5个一维数组,每个一维数组元素可以不相同
int[][] twoArray = new int[5][5]; // 5*5的二维数组
twoArray.length; // 二维数组的行数(二维数组元素个数)
twoArray[n].length; // n 的取值范围 [0,twoArray.length) ,表示的是某一行的一维数组元素
// 静态初始化
// 每个二维数组的每行元素并不一定相等
int[][] a = {{1,2},{3,4,5,6,},{1,2,3}};
for (int i = 0; i < a.length; i++) {
for (int j = 0; j < a[i].length; j++) {
System.out.print("\t"+a[i][j]);
}
System.out.println();
}
1 2
3 4 5 6
1 2 3
内存分析
public static void main(String[] args){
int arr[][] = new int[2][3];
arr[1][1] = 8;
}
习题
// 杨辉三角
final int N = 10;
int[][] array = new int[N][];
for (int i = 0; i < N; i++) {
array[i] = new int[i+1];
for (int j = 0; j <= i; j++) {
if(j == 0 || j == i){
array[i][j] = 1;
}else{
array[i][j] = array [i-1][j] + array[i-1][j-1];
}
}
}
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
System.out.print(array[i][j]+" ");
}
System.out.println();
}
// 实现效果
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
1 8 28 56 70 56 28 8 1
1 9 36 84 126 126 84 36 9 1
多态数组
数组的定义类型为父类类型,里面保存的实际元素类型为子类类型
// 初始化多态数组
public class attay{
public static void main(String[]args){
// 向上转型
// 创建5个对象: 一个人 二个学生 二个老师
person[] p = new person[5];
p[0] = new person();
p[1] = new student();
p[2] = new student();
p[3] = new teacher();
p[4] = new teacher();
}
}
class person{} // 父类
class student extends person{} // 子类
class teacher extends person{} // 子类