(四)java中的数组

版权声明:转发请标明出处,谢谢! https://blog.csdn.net/Myuhua/article/details/81506378

数组的概述

数组可以看成多个相同类型数据的集合,对这些数据的统一管理。

数组变量属引用类型,数组也可以看成是对象,数组中的每个元素相当于对该对象的成员变量。

数组中的元素可以是任何数据类型,包括基本类型和引用类型。

(一)一维数组

(1)一维数组的声明

一维数组的声明方式:type var[ ];或type[ ] var;

package array;

import oop.Person;

/**
 * 说明:数组声明举例
 *
 * @author huayu
 * @date 2018/8/8 1:01 PM
 */
public class ArrayDemo {
    //数组内装的是int类型的数据
    int a[];
    int[] b;
    //数组内装的是double类型的数据
    double c[];
    //这里面装的是Person对象的引用
    Person[] p;
    //数组内装的是String对象的引用
    String string[];
}

java语言中声明数组时不能指定其长度(数组中元素的个数),例如:int a[5];  //非法  因为java中的数组分布在堆上面。

(2)数组对象的创建及内存分析

1)java中使用关键字new 创建数组对象,格式为:数组名=new 数组元素的类型[数组元素的个数]

例如:

/**
 * 说明:创建一个数组,并赋初值
 *
 * @author huayu
 * @date 2018/8/8 1:15 PM
 */
public class CreateArrayDemo {
    public static void main(String[] args) {
         //声明数组array
1.       int[] array;
         //为数组array分配空间
2.       array = new int[5];
         //给数组赋值
3.       for (int i = 0; i < array.length ; i++) {
            array[i]=2*i+1;
            System.out.println("数组下标index["+i+"]"+" array[i]= "+array[i]);
        }
    }
}

结果:
数组下标index[0] array[i]= 1
数组下标index[1] array[i]= 3
数组下标index[2] array[i]= 5
数组下标index[3] array[i]= 7
数组下标index[4] array[i]= 9

对于以上小demo进行内存分析:

执行第1句 int[] array; 这时是只声明了一个int类型的array数组,此时内存状态:

执行第2句array = new int[5]; 这时为声明了的array数组申请空间(new出来的东西放堆内存里面),并为数组初始化赋上了int的默认值0,而栈内存的array引用也有了值;此时内存状态:

执行到第3句for循环,为数组赋值,此时的内存状态:

2)元素是引用数据类型的数组:new出来的对象装的是引用。

注意:元素为引用数据类型的数组中的每一个元素都需要实例化。

package array;

/**
 * 说明:自定义Date类
 *
 * @author huayu
 * @date 2018/8/8 1:49 PM
 */
public class Date {
    private int year;
    private int month;
    private int day;

    public Date(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }
}


import array.Date;

/**
 * 说明:引用类型的数组
 *
 * @author huayu
 * @date 2018/8/8 1:51 PM
 */
public class TestDate {
    public static void main(String[] args) {
1.        Date[] dates;
2.        dates=new Date[3];
3.        for (int i = 0; i <dates.length ; i++) {
            dates[i]=new Date(1970,1,1);
            System.out.println("数组下标index["+i+"]"+" dates[i]= "+dates[i].toString());
        }
    }
}

结果:
数组下标index[0] dates[i]= array.Date@511d50c0
数组下标index[1] dates[i]= array.Date@60e53b93
数组下标index[2] dates[i]= array.Date@5e2de80c

内存分析:

执行到第2句Date[] dates;内存状态(怎么来的我就不做过多解释了,看上面的栗子):

执行第3句for循环,每循环一次就创建一个新对象,执行完后的内存状态:

(3)数组初始化(时刻记得,数组要先分配空间再去赋值)

1)动态初始化:数组定义与为数组元素分配空间和赋值的操作分开进行,例如:

package array;

/**
 * 说明:自定义Date类
 *
 * @author huayu
 * @date 2018/8/8 1:49 PM
 */
public class Date {
    private int year;
    private int month;
    private int day;

    public Date(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }
}

import array.Date;

/**
 * 说明:动态初始化释例
 *
 * @author huayu
 * @date 2018/8/8 1:51 PM
 */
public class TestDate {
    public static void main(String[] args) {
        int a[];
        a = new int[3];
        a[0] = 0;
        a[1] = 1;
        a[2] = 2;
        for (int i = 0; i < a.length; i++) {
            System.out.println("a["+i+"]= "+a[i]);
        }
        Date[] dates;
        dates = new Date[3];
        dates[0]=new Date(2018,1,1);
        dates[1]=new Date(2018,2,2);
        dates[2]=new Date(2018,3,3);
        for (int i = 0; i < dates.length; i++) {
            System.out.println("数组下标index[" + i + "]" + " dates[i]= " + dates[i].toString());
        }
    }
}

结果:
a[0]= 0
a[1]= 1
a[2]= 2
数组下标index[0] dates[i]= array.Date@511d50c0
数组下标index[1] dates[i]= array.Date@60e53b93
数组下标index[2] dates[i]= array.Date@5e2de80c

2)静态初始化:在定义数组的同时就为数组元素分配空间并赋值,例如

package array;

/**
 * 说明:自定义Date类
 *
 * @author huayu
 * @date 2018/8/8 1:49 PM
 */
public class Date {
    private int year;
    private int month;
    private int day;

    public Date(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }
}

import array.Date;

/**
 * 说明:静态初始化释例,它在执行过程其实还是向动态那样,只不过它现在给屏蔽掉,我们看不到而已
 *
 * @author huayu
 * @date 2018/8/8 1:51 PM
 */
public class TestDate {
    public static void main(String[] args) {
        int a[] = {1, 2, 3};
        for (int i = 0; i < a.length; i++) {
            System.out.println("a[" + i + "]= " + a[i]);
        }
        Date[] dates = {
                new Date(2018, 1, 1),
                new Date(2018, 2, 2),
                new Date(2018, 3, 3)
        };

        for (int i = 0; i < dates.length; i++) {
            System.out.println("数组下标index[" + i + "]" + " dates[i]= " + dates[i].toString());
        }
    }
}

结果:
a[0]= 1
a[1]= 2
a[2]= 3
数组下标index[0] dates[i]= array.Date@511d50c0
数组下标index[1] dates[i]= array.Date@60e53b93
数组下标index[2] dates[i]= array.Date@5e2de80c

3)数组元素的默认初始化:数组是引用类型,它的元素相当于类的成员变量,因此数组分配空间后,每个元素也被按照成员变量的规则被隐式初始化,例如:

package array;

/**
 * 说明:自定义Date类
 *
 * @author huayu
 * @date 2018/8/8 1:49 PM
 */
public class Date {
    private int year;
    private int month;
    private int day;

    public Date(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }
}

import array.Date;

/**
 * 说明:数组元素默认初始化演示
 *      1.int的类型的默认值是0(以前讲过各种类型变量的默认值,忘了的可以再去瞄一眼)
 *      2.引用类型的默认值为null
 * @author huayu
 * @date 2018/8/8 1:51 PM
 */
public class TestDate {
    public static void main(String[] args) {
        int a[] = new int[3];
        Date[] dates = new Date[3];
        System.out.println(a[2]);
        System.out.println(dates[2]);
    }
}

结果:
0
null

(4)数组元素的引用(访问数组里面的内容)

1)定义并用运算符new为之分配空间后,才可以引用数组中的每个元素,数组元素的引用方式为:arrayName[index]

注:1.index为数组元素的下标,可以是整型常量或表达式。如a[1],a[i],a[2*i]

       2.数组元素下标从0开始,长度为n的数组的合法下标取值范围为0~n-1

2)每个数组都有一个属性length指明它的长度,例如:array.length的值为数组array的长度(元素的个数)。上面例子已经用过了。

知识连接:数组没有 length()方法,有 length 的属性。String 有 length()方法。JavaScript 中,获得字符串的长度是通过 length 属性得到的,这一点容易和 Java 混淆。

(5)用数组写几个小程序

1)为乱序的int数组排序

以下这个程序自己对每一次的循环结果都在纸上画一下理解一下,后期我会整理一篇通过算法为数组排序的专题文章。

package array;

/**
 * 说明:为a数组排序(简单选择)
 * 先不考虑程序健壮性,只考虑功能的实现
 * 这个方法后期完全可以被封装被优化,感兴趣的自己试一下
 * @author huayu
 * @date 2018/8/8 3:40 PM
 */
public class NumSort {
    public static void main(String[] args) {
        int[] a = {2, 1, 5, 3, 4, 9, 6, 8, 7};
        for (int i = 0; i < a.length; i++) {
            for (int j = i + 1; j < a.length; j++) {
                if (a[j] < a[i]) {
                    int temp = a[i];
                    a[i] = a[j];
                    a[j] = temp;
                }
            }
            System.out.print(a[i]+",");
        }
    }
}


结果:
1,2,3,4,5,6,7,8,9,

2)对引用(某个类的对象)类型的数据排序

package array;

/**
 * 说明:自定义Date类
 *
 * @author huayu
 * @date 2018/8/8 1:49 PM
 */
public class Date{
    private int year;
    private int month;
    private int day;

    public Date(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    /**
     * 比较方法
     * @param date
     * @return
     */
    public int compare(Date date) {
        //想必大家看到这个三目运算符也懵逼了吧,三目运算符还可以这么玩,大家自己捋一下吧
        //现实工作中别这么写,容易被祭天
        return year > date.year ? 1
               : year < date.year ? -1
               : month > date.month ? 1
               : month < date.month ? -1
               : day > date.day ? 1
               : day > date.day ? -1 : 0;
    }

    /**
     * 重写toString方法是为了方便看输出来的结果
     * @return
     */
    @Override
    public String toString() {
        return "Date{" +
                "year=" + year +
                ", month=" + month +
                ", day=" + day +
                '}';
    }
}

import array.Date;

/**
 * 说明:日期输入1970年1月1日以后的
 *
 * @author huayu
 * @date 2018/8/8 1:51 PM
 */
public class TestDateSort {
    public static void main(String[] args) {
        Date[] date = new Date[3];
        date[0] = new Date(2001, 1, 1);
        date[1] = new Date(1992, 4, 1);
        date[2] = new Date(2096, 1, 5);

        for (int i = 0; i < date.length; i++) {
            System.out.println("排序前index[" + i + "]" + date[i]);
        }
        System.out.println();
        Date[] dates = bubbleSort(date);
        for (int i = 0; i < date.length; i++) {
            System.out.println("排序后index[" + i + "]" + dates[i]);
        }
    }

    public static Date[] bubbleSort(Date[] dates) {
        int length = dates.length;
        for (int i = length - 1; i >= 1; i--) {
            for (int j = 0; j < i - 1; j++) {
                if (dates[j].compare(dates[j + 1]) > 0) {
                    Date temp = dates[j];
                    dates[j] = dates[j + 1];
                    dates[j + 1] = temp;
                }
            }
        }
        return dates;
    }
}

结果:
排序前index[0]Date{year=2001, month=1, day=1}
排序前index[1]Date{year=1992, month=4, day=1}
排序前index[2]Date{year=2096, month=1, day=5}

排序后index[0]Date{year=1992, month=4, day=1}
排序后index[1]Date{year=2001, month=1, day=1}
排序后index[2]Date{year=2096, month=1, day=5}

(5)数组的应用

释例一:从算法角度考虑,解决从围成一个圈的一群人中,1,2,3循环数数,数到3就退出圈,求最后还留在圈里的人的位置。

/**
 * 说明:一群人,1,2,3轮着数数,数到三的自动离开
 * 求最后剩下的人原来在什么位置
 *
 * @author huayu
 * @date 2018/8/9 4:28 PM
 */
public class CountThreeQuit {
    public static void main(String[] args) {
        boolean[] arr = new boolean[500];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = true;
        }
        //定义一个变量用来统计离开后,原队伍还剩下的人数
        int leftCount = arr.length;
        //用来记录数的数
        int countNum = 0;
        //用来记录下标位置
        int index = 0;
        //只要队伍中剩下的人多于1个就继续数数,数到3离开
        while (leftCount > 1) {
            if (arr[index] == true) {
                countNum++;
                if (countNum == 3) {
                    countNum = 0;
                    arr[index] = false;
                    leftCount--;
                }
            }
            index++;
            //如果数组下标到了数组的长度就将index置为0,数组从头开始再循环
            if (index == arr.length) {
                index = 0;
            }
        }
        //将找到的位置输出出来
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] == true) {
                System.out.println(i);

            }
        }
    }
}

结果:
435

释例二:从面向对象角度考虑,解决从围成一个圈的一群人中,1,2,3循环数数,数到3就退出圈,求最后还留在圈里的人的位置。

package array;

/**
 * 说明:学生类
 *
 * @author huayu
 * @date 2018/8/9 5:28 PM
 */
public class Student {
    //学生的id
    int sid;
    //左边的学生
    Student left;
    //右边的学生
    Student right;
}

package array;

/**
 * 说明:500个学生围成一圈 学生圈类
 *
 * @author huayu
 * @date 2018/8/9 7:01 PM
 */
public class StudentCircle {
    int count = 0;
    //开头的那个小孩
    Student first;
    //结束的那个小孩
    Student last;

    /**
     * 构造方法
     * @param n 构造多少人的圈
     */
    StudentCircle(int n) {
        //往圈里依次添加人
        for (int i = 0; i < n; i++) {
            add();
        }
    }

    /**
     * 添加方法:往圈里添加一个学生
     */
    public void add() {
        Student student = new Student();
        //刚开始的id设置为0,为了跟数组下标结合
        student.sid = count;
        if (count <= 0) {//圈里一个学生都没有的时候
            first = student;
            last = student;
            student.left = student;
            student.right = student;
        } else {//当圈里有学生的时候,从后面开始添加
            last.right = student;
            student.left = last;
            student.right = first;
            first.left = student;
            last = student;
        }
        //添加到圈里后人数加1
        count++;
    }

    /**
     * 删除方法:圈添加完后,按照123数数,数到3的退出
     * @param student
     */
    public void delete(Student student) {
        if (count <= 0) {
            return;
        } else if (count == 1) {
            first = last = null;
        } else {
            /*想这儿的时候你用形象思维去考虑以下,想由学生围成圈是什么样的,该怎么离开圈
              由一个学生为中心,分这个学生在什么位置的情况作退出圈的行为*/
            //第一种情况,当要被删除的这个小孩不再头、尾的时候
            student.left.right = student.right;
            student.right.left = student.left;
            //第二种情况,当要被删除的学生在第一个位置的时候
            if (student == first) {
                first = student.right;
            } else if (student == last) {
            //第三种情况,当这个要被删除的学生在最后一个位置的时候
                last = student.left;
            }
        }
        //退出圈后,人数减1
        count--;
    }
}

package array;

/**
 * 说明:测试类
 *
 * @author huayu
 * @date 2018/8/9 7:20 PM
 */
public class CountThreeQuit1 {
    public static void main(String[] args) {
        StudentCircle sc=new StudentCircle(500);
        int countNum=0;
        Student student=sc.first;
        while (sc.count>1){
            countNum++;
            if(countNum==3){
              countNum=0;
              sc.delete(student);
            }
            student=student.right;
        }
        System.out.println(sc.first.sid);
    }
}

结果:
435

解决这个问题的大致思路(如果是复杂的程序,分析过程要把面向对象设计原则考虑在内,这个问题先不去考虑那些):

对面向对象设计原则感兴趣的同学可以先看一下以下这一篇文章:

https://blog.csdn.net/Myuhua/article/details/81562759

释例三:

package array;

/**
 * 说明:二分查找
 * 谈到在数组中查找东西,脑海中先要想到先给数组排序
 *
 * @author huayu
 * @date 2018/8/9 5:43 PM
 */
public class BinarySerach {
    public static void main(String[] args) {
        int[] arr = {2,4,5,1,9,7,6,8,3};
        int num = 5;
        System.out.println("排序前:");
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i]);
        }
        System.out.println();
        int[] arrSorted = arraySort(arr);
        System.out.println("排序后:");
        for (int i = 0; i < arrSorted.length; i++) {
            System.out.print(arrSorted[i]);
        }
        System.out.println();
        String index = binarySearch(arr, num);
        System.out.println("被搜索数是:"+num+" 位置在:" + index);
    }

    /**
     * int数组排序方法
     *
     * @param a int数组
     * @return
     */
    public static int[] arraySort(int[] a) {
        for (int i = 0; i < a.length; i++) {
            for (int j = i + 1; j < a.length; j++) {
                if (a[i] > a[j]) {
                    int temp = a[i];
                    a[i] = a[j];
                    a[j] = temp;
                }
            }
        }
        return a;
    }

    /**
     * 二分查找
     *
     * @param a   搜索数来自的数组
     * @param num 要搜索的int值
     * @return
     */
    public static String binarySearch(int[] a, int num) {
        if (a.length == 0) {
            return "未找到,因为数组为空";
        }
        int startPosition = 0;
        int endPosition = a.length;
        int m = (startPosition + endPosition) / 2;
        int count = 0;
        while (startPosition <= endPosition) {
            count++;
            //如果正好m上的值就是被搜索的值直接返回下标m
            if (num == a[m]) {
                return "index[" + m +"]"+ " 执行了几次:" + count;
            }
            //如果被搜索的值大于m位置的数,则起始位置+1
            if (num > a[m]) {
                startPosition = m + 1;
            }
            //如果被搜索的值小于m位置的数,则结束位置+1
            if (num < a[m]) {
                endPosition = m - 1;
            }
            m = (startPosition + endPosition) / 2;
        }
        return "未找到";
    }

}


结果:
排序前:
245197683
排序后:
123456789
被搜索数是:5 位置在:index[4] 执行了几次:1

(二)二维数组

1)二维数组可以看成以数组为元素的数组:例如:

int a[] [] = {{1,2},{3,4,5},{6.7,8,9}};

java中多维数组的声明和初始化按高维到低维(从左到右)的顺序进行,例如:

  //合法声明
  int a[][]=new int[3][];
  a[0] = new int[2];
  a[1] = new int[4];
  a[2] = new int[3];
  //非法声明
  int a1[][]=new int[][3];

内存分布图:

2)二维数组初始化

1.静态初始化

//合法初始化
int intA[][]={{1,2},{2,3},{3,4,5}};
//非法初始化,前面的数java替你确定,它会自己给你做检查,不用写
int intB[3][2]={{1,2},{2,3},{4,5}}; 

2.动态初始化

//第一维是3个小格,第二维,每一个数组都是5个小格
int a[][]=new int[3][5];
int b[][]=new int[3][];
b[0]=new int[2];
b[1]=new int[3];
b[2]=new int[5];
package array;

/**
 * 说明:打印二维数组
 *
 * @author huayu
 * @date 2018/8/8 1:01 PM
 */
public class ArrayDemo {
    public static void main(String[] args) {
        int intA[][]={{1,2},{2,3},{3,4,5}};
        for (int i = 0; i < intA.length; i++) {
            for (int j = 0; j < intA[i].length; j++) {
                System.out.println("a["+i+"]["+j+"]="+intA[i][j]+"");
            }
        }
    }

}

(三)数组的拷贝

使用java.lang.System类静态方法

public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);

可以用于数组src从第srcPos项元素开始的length个元素拷贝到目标数组从destPos项开始的length个位置。

释例一:

/**
 * 说明:数组拷贝释例
 *
 * @author huayu
 * @date 2018/8/10 1:01 PM
 */
public class TestArrayCopy {
    public static void main(String[] args) {
        //拷贝一维int类型数组
        int[] a={1,2,3,4,5};
        int[] b=new int[a.length];
        System.arraycopy(a,0,b,0,a.length);
        for (int i = 0; i < b.length; i++) {
            System.out.println("b["+i+"]="+b[i]);
        }
        //拷贝一维类型String数组
        String[] str1={"yu","hua"};
        String[] str2=new String[str1.length];
        System.arraycopy(str1,0,str2,0,str1.length);
        for (int i = 0; i < str2.length; i++) {
            System.out.println("str2["+i+"]="+str2[i]);
        }
        //拷贝二维int类型数组
        int[][] ints={{1},{2,3,4},{5}};
        int[][] ints1=new int[ints.length][];
        System.arraycopy(ints,0,ints1,0,ints.length);
        for (int i = 0; i < ints1.length; i++) {
            for (int j = 0; j < ints1[i].length; j++) {
                System.out.println("ints1["+i+"]["+j+"]="+ints1[i][j]+" ");
            }
        }
        //拷贝二维String类型数组
        String[][] strings={{"yu"},{"hua"}};
        String[][] strings1=new String[str2.length][];
        System.arraycopy(strings,0,strings1,0,strings.length);
        for (int i = 0; i < strings1.length; i++) {
            for (int j = 0; j < strings1[i].length; j++) {
                System.out.println("strings1["+i+"]["+j+"]="+strings1[i][j]+" ");
            }
        }
    }
}


结果:
b[0]=1
b[1]=2
b[2]=3
b[3]=4
b[4]=5
str2[0]=yu
str2[1]=hua
ints1[0][0]=1 
ints1[1][0]=2 
ints1[1][1]=3 
ints1[1][2]=4 
ints1[2][0]=5 
strings1[0][0]=yu 
strings1[1][0]=hua 

释例二:对以下释例进行内存分析

/**
 * 说明:实验数组赋值是操作的内存中同一块位置
 *
 * @author huayu
 * @date 2018/8/10 1:01 PM
 */
public class TestArrayCopy {
    public static void main(String[] args) {
        //拷贝二维int类型数组
        int[][] ints={{1},{2,3,4},{5}};
        for (int i = 0; i < ints.length; i++) {
            for (int j = 0; j < ints[i].length; j++) {
                System.out.println("未修改ints1的值时ints["+i+"]["+j+"]="+ints[i][j]+" ");
            }
        }
        int[][] ints1=new int[ints.length][];
        System.arraycopy(ints,0,ints1,0,ints.length);
        ints1[1][2]=5;
        for (int i = 0; i < ints1.length; i++) {
            for (int j = 0; j < ints1[i].length; j++) {
                System.out.println("ints1["+i+"]["+j+"]="+ints1[i][j]+" ");
            }
        }
        for (int i = 0; i < ints.length; i++) {
            for (int j = 0; j < ints[i].length; j++) {
                System.out.println("修改ints1的值后ints["+i+"]["+j+"]="+ints[i][j]+" ");
            }
        }
    }
}

结果:
未修改ints1的值时ints[0][0]=1 
未修改ints1的值时ints[1][0]=2 
未修改ints1的值时ints[1][1]=3 
未修改ints1的值时ints[1][2]=4 
未修改ints1的值时ints[2][0]=5 
ints1[0][0]=1 
ints1[1][0]=2 
ints1[1][1]=3 
ints1[1][2]=5 
ints1[2][0]=5 
修改ints1的值后ints[0][0]=1 
修改ints1的值后ints[1][0]=2 
修改ints1的值后ints[1][1]=3 
修改ints1的值后ints[1][2]=5 
修改ints1的值后ints[2][0]=5 

通过输出结果我们清楚的看出在对数组ints复制成ints1后又修改了ints1[1][2]=5;的值,
对原来的ints数组作输出发现原来被复制数组ints的值也发生了变化,
说明复制数组ints1操作的是跟ints数组一样都是同一块内存。

内存分析图:

注意:如果源数据目标超过目标数组边界会抛出IndexOutOfBoundsException异常。

若有问题欢迎大家与我互动交流,可评论,可留言,以后每周我会坚持至少更新一篇博客文章,喜欢的朋友可以加一下关注。

猜你喜欢

转载自blog.csdn.net/Myuhua/article/details/81506378