基础知识

很多道理往往可以用简单的话说明白。 关于 Java 数组,你只需要记住两句话:

1. 数组是相同类型数据的有序集合;

2. 数组也是对象。

展开说说:

  1. 长度确定且不可改;
  2. 类型不可改;
  3. 类型可基本可引用;
  4. 数据的变量属于引用类型,数组也是对象,数据的元素相当于对象的属性。

Java 数组初始化的三种方式:

  1. 静态初始化:一开始就把值给好了;
  2. 动态初始化:一开始给个长度,然后再逐个赋值;
  3. 默认初始化:动态初始化前一步,然后就用这个系统默认给的值:0、0.0、false、null。

练习点:

  1. Array.toString([]);
  2. System.arraycopy(原数组, 原数组开始的索引, 要拷贝到的数组, 目标数组开始拷贝的索引, 从原数组拷贝的长度);
  3. for(type i: array) 来逐个遍历;
  4. 三种初始化方法;

练习代码如下:

public class CopyArray {
    public static void main(String[] args) {
        String[] companies = new String[]{"alibaba", "bytedance",
                "pinduoduo", "dingdong", "souhu"};
        String[] copy2 = new String[3];
        System.arraycopy(companies, 0, copy2, 0, 3);

        for (String s: copy2) {
            System.out.println(s);
        }
    }

}
/** 结果:
* alibaba
* bytedance
* pinduoduo
*/
public class Test02 {

    public static void main(String[] args) {
        // 静态初始化要在声明后直接初始化。
        Man m = new Man(20,"SuperKris");
        // Man[] mans = new Man[6]; 这一行相当于没有用了
        Man[] mans = new Man[]{
                new Man(10, "Kris0"),
                new Man(11, "Kris1"),
                new Man(13, "Kris3"),
                new Man(12, "Kris2"),
                new Man(15, "Kris5"),
        };

        mans[4] = m;

        for (int i=0; i<mans.length; i++) {
            System.out.println(mans[i].getName());
        }
        // 增强 for 循环,把每一个元素取出来,放给这个变量。
        for (Man man: mans) {
            System.out.println(man);
        }
    }

}

class Man {
    private int id;
    private String name;
    
    public Man() {
    }
    public Man(int id, String name) {
        this.id = id;
        this.name = name;
    }
  
    @Override
    public String toString() {
        return "id = " + id + ", name = " + name;
    }
    //省略了 JavaBean()
   
}
public class Test03 {
    public static void main(String[] args) {
        int[] a = {100, 200, 300};
        int[] b = {1,2,3,4234,22,45,765};

        System.out.println(Arrays.toString(a));

        Arrays.sort(b);
        System.out.println(b.toString());
        // 二分查找
        System.out.println("b: " + Arrays.toString(b));
        // 存在的话,就返回索引
        System.out.println("该元素的索引是: " + Arrays.binarySearch(b, 22));
        // 这个数不存在的话,返回负数
        System.out.println("该元素的索引是: " + Arrays.binarySearch(b, 555));

        // 把 b 从 fromIndex,到 toIndex-1 对应的位置,复制为最后一个参数  [) 区间一般都是左开右闭
        Arrays.fill(b, 0,3,500);
        System.out.println(Arrays.toString(b));

    }
}

运行结果如下图:

java初始化数组的方式(Java 数组初始化及常用操作)(1)

查看源码的技巧:

在 IDEA 里按住 Alt/command 键,然后鼠标悬在想看的地方就可以访问到

有两个要注意的地方:

1. Array.toString() 和之前 String里,Object里的 toString 不一样。

这个是普通的 toString 方法:

public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
复制代码

这个是 Array.toString()的源码,是有参数的,而且声明了 static:

public static String toString(int[] a) {
    if (a == null)
        return "null";
    int iMax = a.length - 1;
    if (iMax == -1)
        return "[]";

    StringBuilder b = new StringBuilder();
    b.append('[');
    for (int i = 0; ; i++) {
        b.append(a[i]);
        if (i == iMax)
            return b.append(']').toString();
        b.append(", ");
    }
}

2. 为什么自带的二分法,如果一个数找不到,返回的索引是 -6 呢?

这个时候我们就可以去看一眼源码一窥真相。

二分查找的源码:

private static int binarySearch0(int[] a, int fromIndex, int toIndex,
                                 int key) {
    int low = fromIndex;
    int high = toIndex - 1;

    while (low <= high) {
        int mid = (low + high) >>> 1;
        int midVal = a[mid];

        if (midVal < key)
            low = mid + 1;
        else if (midVal > key)
            high = mid - 1;
        else
            return mid; // key found
    }
    return -(low + 1);  // key not found.
}

然后你就会发现在最后一行:没找到的话,默认取了左边最靠近的索引+1,再取负数。

而且还有一个地方非常的细节,二分法取中间的话,源码用了位移的操作,没有写公式,有效地防止了溢出。