Java中的数组学习

63 阅读6分钟

数组

一、数组这种数据结构的优点和缺点是什么 ?

优点:

  1. 每一个元素的内存她址在空间存储上是连续的。
  2. 每一个元素类型相同,所以占用空间大小一样。
  3. 知道第一个元素内存地址,知道每一个元素占用空间的大小,又知道下标,所以通过一个数学表达式就可以计算出某个下标上元素的内存地址。直接通过内存地址定位元素,所以数组的检索效率是最高的。

补充:

数组中存储100个元素,或者存储109 万个元素,在元素查询/检索方面,效率是相同的因为数组中元素查找的时候不会一个一个找,是通过数学表达式计算出来的。(算出一个内存地址,直接定位的)

缺点:

1.由于为了保证数组中每个元素的内存地址连续,所以在数组上随机删除或者增加元素的时候效率较低,因为随机增删元素会涉及到后面元素统一向前或者向后位移的操作。

2.数组不能存储大数据量,因为很难在内存空闻上龙到一块特别大的连续的内存空间。

注意:

对于数组中最后一个元素增删,是没有效率影响的。

二、怎么声明一个数组?

语法格式:

  1. int [ ] array1;
  2. double [ ] array2;
  3. boolean [ ] array3;
  4. String [ ] array4;
  5. Object [ ] array5;

三、怎么初始化一个数组?

1.静态初始化语法格式:

int [ ] array = {100, 200, 300};

2.动态初始化语法格式:

int [ ] array = new int [5]; 这里的5表示数组的元素个数。

初始化一个5个长度的int类型数组,每个元素默认值为0。

String [ ] name = new String [6]; 初始化6个长度的string类型数组,每个元素默认值为null。

四、数组的读取、存储与修改


public class Text {
    public static void main(String[] args) {
        //声明一个int类型的数组,使用静态初始化的方式
        int [] a = {100, 200, 300, 400, 500, 600};
        //所有的数组对象都有Length属性
        System.out.println("数组中元素的个数" + a.length);
        //数组中每一个元素都有下标
        //通过下标数组中的元素进行存和取
        //取(读)
        
        System.out.println("第一个元素 = " + a[0]);
        System.out.println("最后一个元素 = " + a[5]);
        System.out.println("最后一个元素 =" + a[a.length - 1]);
        // 存(改)
        // 把第一个元素修改(赋值)为111
        a[0] = 111;
        // 把最后一个元素修改为0
        a[a.length - 1] = 0;
        
        System.out.println("第一个元素" + a[0]);
        System.out.println("最后一个元素" + a[a.length - 1]);
     }

五、数组的遍历

public class Text {
    public static void main(String[] args) {
        int [] a = {100, 200, 300, 400, 500, 600};
        
        //从前往后遍历
        for (int i = 0; i < a.length; i++){
            System.out.println(a[i]);
        }
        
        //从最后一个元素遍历到第一个元素
         for (int i = a.length -1; i >= 0; i--){
            System.out.println("颠倒顺序输出--->" + a[i]);
        }
       
     }
}

六、什么时候采用静态初始化的方式, 什么时候采用动态初始的方式?

当你创建数组的时候,定数组中存储哪些具体的元素时,采用静态初始化方式。

当你创建数组的时候,不确定将来数组中存储哪些数据,你可以使用动态初始化的方式,预先分配内存空间。

七、当方法参数为数组时如何传递参数?

public class Text {
    public static void main(String[] args) {
        //创建一个int类型的数组
        int[] x = {1, 2, 3, 4, 5, 6};
        printArray(x);
        
        printArray(new int[] {10, 11, 12});    //静态方法直接传入到方法中
        
        //创建一个字符串类型的数组
        String[] str = {"abc", "qwe", "zxc"};
        printArray(str);
        
        //动态初始化一个字符串类型的数组
        String[] str1 = new String[3];
        printArray(str1);        //3个null
        
        //动态初始化一个int类型的数组
        printArray(new int[6]);    //动态方法直接传入到方法中
     }
    
    public static void printArray(int[] array){         //这里的形参是一个int类型数组
        for (int i = 0; i < array.length; i++){
            System.out.println(array[i]);
        }
    }
    
    public static void printArray(String[] array){      //这里的形参是一个String类型的数组
        for (int i = 0; i < array.length; i++){
            System.out.println(array[i]);
        }
    }
}

八、main方法的String数组

在idea运行配置中可以修改程序实参。

public static void main(String[] args) {
    
}
//这里是一个名为mian的静态方法。方法内需要传入一个String类型的数组。我们可以通过修改运行配置中的实参来实现。
    public static void main(String[] args) {
        if(args.length != 2){
            System.out.println("对不起请输入符合规定的长度");
        }
        String userName = args[0];
        String passKey = args[1];
        
        //把"tom"放在前面可以避免空指针异常
        if("tom".equals(userName) && "123456".equals(passKey)){
            System.out.println("欢迎您进入系统");
        } else {
            System.out.println("对不起,用户名或密码错误");
        }
    }

九、数组中存放引用数据类型

对于数组来说实际上只能存储Java对象的"内存地址"。数组中存储的每个元素是“引用”。

1.静态创建引用数据存放

public class Text {
    public static void main(String[] args) {
        
        //创建对象
        Animal a1 = new Animal();
        Animal a2 = new Animal();
        
        //创建一个名为array的Animal类型数组,里面存放了两个引用
        Animal[] array = {a1, a2};
        for (int i = 0; i < array.length; i++){
            //通过for循环来调用Animal类中的move()方法
            array[i].move();
        }
    }
}
class Animal{
    public static void move(){
        System.out.println("Animal move....");
    }
}

2.动态创建引用数据存放

public class Text {
    public static void main(String[] args) {
        
        //动态初始化一个长度为2的Animal类型数组
        Animal[] array = new Animal[2];
        //创建一个Animal对象,把它放到数组的第一个盒子中。
        array[0] = new Animal();
        
        //Animal数组中只能存放Animal类型,不能存放与Animal无关的类型。
        //例:array[0] = new product();   这种写法是错误的
        
        //Animal数组中可以存放Cat类型数据,因为Cat是一个Animal
        //Cat是Animal的子类。
        array[1] = new cat();
        for (int i = 0; i < array.length; i++){
            Animal a = array[i];
            a.move();
        }
    }
}
class Animal{
    public void move(){
        System.out.println("Animal move....");
    }
}
class product{
    
}
class cat extends Animal{
    public void move(){
        System.out.println("cat move.......");
    }
}

3.引用数据在遍历中的多种情况

调用的方法是父类中存在的方法

public class Text {
    public static void main(String[] args) {
       Cat c = new Cat();
       Bird b = new Bird();
       Animal[] anis = {c, b};   //该数组中存储了两个对象的内存地址。
        for (int i = 0; i < anis.length ; i++) {
            Animal an = anis[i];// 这个取出来的可能Cat(引用c) ,也可能是Bird(引用b),
                                   //Animal an = new Cat(); 或 Animal an = new Bird();                                        //不过肯定一个Animal类型.
            
            an.move();         //如果调用的方法是父类中存在的方法不需要向下转型。
                              //直接使用父类型引用调用即可。
                             //如果想要调用子类中独有的方法则不行
        }
    }
}
class Animal{
    public void move(){
        System.out.println("Animal move....");
    }
}
class Cat extends Animal{
    public void move(){
        System.out.println("猫在走猫步");
    }
}
class Bird extends Animal{
    public void move(){
        System.out.println("鸟儿在飞翔");
    }
}

调用的方法是父类中不存在的方法(子类中独有的方法)

public class Text {
    public static void main(String[] args) {
       Cat c = new Cat();
       Bird b = new Bird();
       Animal[] anis = {c, b};   //该数组中存储了两个对象的内存地址。
        for (int i = 0; i < anis.length ; i++) {
            
            //调用子类中特有的方法的话需要向下转型!!!
            if(anis[i] instanceof Cat){
                Cat cat = (Cat)anis[i];
                cat.catchMouse();
            } else if (anis[i] instanceof Bird) {
                Bird bird = (Bird)anis[i];
                bird.sing();
            }
        }
    }
}
class Animal{
    public void move(){
        System.out.println("Animal move....");
    }
}
class Cat extends Animal{
    public void move(){
        System.out.println("猫在走猫步");
    }
    //独有方法
    public void catchMouse(){
        System.out.println("猫在抓老鼠");
    }
}
class Bird extends Animal{
    public void move(){
        System.out.println("鸟儿在飞翔");
    }
    //独有方法
    public void sing(){
        System.out.println("鸟儿在唱歌");
    }
}

十、数组的扩容

在java开发中,数组长度一旦确定不可变,那么数组满了怎么办 ?

数组满了,需要扩容。

java中对数组的扩容是:

先新建一个大容量的数组,然后小容量数组中的数据一个一个拷贝到大数组当中。

结论: 数组扩容效率较低。因为涉及到拷贝的问题。所以在以后的开发中请注意:尽可能少的进行数组的拷贝。可以在创建数组对象的时候预估计一下多长合适,最好预估准确,这样可以减少数组的扩容次数。提高效率。

数组的扩容(拷贝)代码

public class Text {
    public static void main(String[] args) {

        //拷贝源(从这个数组中拷贝)
        int[] src = {1, 22, 44, 55};

        //拷贝目标(拷贝到这个目标数组上)
        int[] arrge = new int[10];    //动态初始化一个长度为10的数组,每一个的默认元素为0.

        //调用JDK System类中的arraycopy方法,来完成数组的拷贝
        System.arraycopy(src, 1, arrge, 4, 2);  //src表示拷贝源数组,
                                               //1表示从src数组下标为1开始拷贝。
                                              //arrge表示拷贝目标,
                                             //4表示从arrge数组下标为4开始完成拷贝。
                                            //2表示拷贝过来的元素个数一共有两个。
        //System.arraycope(src, 0, arrge, 0, src.length);
        for (int i = 0; i < arrge.length; i++){
            System.out.println(arrge[i]);                          
        }
    }
}

十一、二维数组

1、二维数组其实是一个特殊的一维数组,特殊在这个一维数组当中的每一个元素是一个一维数组。

2、三维数组是什么 ?

三维数组是一个特殊的二维数组,特殊在这个二维数组中每一个元素是一个一维数组。实际的开发中使用最多的就是一维数组。二维数组也很少使用。三维数组几平不用

3、二维数组静态初始化

int[][] array = {{1, 2, 3}, {1, 1, 1}, {4, 5, 6}};

4、读取二维数组的长度

array[0].length;  //获取二维数组中第一个数组的长度

5、二维数组中的读和改

public class Text {
    public static void main(String[] args) {

        //初始化一个二维数组
        int[][] a = {
                {1, 1, 1},
                {2, 2, 2},
                {3, 3, 3},
                {4, 4, 4}
        };

        //a[二维数组中一维数组的下标][一维数组的下标];
        System.out.println(a[3][2]);
    }
}

public class Text {
    public static void main(String[] args) {

        //初始化一个二维数组
        int[][] a = {
                {1, 1, 1},
                {2, 2, 2},
                {3, 3, 3},
                {4, 4, 4}
        };

        //a[二维数组中一维数组的下标][一维数组的下标];
        System.out.println(a[3][2]);
        a[3][2] = 200;       //将[3][2]位置的元素改为200。
        System.out.println(a[3][2]);
    }
}

6、二维数组的遍历

public class Text {
    public static void main(String[] args) {

        //初始化一个二维数组
        int[][] a = {
                {1, 1, 1},
                {2, 2, 2},
                {3, 3, 3},
                {4, 4, 4}
        };
        for (int i = 0; i < a.length; i++){     //外层循环三次(负责纵向)。
            
            //负责遍历一遍一维数组
            for (int j = 0; j < a[i].length; j++){
                System.out.print(a[i][j] + "");
            }
            //输出换行符
            System.out.println();
        }
    }
}

7、动态初始化一维数组

int[][] array = new int[3][4]; //三行四列,3个一维数组,每个一维数组中有4个元素。

8、方法的参数是一个二维数组

public class Text {
    public static void main(String[] args) {

        //初始化一个二维数组
        int[][] a = {          //也可以这么写
                {12, 15, 13}, //printArray(new int[][]{{1, 1, 1}, {2, 3, 4}, {3, 6, 8}});
                {25, 278, 26},  
                {3345, 389, 34},
                {434, 495, 467}
        };

        //传入参数
        printArray(a);
    }
    public static void printArray(int[][] array){
        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.这个栈可以存储Java中的任何引用类型的数据。

2.在栈中提供push方法模拟压栈。(栈满了,要有提示信息。)

3.在栈中提供pop方法模拟弹栈。 (栈空了,也有有提示信息。)

4.编写测试程序,new栈对象,调用push pop方法来模拟压栈弹栈的动作。

题解:

MyStack类

public class MyStack {

    //创建一个私有的Object类型数组。
    private Object[] elements;

    //设置一个栈帧,用来对数组个数进行判断。
    //此时index = 0;代表数组内一个元素都没有
    int index = 0;

    //编写无参构造,初始化数组的长度(栈容量)为10。
    public MyStack(){
        this.elements = new Object[10];
    };

    //编写有参构造,可以让用户初始化数组的长度。
    public MyStack(Object[] elements){
        this.elements = elements; 
    }

    //对Object类型数组设置set,get方法(只要私有化了,就要写上set,get方法)
    public Object[] getElements() {
        return elements;
    }

    public void setElements(Object[] elements) {
        this.elements = elements;
    }


    //编写push方法实现压栈动作
    public void push(Object pushNum){   /*这里的形式参数为Object类型是
                                       为了让引用数组类型可以传入其中*/
        
        if (this.index == elements.length){  /*我们设置了栈帧为0所以说此时的栈帧指向了数组长度只要                                                栈帧增加到与数组长度相等时就无法压栈*/
            
            System.out.println("对不起元素已满,压栈失败");
            return;
        }
        this.index++;   /*如果通过了上述if语句代表数组还没满,可以继续压栈。
                         每进行一次压栈,栈帧就会自加1(也就是代表数组中的元素加1)。*/
                             
        this.elements[index - 1] = pushNum; /*将新的引用存放入数组中。栈帧减1就代表了数组的下标,                                               随着栈帧的变化,存放"引用"也是被顺序存放*/
        System.out.println(elements[index - 1] + "压栈成功,栈帧指向" + index);
    }

    //弹栈的方法,从数组中往外取元素。每取出一个元素栈帧向下移一位。弹出后需要返回一个类型方便以后我们使用
    public Object pop(){
        if (this.index == 0){     /*通过if语句来判读栈帧的指向。如果栈为0
                                  则代表数组内没有元素了,提醒用户并返回空值*/
            
            System.out.println("对不起栈已空,弹栈失败");
            return null;
        }
        this.index--;  //如果没有运行上述if语句则代表数组内还有元素,可以继续弹栈
        System.out.println(elements[index] + "弹栈成功,栈帧指向" + index);
        return elements[this.index] = null; //用null覆盖住原先的元素便于在使用时区分
    }
}

Text类

package 数组.静态数组的创建;
public class Text {
    public static void main(String[] args) {

        //初始化一个长度为3的动态数组
        MyStack stack = new MyStack(new Object[3]);

        //创建几个对象测试用
        Tack tack = new Tack();
        Tack tack1 = new Tack();
        Tack tack2 = new Tack();

        //进行压栈测试,调用push方法把三个引用放入数组中
        stack.push(tack);
        stack.push(tack1);
        stack.push(tack2);

        //调用pop方法,来实现弹栈动作。
        stack.pop();
        stack.pop();
        stack.pop();  //最后压入的最先弹出来(这个才符合栈的数据结构。)
    }
}

Tack类

public class Tack {}

重要提示:System.out.println()方法执行时,如果想输出引用的话,会自动调用引用的toString方法。

第二题:

为某个酒店编写程序:酒店管理系统,模拟订房、退房、打印所有房间状态等功能。

1、该系统的用户是:酒店前台。

2、酒店使用一个二维数组来模拟。"Room[] [] rooms;"

3、酒店中的每一个房间应该是一个Java对象: Room。

4、每一个房间Room应该有: 房间编号、房间类型、房间是否空闲。

5、系统应该对外提供的功能:

可以预定房间:用户输入房间编号,订房。

可以退房:用户输入房间编号,退房。

可以查看所有房间的状态: 用户输入某个指令应该可以查看所有房间状态。

题解:

HotelMgSystem类

import java.util.Scanner;

public class HotelMgSystem {
    public static void main(String[] args) {
        Hotel hotel = new Hotel();
        //接受用户键盘输入
        Scanner input = new Scanner(System.in);

        //一个简单的欢迎界面
        System.out.println("欢迎使用酒店管理系统,请阅读以下使用说明");
        System.out.println("功能编号对应功能");
        System.out.println("1 ------> 查看房间列表");
        System.out.println("2 ------> 表示订房");
        System.out.println("3 ------> 表示退房");
        System.out.println("0 ------> 退出系统");
        while (true){
            System.out.println("请输入功能编号:");
            int i = input.nextInt();
            if(i == 1){
                //查看房间列表
                hotel.print();
            } else if (i == 2){
                //订房
                System.out.println("请输入您想要预定的房间号");
                int j = input.nextInt();
                hotel.order(j);
                System.out.println("预定成功");
            } else if (i == 3){
                //退房
                System.out.println("请输入您想要退房的房间号");
                int j = input.nextInt();
                hotel.exit(j);
                System.out.println("退房成功");
            } else if (i == 0){
                //退出程序
                System.out.println("程序已安全退出");
                return;
            } else {
                System.out.println("对不起您输入的指令有误,请重新输入");
            }
        }
    }
}

Hotel类

//模拟酒店对象,酒店中有二维数组,二维数组模拟大厦。
public class Hotel {
    //二维数组模拟大厦所有房间
    private Room[][] rooms;

    //通过构造方法来盖楼
    public Hotel(){
        //一共有三层,每一层有十个房间。
        this.rooms = new Room[3][10];

        for(int i = 0; i < rooms.length; i++){    //i是楼层,j是房间
            for (int j = 0; j < rooms[i].length; j++){
                if (i == 0){
                    //一层
                    rooms[i][j] = new Room(100*(i+1)+j, "单人间", true);
                } else if (i == 1){
                    //二层
                    rooms[i][j] = new Room(100*(i+1)+j, "双人间", true);
                } else if (i == 2){
                    //三层
                    rooms[i][j] = new Room(100*(i+1)+j, "电竞房", true);
                }
            }
        }
    }

    //在酒店对象上提供一个打印房间列表的方法
    public void print(){
        //打印所有房间状态就是遍历二维数组
        for (int i = 0; i < rooms.length; i++){
            //里面的for循环负责输出一层
            for (int j = 0; j < rooms[i].length; j++){
                System.out.print(rooms[i][j]);
            }
            //换行
            System.out.println("");
        }
    }

    //订房的方法。调用此方法时需要传一个房间编号过来。
    public void order(int roomNum){
        //订房最主要的是将对象的status修改为false。
        //Room对象的status修改为false。
        //可以假设一个房间号,然后通过演算获取房间对象
        Room room = rooms[roomNum / 100 - 1][roomNum % 100 -1];
        //修改为占用
        room.setStatus(false);
    }

    //退房
    public void exit(int roomNum){
        Room room = rooms[roomNum / 100 - 1][roomNum % 100 -1];
        //修改为空闲
        room.setStatus(true);
    }
}

Room类

package HotelMgSystem;

import HotelManagementSystem.RoomStatus;

public class Room {
    //房间编号
    private int id;

    //房间类型
    private String type;

    //房间状态
    private boolean status;


    public Room(){}

    public Room(int id, String type, boolean status) {
        this.id = id;
        this.type = type;
        this.status = status;
    }

    //set,get方法的创建
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public boolean isStatus() {   //is是boolean类型的get方法可以修改为get
        return status;
    }

    public void setStatus(boolean status) {
        this.status = status;
    }

    public String toString(){    //toString方法就是将Java对象转换成字符串形式。目的:简单,明了
        return "[" + this.id + " " + this.type + " " + (this.status ? "空闲":"占用") + "]";
    }

    //你认为两个房间的房间编号相同,就代表同一个房间,那么写代码比较房间编号就行。
    public boolean equals(Object obj){
        if (obj == null || !(obj instanceof Room)) return false;
        if (this == obj) return true;
        Room room = (Room)obj;
        //当前房间编号等于传过来的房间编号。认为是同一个房间。
        if (this.id == room.getId()) return true;
        return false;
    }
}