Java:数组

数组的基本概念

Java里面将数组定义为了引用数据类型,所以数据的使用一定牵扯到内存分配,那么一定可以想到使用关键字new来处理,数组的定义格式:

  • 数组的动态初始化:初始化之后数组每一个元素的保存内容为其对应数据类型的默认值;
    • 声明并初始化数组:
      • 数据类型 数组名称 [] = new 数据类型 [长度];
      • 数据类型 [] 数组名称 = new 数据类型 [长度];
  • 数据的静态初始化:在数据定义的时候就为其设置好了里面的内容
    • 简化格式:数据类型 数据名称 [] = {数据1 , 数据2 , 数据3 , …};
    • 完整格式:数据类型 数据名称 [] = new 数据类型 [] {数据1 , 数据2 , 数据3 , …}; // 匿名数组

当创建了一个数组之后就可以按照如下的方式进行使用:

  • 数组里面可以通过脚标进行每一个元素的访问,脚标从0开始定义,所以可以使用的脚标范围:“0~数据长度-1”,注意:如果使用的时候超过了数组脚标范围则会出现“ArrayIndexOutOfBoundsException(数组越界)”异常。
  • 使用数组是为了其可以进行方便的变量管理,所以再进行数组操作的时候往往会利用for循环来完成;

再以后的项目开发过程之中,见到最多的数组使用形式:进行数组的循环处理。

数组本身分为动态初始化与静态初始化,上面使用的是动态初始化,动态初始化之后会发现数组之中的每一个元素的内容都是其对应数据类型的默认值,随后可以通过下标为数组进行内容的设置,如果现在不希望这么复杂,而是希望数组定义的时候就已经可以提供内容,则可以采用静态初始化的方式完成。

对于数组的操作而言,基本上都是拿到数据后循环控制。

数据的引用传递

通过数组的进本定义可以发现,在数组的使用过程之中依然需要关键字new进行内存空间的开辟,同理,那么这里一定存在内存的关系匹配。

范例:定义一个简单代码

public class ArrayDemo{
    public static void main(String[] args){
        int data [] = new int [3] ;
        data [0] = 10;
        data [1] = 20;
        data [2] = 30;
        for(int x = 0 ; x < data.length ; x ++ ){
            System.out.println(data[x]);
        }
    }
}

数组本身属于引用数据类型,既然是引用数据类型,就一定会发生引用传递,引用传递应该还是按照传统方式:一个堆内存可以被多个栈内存所指向。

由于数组属于引用类型,所以一定要为其开辟对内存空间之后才可以使用,如果现在使用了未开辟堆内存空间的数组,会出现”NullPointException“异常。

foreach迭代输出

对于数组而言,一般都会使用for循环进行输出,但是在使用传统for循环输出的时候往往都采用了下标的形式进行数组元素的访问。

public class demo01 {
    public static void main(String[] args) {
        int data [] = new int [] {1,2,3,4,5};
        for (int x = 0; x < data.length ; x++){
            System.out.println(data[x]);
        }
    }
}

从JDK1.5之后为了减轻下标对程序的影响(如果下标处理不当会产生数组越界异常),所以参考了.NET中的设计,引入了一个增强型for循环(foreach),利用foreach的语法结构可以直接自动获取数组中的每一个元素,避免下标访问,语法:for(数据类型 变量 : 数组 | 集合){}

最大的特点在于可以将数组中的每一个元素的内容取出保存在变量里面,这样就可以直接通过变量获取数组内容避免下标的方式来获取了

范例:使用foreach语法形式输出

public class demo01 {
    public static void main(String[] args) {
        int data [] = new int [] {1,2,3,4,5};
        for (int temp : data){//自动循环,将data数组每一个内容交给temp
            System.out.println(temp);
        }

    }
}

这种语法的好处是可以避免下标操作。

二维数据

在之前所定义的数组里面会发现只有一个”[]“,所以此时的数组就好像一行数据一样,可以利用下标进行行数据的访问

  • 传统的数组就好比一行数据,如果想要找到一个数据只需确定一个下标即可。
  • 如果说现在你需要的是一个多行多列的结构(表),则就需要通过两个下标才可以描述出一个数据。

对于二维数组可以使用的定义语法如下:

  • 数组的动态初始化:
    • 数据类型 数组名称 [][] = new 数据类型 [行个数][列个数];
  • 数据的静态初始化:
    • 数据类型 数组名称 [][] = new 数据类型 [][] {{数据,数据,…},{数据,数据,…},{数据,数据,…},…}

既然二维数组的每一行都属于一个数组,那么就可以通过每一行的数组求出数组长度。

public class demo01 {
    public static void main(String[] args) {
        int data [][] = new int [][] {{1,2,3,4,5},{1,2,3},{5,6,7,8}};
        for ( int x = 0; x < data.length;x++){
            for (int y = 0 ; y < data[x].length;y++){
                System.out.println("data["+x+"]["+y+"]=" + data[x][y]);
            }
            System.out.println();
        }
    }
}

如果这个时候要求使用foreach进行输出。

范例:使用foreach进行二维数组的输出

public class demo01 {
    public static void main(String[] args) {
        int data[][] = new int[][]{{1, 2, 3, 4, 5}, {1, 2, 3}, {5, 6, 7, 8}};
        for (int temp[] : data) {
            for (int num : temp) {
                System.out.print(num + "、");
            }
            System.out.println();
        }
    }
}

通过foreach的输出格式可以清楚地观察到,二维数组就是数组的嵌套使用,如果要进行一些应用层的程序开发,那么很少会涉及到二维数组,更不用说更高级的多维数组。

数组与方法

对于引用数据类型而言,主要的特点是可以与方法进行引用传递,而数组本身也属于引用数据类型,也可以通过方法实现引用传递的操作

范例:实现一个数组的引用传递。

public class demo01 {
    public static void main(String[] args) {
        int data [] = new int [] {1,2,3,4,5};
        printArray(data);
    }
    public static void printArray(int temp [] ){
        for (int x = 0;x < temp.length ; x++ ){
            System.out.println(temp[x]);
        }
    }
}

对于此时的引用传递具体的内存关系如下。既然可以通过方法接受一个数组,那么也就可以通过方法返回一个数组对象,那么此时只需要在方法的返回值类型上进行控制即可。

public class demo01 {
    public static void main(String[] args) {
        int data [] = initArray();
        printArray(data); //传递数组
    }
    public static int [] initArray(){
        int arr [] = new int [] {1,2,3,4,5};
        return arr; //返回一个数组
    }
    public static void printArray(int temp [] ){ //要求接受一个int型的数组
        for (int x = 0;x < temp.length ; x++ ){
            System.out.println(temp[x]);
        }
    }
}

下面来针对此程序进行内存分析:

范例:通过方法修改数组内容

public class demo01 {
    public static void main(String[] args) {
        int data [] = new int [] {1,2,3,4,5};
        changeArray(data); //修改数组内容
        printArray(data); //传递数组
    }
    public static void changeArray(int arr[]){
        for ( int x = 0 ; x < arr.length ; x++){
            arr[x] *= 2 ;  //每个元素的内容乘以2 保存
        }
    }
    public static void printArray(int temp [] ){ //要求接受一个int型的数组
        for (int x = 0;x < temp.length ; x++ ){
            System.out.println(temp[x]);
        }
    }
}

本程序的内存关系图:

范例:求数组的总和、平均值、最大值、最小值。

public class demo01 {
    public static void main(String[] args) {
        int data [] = new int [] {1,2,3,4,5};
        printArray(data); //传递数组
        int sum = 0;
        int max = data[0];
        int min = data[0];
        double avg = 0.0;
        for (int x = 0; x < data.length ; x++){
            if (data[x] > max){
                max = data[x];
            }else if(data[x] < min){
                min = data[x];
            }
            sum += x;
        }
        avg = sum / data.length;
        System.out.println("数组内容总和:" + sum );
        System.out.println("数组内容平均值:" + avg );
        System.out.println("数组内容最大值:" + max );
        System.out.println("数组内容最小值:" + min );
    }
    public static void printArray(int temp [] ){ //要求接受一个int型的数组
        for (int x = 0;x < temp.length ; x++ ){
            System.out.println(temp[x]);
        }
    }
}

主方法所在类往往被称为主类,那么既然是主类肯定不希望涉及过于复杂的功能。在进行开发的过程中,主方法本身就相当于一个客户端,而对于客户端的代码应该尽量简单一些,此时最好将这一系列的计算过程交给单独的程序类去完成。

范例:对上方代码改善操作设计

class ArrayUtil{
    private int sum;
    private double avg;
    private int max;
    private int min;

    public ArrayUtil(int data[]){ //进行数组计算
        this.max = data[0];
        this.min = data[0];
        for (int x = 0; x < data.length ; x++){
            if (data[x] > max){
                this.max = data[x];
            }else if(data[x] < min){
                this.min = data[x];
            }
            this.sum += data[x];
        }
        this.avg = this.sum / data.length;
    }
    public int getSum(){
        return this.sum;
    }
    public double getAvg(){
        return this.avg;
    }
    public int getMax(){
        return this.max;
    }
    public int getMin(){
        return this.min;
    }
}

public class demo01 {
    public static void main(String[] args) {
        int data [] = new int [] {1,2,3,4,5};
        ArrayUtil util = new ArrayUtil(data);
        System.out.println("数组内容总和:" + util.getSum() );
        System.out.println("数组内容平均值:" + util.getAvg() );
        System.out.println("数组内容最大值:" + util.getMax() );
        System.out.println("数组内容最小值:" + util.getMin() );
    }
}

此时的主类就好比我们使用电脑一样,只关心如何操作,而具体的操作过程被类进行包装了。

在以后进行类设计的时候,如果发现类中没有属性存在的意义,那么定义的方法就没有必要使用普通方法了,因为我们普通方法需要在有实例化对象产生的情况下才可以调用。所以用static方法就可以了,用类名称进行调用。

数组相关类库操作方法

由于数组是一个重要的概念,所以在java语言本身也提供有数组的相关支持处理,这些处理时在开发中使用的。

  • 数组排序:
    • java.util.Arrays.sort(数组名称)
  • 数组拷贝(把方法做一些变形):
    • System.arraycopy(源数组,源数组开始点,目标数组,目标数组开始点,拷贝长度)

方法可变参数

如果说现在要求定义一个方法,这个方法可以实现任意多个整型数据的相加处理。这样的情况下最早的时候只能够通过数组来进行处理。

class ArrayUtil02{
    public static int sum(int ... data) {
        int sum = 0;
        for (int temp : data) {
            sum += temp;
        }
        return sum;
    }
}
public class demo02 {
    public static void main(String[] args) {
        System.out.println(ArrayUtil02.sum(1,2,3));

        System.out.println(ArrayUtil02.sum(new int[] {1,2,3}));
    }
}

可变参数的最大作用在于,在以后进行一些程序类设计或者开发者调用的时候,利用此种形式就可以避免数组的传递操作。可变参数的本质需要清楚的是,依然属于数组。

对象数组

在之前所接触到的都是基本数据类型定义的数组,但是在java程序本身各种数据类型都可以成为数组类型,所以类也可以成为数组类型,而这样的数组就成为对象数组,对象数组的定义格式如下:

  • 动态初始化:类 对象数组名称 [] = new 类 [长度],每一个元素的内容都是null。
  • 静态初始化:类 对象数组名称 [] = new 类 []{实例化对象,实例化对象,…}。

范例:使用动态初始化定义数组。

class Person{
    private String name;
    private int age;
    public Person(String name , int age ){
        this.age = age;
        this.name = name;
    }
    public String getInfo(){
        return "姓名:" + this.name + "、年龄:"  + this.age ;
    }
    //setter getter 略
}
public class demo03 {
    public static void main(String[] args) {
        Person per [] = new Person [3];
        per[0] = new Person("张三",20);
        per[1] = new Person("张四",30);
        per[2] = new Person("张五",40);
        for (int x = 0 ; x < per.length ; x ++){
            System.out.println(per[x].getInfo());
        }
    }
}

范例:使用静态初始化定义数组。

class Person{
    private String name;
    private int age;
    public Person(String name , int age ){
        this.age = age;
        this.name = name;
    }
    public String getInfo(){
        return "姓名:" + this.name + "、年龄:"  + this.age ;
    }
    //setter getter 略
}
public class demo03 {
    public static void main(String[] args) {
        Person per[] = new Person[]{

            new Person("张三",20),
            new Person("张四",30),
            new Person("张五",40)};
        for (Person person : per) {
            System.out.println(person.getInfo());
        }
    }
}

对于对象数组而言,本身只是更换了一种所谓的数组定义的类型,但是此时的内存图就比较麻烦了。

所有的开发都不可能离开对象数组,但是通过一系列的分析也应该知道一个数组中的最大缺陷:长度是固定的。优势:数据线性保存,根据索引访问,速度较快(时间复杂度为O(1))。

标签: