JavaSE进阶 数组

本文详细介绍了Java中的一维数组,包括其特点、内存结构、优缺点、声明与定义、初始化、遍历、方法参数、存储引用数据类型以及数组扩容。此外,还探讨了冒泡排序和选择排序算法,并介绍了如何进行线性查找和二分法查找。最后提到了Java的Arrays工具类的使用。
摘要由CSDN通过智能技术生成

一维数组

概述

  1. Java语言中的数组是一种引用数据类型,不属于基本数据类型,数组的父类是Object。
  2. 数组实际上是一个容器,可以同时容纳多个元素(数组是数据的集合)
  3. 元素的类型要统一
  4. 数组中的元素内存地址是连续的
  5. 数组中可以存储“基本数据类型”和“引用数据类型”(对象的引用,即内存地址,4个字节长度)的数据
  6. 因为数组是引用数据类型,所以数组对象是在堆内存当中(数组存储在堆内存当中)
  7. 数组一旦创建,在java中规定,长度不可变(数组长度不可变)
  8. 所有数组对象都有 length 属性(java自带的),用来获取数组中的元素个数
  9. 数组是一种数据结构
  10. 所有数组都是将首元素的内存地址作为自己的内存地址
  11. 数组中的每一个元素都有下标,下标从0开始,以1递增,最后一个元素的下标是:length-1

特点总结连续统一不变

内存结构图

优点

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

数組中存储100个元素,或者存储100万个元素,在元素査询/检索方面,效率是相同的,因为数組中元素査找的肘候不会一个一个找,是通过数学表达式计算出来的。

缺点

  1. 由于为了保证数组中每个元素的内存地址连续,所以在数组上随机删除或者増加元素的时候,效率较低,因为随机増删元素会涉及到后面元素统一向前或者向后位移的操作。注意:对于数组中最后一个元素的増删,是没有效率形响的。
  2. 数组不能存储大數据量,因为很难在内存空间上找到一块特别大的连续的内存空闾。

声明/定义

局部变量定义好后要显式地初始化

//Java风格
int[] array01;
//c++的方式,不建议
//c中这样回报错:storge size不知道
int array01[];

//eg.main方法中的参数
String[] args
//或者
String args[]

//显示初始化
int[] nums={1,2,3};
array01=nums;

//以下不是显式地初始化
//在init类中的静态方法doSome()中进行初始化
init.doSome(array01);

初始化

静态初始化

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

//假设如下初始化
//array02.length=0
//array02有地址
int[] array02={};

动态初始化

//每个元素默认值是0
int[] array01=new int[5];

//每个元素默认值是null
String[] array02=new String[5];

当你创建数组的时候,确定数组中存储哪些具体的元素时,采用静态初始化方式,不确定的时候,采用动态初始化,预先分配内存空间

遍历数组

int[] a={1,2,3,4,5};
//从第一个元素开始遍历
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 ArraysTest01 {
    public static void main(String[] args) {

        int[] a={1,2,3};
        printArray(a);

        System.out.println("-------------");
        printArray(new int[3]);

        System.out.println("-------------");
        printArray(new int[]{6,7,8});

    }
    public static void printArray(int[] array){
        for(int i=0;i<array.length;i++){
            System.out.println(array[i]);
        }
    }
}

结果:

解析mian方法参数

" String[ ] args "有什么作用?JVM调用main方法时,会自动传一个String数组过来,主要用来接收用户输入参数的。

 这个数组什么时候里面会有值呢?其实这个数组是留给用户的,用户可以在控制台上输入参数,这个参数自动会披转换为" String[ ] args "  。例如这样运行程序∶java ArrayTest05 abc def xyz,那么这个时候JVM会自动将abc def xyz通过空格的方式进行分离,分离完成之后,自动放到" String[ ] args "数组。(把abc def xyz转换成字符串数组:{ "abc", "def", "xyz" } )

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

        //这里测试得出两个结论
        //1.args数组长度默认值为0
        //2.args不为null,可能JVM内部已进行初始化
        System.out.println("args数组长度为"+args.length);

        String[] strs={ };
        printLength(strs);

        for(int i=0;i< args.length;i++){
            System.out.println(args[i]);
        }

    }
    public static void printLength(String[] array){
        System.out.println("strs数组长度为"+array.length);
    }
}

怎么在IDEA中传入abc、def、xyz等参数?   run→ edit configuration →在 Program arguments 上打上abc、edf、xyz,结果如下:

实际应用:模拟一个系统,假设这个系统要使用,必须输入用户名和密码

public class ArrayTest03 {

    public static void main(String[] args) {

        if(args.length!=2){
            System.out.println("要使用此系统,必须输入用户名和密码,如 zhangsan 123");
            return;
        }

        //程序执行到这里说明正确输入了信息
        //取出用户名
        String username=args[0];
        //取出密码
        String password=args[1];
        //表示用户名为admin,密码是123时登录成功,其他一律失败
        //username.equals("admin")&&password.equals("123")
        //以下写法能避免空指针异常
        if("admin".equals(username)&&"123".equals(password)){
            System.out.println("登录成功,欢迎["+username+"]回来");
            System.out.println("您可以继续使用该系统....");
        }else{
            System.out.println("验证失败,用户名不存在或者密码错误!");
        }
    }
}

存储引用数据类型

public class ArraysTest04 {

    public static void main(String[] args) {

        Bird a1=new Bird();
        Cat a2=new Cat();
        Plant a3=new Plant();
        //能放子类,放不了Plant类
        Animal[] ans= {a1,a2};
        //Animal中没有Scratch()方法
        //ans[0].Scratch();
        for(int i=0;i< ans.length;i++){
            if(ans[i] instanceof Cat){
                Cat cat=(Cat)ans[i];
                cat.scratch();
            }else if(ans[i] instanceof Bird){
                Bird bird=(Bird)ans[i];
                bird.sing();
            }
        }

    }
}
class Animal{
    public void move(){
        System.out.println("Animal is moving.");
    }
}

class Cat extends Animal{
    @Override
    public void move() {
        System.out.println("Cat is walking");
    }
    public void scratch(){
        System.out.println("疯狂乱抓");
    }
}
class Bird extends Animal{
    @Override
    public void move() {
        System.out.println("Bird is flying.");
    }
    public void sing(){
        System.out.println("Bird is singing songs.");
    }
}

class Plant{
    public void absorb(){
        System.out.println("Plant is obsorbing CO2");
    }
}

数组扩容

在java开发中,数组长度一旦确定不可变,那么数组满了时需要扩容。 java中对数组的扩容是∶先新建—个大容量的数组,然后将小容量数组中的数据一个一个拷贝到大数组当中。

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

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

        //数组拷贝
        //拷贝源
        int[] src={1,11,22,3,4};
        //拷贝目标
        int[] dest=new int[20];//动态初始化,每个元素默认值为0

        //调用JVM System类中的arraycopy方法,来完成数组的拷贝
        System.arraycopy(src,1,dest,3,2);

        for(int i=0;i< dest.length;i++){
            System.out.println(dest[i]);  //0 0 0 11 22 ... 0
        }
        //完全拷贝
        System.arraycopy(src,0,dest,0,src.length);

        Object[] obj={new Object(),new Object(),new Object()};
        Object[] newObj=new Object[5];
        //这里拷贝的是对象的地址,而不是对象
        System.arraycopy(obj,0,newObj,0,obj.length);

        for(int i=0;i< newObj.length;i++){
            System.out.println(newObj[i]);
            //java.lang.Object@16b98e56 java.lang.Object@7ef20235 java.lang.Object@27d6c5e0...null
        }
    }
}

二维数组

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

遍历

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

        //静态初始化
        int[][] a={
                {1,2,3},
                {4,5,6,7},
                {8,9,10}
        };


        System.out.println(a[2][2]);
        System.out.println("--------------------");
        printArray(a);
        System.out.println("--------------------");
        printArray(new int[][] { {11,22},{33,44,55} });

    }
    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. 每一次循环结束之后,都要找出最大的数据,放到参与比较的这堆数据的最右边。(冒出最大的那个气泡)
  2. 左边和右边比大小,左边>右边时交换位置
/*
冒泡排序法:
原始数据:3 2 8 7 1  最外层循环4次 内层循环最大4次递减
第一次循环:
2 3 8 7 1(3比2,交换)
2 3 8 7 1(3比8,不交换)
2 3 7 8 1(8比7,交换)
2 3 7 1 8(8比1,交换)

第二次循环:2 3 7 1
2 3 7 1(2比3,不交换)
2 3 7 1(3比7,不交换)
2 3 1 7(7比1,交换)

第三次循环:2 3 1
2 3 1(2比3,不交换)
2 1 3(3比1,交换)

第四次循环:2 1
1 2(2比1,交换)

*/

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

        int[] a={3 ,2 ,8 ,7 ,1};
        //5个数据,循环4次
        //for(int i=0;i<a.length-1;i++){}

        //定义一个比较次数
        int count=0;
        //采用递减的方式与内层循环有联系
        for(int i=a.length-1;i>0;i--){
            for(int j=0;j<i;j++){
                count++;
                if(a[j]>a[j+1]){
                    int temp;
                    temp=a[j];
                    a[j]=a[j+1];
                    a[j+1]=temp;
                }
            }
        }
        //比较次数
        System.out.println("比较次数:"+count);
        //输出结果
        for(int i=0;i<a.length;i++){
            System.out.println(a[i]);
        }
    }
}

选择排序算法

每一次从这堆参与比较的数据当中找出最小值,拿着这个最小值和最前面的元素交换位置,选择排序比冒泡排序好在:每一次交换位置都是有意义的。关键点在于找出最小,先假设一开始最左边最小,假设

  1. 第一个3最小,3和2比,发现2更小,所以此时最小的是2
  2. 继续拿着2往下比对,2和6比较,2仍然是最小的
  3. 继续拿着2往下比对,2和1比较,发现1更小,所以此时最小的是1
  4. 继续拿着1往下比对,1和5比较,1仍然是最小

与冒泡排序算法相比:比较次数一样交换次数减少

/*

第一次循环:参与 3 1 6 2 5 (参与比较的最左边的元素下标为0)
1 3 6 2 5(排序后)
第二次循环:参与 3 6 2 5(参与比较的最左边的元素下标为1)
2 6 3 5(排序后)
第三次循环: 参与 3 6 5(参与比较的最左边的元素下标为2)
3 6 5(排序后)
第四次循环:参与 6 5(参与比较的最左边的元素下标为3)
5 6(排序后)

5条数据,循环4次
*/
public class SelectSort {
    public static void main(String[] args) {

        int[] a={3,1,6,2,5};
        int count=0;
        for(int i=0;i<a.length-1;i++){
            //这时输出i正好是0 1 2 3 对应着起点下标
            //假设起点i下标位置上的元素是最小的
            int min=i;
            for(int j=i+1;j<a.length;j++){
                count++;
                //输出j 对应i=0时 j为1 2 3 4
                if(a[j]<a[min]){
                    min=j;
                }
            }
            //当i和min相等,说明一开始的假设是对的
            //如果不相等,说明有更小的元素,需要交换位置
            if(min!=i){
                int temp;
                temp=a[min];
                a[min]=a[i];
                a[i]=temp;
            }

        }
        //比较次数
        System.out.println("比较次数:"+count);
        //排序之后遍历
        for(int k=0;k<a.length;k++){
            System.out.println(a[k]);
        }
    }
}

二分法查找

线性查找

//需求:找出89的下标,如果没有,返回-1
public class ArraySearch {
    public static void main(String[] args) {
        int[] a = {1, 6, 89, 55, 34};
        int index = arraySearch(a, 55);
        System.out.println(index == -1 ? "该元素不存在" : "该元素下标是" + index);
    }

    //参数1:传入的数组  参数2:要找的元素值
    //返回值返回下标,没有返回-1
    public static int arraySearch(int[] array,int x){
        for(int i=0;i<array.length;i++){
            if(x==array[i]){
                return i;
            }
        }
        return -1;
    }
}

二分法查找(基于排序的基础上

public class ArrayUtil {
    public static void main(String[] args) {
        int[] a = {1, 6, 11, 13, 17,19};
        int index = binarySearch(a, 11);
        System.out.println(index == -1 ? "该元素不存在" : "该元素下标是" + index);
    }

    //参数1:传入的数组  参数2:目标元素
    //返回值返回下标,没有返回-1
    public static int binarySearch(int[] array,int dest){

        //开始下标
        int begin=0;
        //结束下标
        int end=array.length-1;


        //开始元素的下标只要在结束元素下标的左边,就有机会循环
        while (begin<=end){
            //中间元素下标
            int mid=(begin+end)/2;

            if (array[mid] == dest) {
                return mid;
            } else if (array[mid] < dest) {
                //到这里说明目标在中间的右边
                begin = mid + 1;
            } else {
                //目标在中间的左边
                end = mid - 1;
            }
        }
        return -1;
    }
}

数组工具类

Arrays

java.util.Arrays 工具类中的方法大多都是静态的,以下两个都为静态方法,直接使用类名调用就行,使用Arrays.binarySearch()的前提是先使用Arrays.sort(数组名) ,把数组中的元素排好序(实践中发现如果不先排好序,返回的下标不准),其返回值示要查的值而定

import java.util.Arrays;

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

        int[] a={11,34,5,43,33,44,44};
        //排序
        Arrays.sort(a);
        //遍历
        for(int i=0;i<a.length;i++){
            System.out.print(a[i]+"   ");//5   11   33   34   43   44  44
        }

        System.out.println();

        //二分法查找
        int index01=Arrays.binarySearch(a,5);
        int index02=Arrays.binarySearch(a,4);  //返回-1
        int index03=Arrays.binarySearch(a,12); //搜索值在范围里但不存在,返回"-插入点索引值" 这里33为插入点
        //即5 11 12 33 34 43 44 44 此时33下标为3,3即为插入点索引值
        int index04=Arrays.binarySearch(a,45); //返回"– (length + 1)"

        System.out.println(index01+" "+index02+" "+index03+" "+index04);//0 -1 -3 -8

    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值