Java数组

Java数组

1.容器简述

现在需要统计某课程学生的成绩,例如第一名、最后一名、班级平均成绩。班级55个人,用变量的概念解决问题,首先需要声明55个变量,来分别保存每个学生的成绩,然后再进行操作,这样做会显得很麻烦,而且错误率颇高。因此,我们可以使用容器来进行操作,将所有数据全部存储到一个容器中,统一操作。

1.1容器
能将一些数据存储到一起,统一进行操作,容器中的每一个记录称之为元素。
1.1生活的容器
  • 教室
  • 公交车
  • 水杯

2.数组

2.1 数组概述

在Java中也提供了容器技术(数据结构),Java中最常见的容器称之为数组,数组在数据结构中是一种典型的线性结构;如下图
在这里插入图片描述

2.2数组的定义

数组是一种引用数据类型,在内存中的存储位置位于堆空间(heap);数组的定义方式分为两种:

  • 静态初始化
  • 动态初始化
2.2.1 静态初始化

声明数组时直接初始值,
语法:

数据类型[ ] 数组名 = { 初始值1,初始值2…}

实例

int[] a = {1,2,3,4,5,6,7,8};
2.2.2 动态初始化

动态初始化,即在声明数组时,只申请存储空间的容量,而不赋予每个空间的具体元素

语法如下:

数据类型[] 数组名 = new 数据类型[长度]

int[] x = new int[5]

3. 数组访问

由于数组是一个连续的结构,因此数组中的每一个元素都有一个索引(下标),**数组的索引从0开始**,另外任何数组都有一个用于获取该数组容量的属性`length`;因此数组的索引范围是`0~(length-1)`
3.1根据索引访问数组(访问部分元素)
int[] a = {1,2,3,4,5,6};
//通过数组的索引访问数组中的元素
System.out.println(a[3]);
//获取数组中元素的个数
System.out.println(a.length);
//访问不在索引范围内的元素将会导致数组索引越界异常
System.out.println(a[6]);
3.2对数组进行遍历(获取数组中的所有元素)
int[] a = {1,2,3,4,5,6,7,9,11,3,5,4,1,8};

//for循环遍历
for(int i = 0;i < a.length;i++){
    int k = a[i];
    System.out.print(k+" ");
}
System.out.println();

//while循环?
int i = 0;
while(i < a.length){
    System.out.print(a[i]+" ");
    //索引递增
    i++;
}

以上为普通循环对数组的遍历,以上遍历方式都需要使用到数组的索引。

3.4forEach循环(JDK1.5新增)

forEach循环内部是基于**迭代器(Iterator)**的实现,将一个数组转化为一个栈结构;语法如下

for(数据类型 变量:数组){
    //循环体
}

案例:

//forEach
for(int n:a){
    System.out.print(n+" ");
}

String[] names = {"james","curry","wade","bosh","nash"};
//如何使用forEach遍历以上数组?
for (String name : names) {
    System.out.print(name+" ");
}

数组元素访问时注意事项

数组的索引从0开始,索引范围只能是0~length-1,一但超过索引范围将会导致一个java.lang.ArrayIndexOutOfBoundsException异常(数组索引越界异常)

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 6
	at com.softeem.j2106.array.ArrayDemo1.main(ArrayDemo1.java:23)

4.常见练习

4.1排序
  • 有n个学生,有不同的成绩,请获取这n个学生的平均分,最高分和最低分
double[] s = {86,88,76,54,40,66,99,75,89,83,68};
double total = 0;
double max = 0;
//初始最低分为其中一个元素
double min = s[0];
for (double i : s) {
    total += i;
    if(max < i){
        max = i;
    }
    if(min > i){
        min = i;
    }
}
System.out.println("平均分:"+ (total / s.length));
System.out.println("最高分:"+ max);
System.out.println("最低分:"+ min);
  • 以上问题,如果需要将学生的成绩按照分数从高到低输出,如何实现(排序)
//选择排序
int[] s = {86,88,76,54,40,66,99,75,89,83,68};
for (int i = 0; i < s.length; i++) {
    int temp = 0;
    for (int j = i + 1; j < s.length; j++) {
        if(s[i] < s[j]){
            temp = s[j];
            s[j] = s[i];
            s[i] = temp;
        }
    }
    for (int v : s) {
        System.out.print(v+" ");
    }
    System.out.println();
}
System.out.println();
System.out.println("最终结果:");
for (int v : s) {
    System.out.print(v+" ");
}

关于排序算法:

  • 冒泡
  • 选择
  • 插入
  • 归并
  • 希尔

java内部提供了Arrays类,是一个工具类,提供排序之类的算法,例如:

Arrays.sort(数组对象);

内部实现:

  • 归并
  • 快速
  • 接上题,如果需要从分数中找出83分学生的排名,如何最快获取(查找:折半查找)
4.2折半查找

折半查找也叫二分法,原理在一组已经排好序的数组中通过每次获取中间数比较的方法限定查找范围,从而以最快的方式实现元素的搜索

 //常规普通判定
    public static int Binary1(int[] nums, int target) {
        if (nums.length == 0) {
            return -1;
        }
        int left = 0;
        int right = nums.length - 1; // 注意

        while (left <= right) { // 注意
            int mid = (left + right) / 2;
            if (nums[mid] == target) {
                return mid;
            } else if (nums[mid] < target) {
                left = mid + 1;
            } else if (nums[mid] > target) {
                right = mid - 1; // 注意
            }
        }
        return -1;
    }
    //左值判定法
    public static int Binary2(int[] nums, int target) {
        if (nums.length == 0) {
            return -1;
        }
        int left = 0;
        int right = nums.length; // 注意

        while (left < right) { // 注意
            int mid = (left + right) / 2;
            if (nums[mid] == target) {
                return mid;
            } else if (nums[mid] < target) {
                left = mid + 1;
            } else if (nums[mid] > target) {
                right = mid; // 注意
            }
        }
        return -1;
    }
    //右值判定
    public static int Binary3(int[] nums, int target) {
        if (nums.length == 0) {
            return -1;
        }
        int left = 0;
        int right = nums.length; // 注意

        while (left < right) { // 注意
            int mid = (left + right) / 2;
            if (nums[mid] == target) {
                left = mid + 1;
            } else if (nums[mid] < target) {
                left = mid + 1;
            } else if (nums[mid] > target) {
                right = mid; // 注意
            }
        }
        if (nums[left-1] == target){
            return left - 1;
        }else
            return -1;
    }

5. 数组拷贝

5.1.概述

数组的长度一旦定义则无法改变,所以在实际开发中(Java)一般会使用动态数组,而非原始数组;因为在创建数组时无法严格控制元素的个数从而不容易确定数组的长度,因此需要一种能够长度动态改变的数组容器,Java中就出现动态数组的概念;

而动态数组的实现核心原理之一就是:数组拷贝。

5.2. System.arraycopy()

在JDK中提供了一个类System,该类提供了一个用于数组拷贝的方法arraycopy(src,srcPos,dist,distPos,length),参数一览:

  • src:需要拷贝的原数组对象
  • srcPos:原数组元素的起始拷贝位置
  • dist:拷贝到的目标数组对象
  • distPos:目标数组的起始拷贝位置
  • length:拷贝的元素个数

使用方式:

int[] a = {10,20,22,33,44};

//创建一个大小为原来数组1.5倍大小的新数组
int[] b = new int[a.length + (a.length >> 1)];

//数组拷贝
System.arraycopy(a,0,b,0,a.length);

for (int i : b) {
    System.out.println(i);
}
5.3. 动态数组

java中对于可变长数组的实现提供一个类java.util.ArrayList;这是出自JDK1.5之后集合框架的内容,是一个可变长度数组的实现(即动态数组),用法如下:

//面向对象
ArrayList list = new ArrayList();
list.add(10);
list.add(20);
list.add(30);
list.add(40);
list.add(50);

System.out.println(list.get(3)); //list[3]
System.out.println(list.size()); //list.length

for (Object o : list) {
    System.out.println(o);
}

6.数组的特殊性

数组之所以如此特殊离不开三大方面 “效率” “类型” "保存基本类型的能力"

6.1.效率

数组就是一个简单的线性序列,这使得元素在访问非常迅速.但是这种的快速付出的代价就是数组的大小固定不可被修改(在生命周期内).
这里有人可能会想到ArrayList(具体用法查JDKAPI),他可以创建一个动态数组(创建新的实例,把旧实例引用到新的实例中,从而实现更多的空间自动分配),但是这样就失去了原来的速度.尽管在很多时候我们首选的是ArrayList,而不是数组,但是ArrayList的开销比数组大的多.因此,ArrayList效率比数组低.

任何容器都可以保证编程人员不能滥用它,一旦越界报RuntimeException

6.2. 类型

在泛型之前,其他的容器处理对象时,将他们视为没有任何具体类型.意思是,他们将这些对象都当做Java中所有类的根类Object处理.数组之所以优于其他的容器,就是因为你可以去创建一个数组具有某种类型.这意味在后期编译检查时,来防止插入错误类型以及防止抽取不当类型.当然在编译或者运行时,java都会阻止你向对象发送不当的信息.所以没有 哪种方法更不安全之说,如果直接在编译时指出错误,会显得更加优雅.也减少了使用者的调试的过程.

6.3 保存基本类型的能力

数组能持有基本类型,而泛型之前的容器不能,但是有了泛型容器就能注定并检查他们所持有的对象的类型,并且有了自动包装机制,容器看起来还能够持有基本类型.

7.多维数组

我们之前所学习的为一维数组,即一个数组中的所有元素都是一个具体的值,或者一个对象;但是一维数组不能解决所有的容器存储的问题,比如需要表示一个电影院影厅的座位布局,一个游戏的地图等有关平面的业务需求,此时一维数组就无法满足需求,所以需要一种平面概念,即能否在数组中将内部的元素再定义为一个子数组:多维数组

通常对于多维数组的研究主要集中在:二维数组

多维数组即:数组中的数组

7.1定义方式

对于二维数组来说,与一维数组相同,也有两种定义方式:

  1. 静态初始化
  2. 动态初始化
7.1.1.静态初始化

即在声明数组时,直接为数组赋值元素,语法

数据类型[][] 数组名 = 初始值

案例:

 int[][] a = {{1,2,3},{4,5},{6,7,8,9}};
7.1.2.动态初始化

即在声明数组时,只为数组指定大小,而不给定具体的值

数据类型[][] 数组名 = new 数据类型[长度][];

案例

//声明5行5列二维数组
int[][] b = new int[5][5];
//声明5行,列数不定二维数组
int[][] c = new int[5][];

动态初始化的赋值:

int[][] a = new int[3][];
//为每一行指定列数
a[0] = new int[2];
a[1] = new int[3];
a[2] = new int[4];
//        a[3] = new int[5];
//指定第一行中的列值
a[0][0] = 1;
a[0][1] = 2;
//        a[0][2] = 3;
//指定第二行中的列值
a[1][0] = 3;
a[1][1] = 4;
a[1][2] = 5;
//指定第三行中的列值
a[2][0] = 6;
a[2][1] = 7;
a[2][2] = 8;
a[2][3] = 9;

System.out.println(a[0]);

7.2. 读取二维数组中的元素

跟一维数组相同:二维数组中的元素读取也是通过索引(索引从0开始);区别在于二维数组中的每一个元素是一个一维数组,元素读取方式:

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

System.out.println(a[1][1]);//5
System.out.println(a[2][2]);//8
System.out.println(a[0][0]);//1

7.3.遍历方式

二维数组由于是数组中的数组,因此,一般对二维数组的遍历需要使用嵌套循环来读取

//for循环遍历二维数组
for (int i = 0; i < a.length; i++) {
    //获取二维数组中的一个元素
    int[] t = a[i];
    for (int j = 0; j < t.length; j++) {
        System.out.print(t[j] + " ");
    }
    System.out.println();
}

System.out.println("----------------");

//forEach遍历
for(int[] t : a){
    for(int n : t){
        System.out.print(n+" ");
    }
    System.out.println();
}

思考题

基于数组完成一个学生管理系统,学生包含属性以下属性:

学号(int id)

姓名(String)

性别(String)

专业(String)

入学时间(int:年份)

毕业时间(int:年份)

要求包含以下功能:

  1. 学生添加(从键盘输入学生的信息)

  2. 学生删除(输入学号,从数组中删除学生信息)

  3. 根据学号查询学生信息

  4. 根据入学时间查询学生信息

  5. 根据毕业时间查询学生信息

  6. 查询所有的学生信息

8.Arrays类

Arrays类在java.util类库中可以找到,它有一套用于数组的static(静态方法),其中六个基本方法

  • equals() 用于比较两个数组是否相等(deepEquals()多用于数组)

  • fill() 能用于 用同一个值填充数组的哥哥位置,而针对对象而言,就是复制同一个引用进行填充.这个方法作用十分有限

  • sort() 用于对数组排序;

  • binarySearch()用于在已经排序好的数组查找元素(二分法)

  • toString(),产生数组的String表示

  • hashCode()产生数组的散列码

    此外.Arrays.asList()接受任意的序列或数组作为其参数,并将转变为List容器

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

西伯利亚大熊猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值