Java数组

77 阅读5分钟

Java数组

数组

一维数组

image-20220330093229938

数组是引用数据类型,可以存放多个同一类型的数据,引用数据类型有默认初始值

数据类型byteshortcharintlongfloatdoublebooleanString
初始值00\u0000000.00.0falsenull

数组的创建

// 先声明后初始化
// 声明数组名
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、引用类型的变量保存引用值,所谓的引用值就是对象所在内存空间的“首地址值”,通过对这个引用值来操作对象。(引用传递)

常见的引用类型:类类型,接口类型和数组

举例说明

内存图(代码如下)

image-20220330121211030

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{}  // 子类