22.Java 数组概念/数组常见操作/多维数组/数组存储表格数据/javabean 和数组存储表格信息/Comparable接口
数组学习目标
数组概念
数组的定义
数组是相同类型数据的有序集合。
数组描述的是想同类型的若干个数据,按照一定的先后次序排列组合而成。
每一个数据称作一个元素,每个元素可以通过一个索引(下标)来访问。
数组简述
数组变量属于引用类型,数组也是对象,数组中每个元素相当于该对象的成员变量。
Java 中对象存储在堆中,所以数组对象也在堆中存储。
数组四个基本特点
- 长度是确定的。数组一旦被创建,其大小是不可改变的。
- 其元素的类型必须是相同类型,不允许出现混合类型。
- 数组类型可以是任何数据类型,包括基本类型和引用类型。
- 数组变量属于引用类型,数组也是对象。数组的元素相当于对象的属性。
数组声明
type[] arr_name; // 方式一(推荐)
type arr_name[]; // 方式二
注意点:
声明时不会实例化任何对象,只有在实例化数组对象时,JVM 才分配空间,此时才跟长度有关。
声明一个数组时,数组不会真正被创建。
构造一个数组,必须指定长度。
数组的初始化
(1)静态初始化
定义数组的同时就为数组元素分配空间并赋值。
(2)动态初始化
数组定义与数组元素分配空间并赋值的操作分开进行。
(3)默认初始化
数组是引用类型,它的元素相当于类的实例变量,因此数组分配空间时,其每个元素也被按照实例变量同样的方式被隐式初始化。比如 int类、String类等自带的类
代码示例:测试数组的三种初始化方式
package cn.jungle.test.ShuZu;
// 测试数组的三种初始化方式
public class Test2 {
public static void main(String[] args) {
// 数组的静态初始化
int[] a = {1,2,3}; // 静态初始化基本类型数组
Man[] mans = {new Man(1,1),new Man(2,2)}; // 静态初始化引用类型数组,new Man(1,1) 代表新创建的对象
// 数组的动态初始化
int[] a1 = new int[2]; // 动态初始化数组,先分配空间;
a1[0] = 1; // 给数组元素赋值
a1[1] = 2; // 给数组元素赋值
// 数组的默认初始化(针对 int类、String类这种自带的类)
int a2[] = new int[2]; // 默认值:0,0
boolean[] b = new boolean[2]; // 默认值:false,false
String[] s = new String[2]; // 默认值:nullm,null
}
}
代码示例:创建基本类型一维数组
package cn.jungle.test.ShuZu;
// 创建基本类型一维数组
public class Test {
public static void main(String[] args){
int[] s = null; // 声明数组
s = new int[10]; // 给数组分配空间
for(int i = 0;i < 10;i++){
// 给数组元素赋值,数组是对象,数组中的元素就是对象的属性
s[i] = 2 * i + 1;
System.out.println(s[i]); // 打印数组元素
}
}
}
代码示例:创建引用类型一维数组
// 测试引用类型数组
class Man {
private int id;
private int age;
public Man(int id,int age){
super(); // 可写可不写,默认会执行
this.age = age;
this.id = id;
}
}
public class AppMain{
public static void main(String[] args) {
Man[] mans; // 声明引用类型数组
mans = new Man[10]; // 给引用类型数组分配空间
Man m1 = new Man(1,11); // 创建id和age属性为1和11的新对象,将其赋值给左边的 m1 对象
Man m2 = new Man(2,22); // 创建id和age属性为2和22的新对象,将其赋值给左边的 m2 对象
mans[0] = m1; // 给引用类型数组的元素赋值为 m1 对象
mans[1] = m2; // 给引用类型数组的元素赋值为 m2 对象
}
}
引用类型数组内存分析图
代码示例:测试 “数组也是对象的概念”
package test.jungle.array;
// 测试数组也是对象的概念
public class Dog {
// 声明 name 属性
String name;
public static void main(String[] args){
// 创建一个 Dog 对象
Dog dog1 = new Dog();
// 对象调用方法
dog1.back(); // null,因为没有引用到任何对象的引用变量的值为null值
dog1.name = "阿jun修炼手册";
// 创建大小为 3 的 Dog 引用类型数组,并赋值给前面所声明的 Dog[] 类型变量 myDogs
Dog[] myDogs = new Dog[3];
// 上方代码只是对 Dog 的引用,缺少了实际的 Dog 对象,所以需要创建新的 Dog 对象并赋值给数组的元素
myDogs[0] = new Dog();
myDogs[1] = new Dog();
myDogs[2] = dog1;
// 通过数组引用存取 Dog:对象调用成员属性
myDogs[0].name = "阿jun";
myDogs[1].name = "阿junZzz";
// 打印 myDogs[2] 的 name 值
System.out.println(myDogs[2].name); // 阿jun修炼手册
// 逐个对 Dog 执行 bark() 方法
int x = 0;
while(x < myDogs.length){ // myDogs.length = 3,即是数组的大小
myDogs[x].back();
x++;
}
}
public void back(){
System.out.println(name);
}
}
数组常见操作
数组普通遍历
利用 for 循环对数组的元素进行提取打印
for-each 增强循环数组遍历
for-each 专门用于读取数组或集合中所有元素,即对数组进行遍历,是 JDK1.5 新增加的功能。
for-each 增强 for 循环在遍历数组过程中不能修改数组中某元素的值。
for-each 仅适用于遍历,不涉及有关索引(下标)的操作。
代码示例:数组普通遍历以及 for-each 增强循环数组遍历
package test.jungle.array;
// 测试数组的定义和初始化以及循环的使用
public class Test01 {
public static void main(String[] args) {
// 默认初始化
int[] arr1 = new int[5];
System.out.println(arr1[0]); // 0
System.out.println(arr1[1]); // 0
// 静态初始化
int arr2[] = {20,30,40};
System.out.println(arr2.toString()); // [I@1b6d3586
// 数组的 for 一般遍历
for (int i=0;i<arr1.length;i++){ // arr1.length 的值为 arr1 的数组长度
// 对数组元素进行赋值
arr1[i] = i * 2 + 1;
}
// 使用 for-each 增强循环遍历数组
String[] ss = {"aa","bb","cc","dd"};
for(String temp1:ss){
System.out.println(temp1);
}
}
}
数组拷贝
System 类中包含了一个 static void arraycopy (object src,int srcpos,object dest,int destpos int length) 方法,
该方法可以将 src 数组里的元素复制给 dest 数组的元素,
其中 srcpos 指定从 src 数组的第几行开始赋值,length 参数指定将 src 数组的多少个元素赋值给 dest 数组的元素。
格式:数组拷贝
System.arraycopy(目标数组,目标数组起始位置索引,拷贝数组,拷贝数组数据起始位置,拷贝数量);
代码示例:数组拷贝
package test.jungle.array;
// 测试数组拷贝
public class TestKB {
public static void main(String[] args) {
String[] s = {"淘宝","京东","腾讯","网易","阿jun"};
String[] sBak = new String[7];
System.arraycopy(s,0,sBak,0,s.length);
for(int i = 0;i < sBak.length;i++){
System.out.print(sBak[i] + "\t");
}
}
}
java.util.Arrays 类
这个类是 java 提供给我们操作数组的工具类。
Arrays 类包含了:排序、查找、填充、打印内容等常见操作。
代码示例:使用 Arrays 类输出数组中的元素:数组排序、填充、二分法查找
package test.jungle.array;
import java.util.Arrays;
// 测试 java.util.Arrays 类
public class TestArrays {
public static void main(String[] args) {
int[] a = {3,99,22,11};
// 打印数组引用的值
System.out.println(a); // [I@1b6d3586
// 打印数组元素的值
System.out.println(Arrays.toString(a)); // [3, 99, 22, 11]
Arrays.sort(a); // 排序数组
System.out.println(Arrays.toString(a)); // [3, 11, 22, 99]
// 测试二分法查找:测试二分法查找之前,必须先对数组进行排序,否则没法查
// 返回排序后元素新的索引位置,如果未找到,则返回负数,binarySearch(数组名,要查找的元素名)
System.out.println("要查找元素的索引是:" + Arrays.binarySearch(a,22)); // 2,查找 a 数组里面是否有 22 元素
System.out.println("要查找元素的索引是:" + Arrays.binarySearch(a,33)); // -4 查找 a 数组里面是否有 33 元素
// 使用 Arrays 类对数组进行填充
System.out.println(Arrays.toString(a)); // [3, 11, 22, 99]
// 将 2 到 4 的索引元素进行替换,包头不包尾
Arrays.fill(a,2,4,520);
System.out.println(Arrays.toString(a)); // [3, 11, 520, 520]
}
}
多维数组
概念
多维数组的元素是数组。
因为数组元素的数据类型可以是任意类型。
代码示例:二维数组的声明
package test.jungle.array;
import java.util.Arrays;
// 测试二维数组的声明
public class TestArrays01 {
public static void main(String[] args) {
// 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[0][4]; // 这个是非法的定义
System.out.println(Arrays.toString(a)); // [[I@1b6d3586, [I@4554617c, [I@74a14482],打印的是多维数组的数组元素对应内存地址
System.out.println(Arrays.toString(a1)); // 结果为 [] 能够对比发现,a1 不属于是多维数组
}
}
代码示例:二维数组的静态初始化、动态初始化以及获取数组长度
package test.jungle.array;
// 测试二维数组的静态初始化和动态初始化
public class TestArray02 {
public static void main(String[] args) {
// 测试二维数组的静态初始化
int[][] a = {{1,2,3},{3,4},{3,5,6,7}};
System.out.println(a[2][3]); // 7
// 测试二维数组的动态初始化
int[][] a1 = new int[3][];
a1[0] = new int[] {1,2};
a1[1] = new int[] {3,4};
a1[2] = new int[] {5,6};
System.out.println(a[2][1]); // 5
// 获取数组长度
// 获取二维数组第一堆数组的长度
System.out.println(a.length); // 3
// 获取第二维第二个数组长度
System.out.println(a[1].length); // 2
}
}
二维数组的静态初始化内存图如下:
数组存储表格数据
二维数组存储表格数据
每一行都可以使用一个一维数组来存储表格数据
// 静态初始化一个一维数组
Object[] a1 = {1001,"阿jun",18,"学员","2021-12-06"};
代码示例:测试二维数组 Object[][] 存储 “整个表格” 的数据
package test.jungle.array;
import java.util.Arrays;
/**
* 测试二维数组
* 使用 Object[][] 存储整个表格的数据
* */
public class TestArrays03 {
public static void main(String[] args) {
// 利用静态初始化定义几个一维数组
Object[] a1 = {"阿junZzz1",18,"不是很爱学习,爱偷懒","2021-12-06"};
Object[] a2 = {"阿junZzz2",20,"爱偷懒,不是很爱学习","2021-12-06"};
Object[] a3 = {"阿junZzz3",22,"你一定要努力,别负青春","2021-12-06"};
// 动态初始化多维数组
Object[][] emps = new Object[3][];
emps[0] = a1;
emps[1] = a2;
emps[2] = a3;
// 利用 for 循环打印二维数组的内容(类似于打印表格数据)
for (int i=0;i < emps.length;i++){
System.out.println(Arrays.toString(emps[i]));
}
System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
// 利用 for 循环嵌套打印二维数组的内容(类似于打印表格数据)
for (int i=0;i < emps.length;i++){
for (int j=0;j < emps[i].length;j++){
System.out.print(emps[i][j] + "\t");
}
System.out.println();
}
}
}
javabean 和数组存储表格信息(掌握)
使用 javabean 和数组存储表格信息
代码示例:使用 javabean 和数组存储表格数据、使用多种方式去调用数组值
package test.jungle.array;
// 使用 javabean 和数组存储表格数据
public class TestArrays04 {
public static void main(String[] args) {
// 定义对象来存储表格数据
Emp emp0 = new Emp(1001,"阿jun",18,"未来的逆向工程师","2021-12-07");
Emp emp1 = new Emp(1001,"阿junZzz",20,"未来的逆向工程师","2021-12-07");
Emp emp2 = new Emp(1001,"阿jun修炼手册",22,"未来的逆向工程师","2021-12-07");
// 静态初始化创建一维数组
// Emp[] emps = {emp0,emp1,emp2};
// 动态初始化创建一维数组
Emp[] emps = new Emp[3];
emps[0] = emp0;
emps[1] = emp1;
emps[2] = emp2;
// 循环遍历调用对应的属性值
for (int i=0;i < emps.length;i++){
// emps[i] 定位到具体的引用后,使用 get 方法去获取值
System.out.println(emps[i].getId() + "\t" + emps[i].getName() + "\t" + emps[i].getAge() + "\t" + emps[i].getJob() + "\t" + emps[i].getHiredate());
}
System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
// 通过在 Emp 类中重写 toString() 方法,获取数组值
for (int i=0;i < emps.length;i++){
System.out.println(emps[i]);
}
}
}
// 创建一个类用来表示表结构,再创建一个对象来表示这些数据
class Emp{
private int id;
private String name;
private int age;
private String job;
private String hiredate;
// 定义一个空构造器,建议写,当后期增加其他内容时,可提高代码可维护性
public Emp(){}
// 定义构造器:可使用 alt + insert 快捷键
public Emp(int id, String name, int age, String job, String hiredate) {
this.id = id;
this.name = name;
this.age = age;
this.job = job;
this.hiredate = hiredate;
}
// 通过方法重写覆盖,实现 mian 主类中数组对应属性值的调用打印
@Override
public String toString(){
//return id + "\t" + name + "\t" + age + "\t" + job + "\t" + hiredate;
//推荐使用 get 方法来获取属性值,即取值时尽量使用 get/set 方法,提高代码容错率
return getId() + "\t" + getName() + "\t" + getAge() + "\t" + getJob() + "\t" + getHiredate();
}
// get / set 方法
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public String getHiredate() {
return hiredate;
}
public void setHiredate(String hiredate) {
this.hiredate = hiredate;
}
}
作业留存
作业代码(大概实现了)
package test.jungle.array;
// 使用数组的形式打印一张商品信息表
public class TestArrays05 {
public static void main(String[] args) {
Goods goods1 = new Goods(1,"百战牌鼠标","BZ_001",99.21,0.9);
Goods goods2 = new Goods(2,"键盘侠玩偶","WO_102",403.00,0.7);
Goods goods3 = new Goods(3,"实战java程序设计","BK_001",89.00,0.8);
Goods goods4 = new Goods(4,"阿jun的新衣","GQ_XF_12",700.00,0.5);
Goods goods5 = new Goods(5,"大米牌手机","DM_PH_13",900.00,0.3);
// 静态初始化一个数组,用于存储这上方 5 个商品对象信息
Goods[] goods = {goods1,goods2,goods3,goods4,goods5};
// 将重写的 toString() 方法遍历数组,否则的话,只会打印对象内存地址,不会打印对应内容
// 打印商品信息
for (int i=0;i<goods.length;i++){
System.out.println(goods[i]);
}
System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
// 输出价格
for (int i=0;i<goods.length;i++){
System.out.println(goods[i].getName() + "的最终价格是:" +goods[i].getDiscount() * goods[i].getPrice() + "元");
}
}
}
class Goods{
private int id; // ID
private String name; // 名称
private String model; // 型号
private double price; // 价格
private double discount; // 折扣
// 预定义一个空构造方法,如果不定义,当有其他构造方法出现时,就不会被系统默认定义
public Goods(){};
// 定义一个形参构造方法
public Goods(int id, String name, String model, double price, double discount) {
this.id = id;
this.name = name;
this.model = model;
this.price = price;
this.discount = discount;
}
// 重写 toString() 方法,用于主类中 main 方法的调用
@Override
public String toString(){
return getId() + "\t" + getName() + "\t" + getModel() + "\t" + getPrice() + "\t" + getDiscount();
}
// 定义 set / get 方法
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public double getDiscount() {
return discount;
}
public void setDiscount(double discount) {
this.discount = discount;
}
}
Comparable接口(对不同对象排序)
简述
想对某个类的对象之间做比较,就需要实现 Comparable 接口。
接口中只有一个方法 compareTo ,这个方法定义了对象之间的比较规则。
根据这个 “比较规则”,可以对所有对象实现排序,比如不同对象之间按照年龄 age 的属性值排序。
Java 中排序算法的底层也依赖 Comparable 接口。
接口方法实现
Comparable 接口只有一个 compareTo 方法:
public int compareTo(Object obj) // 其中 obj 是要比较的对象
方法中,将当前对象和 obj 这个对象进行比较,如果大于返回 1,等于返回 0,小于返回 -1。(此处的 1 也可以是正整数,-1 也可以是负整数)。
compareTo 方法的代码也比较固定。
compareTo 方法代码案例:测试 Comparable 接口:使用 Arrays 类对数组元素进行排序:按照 age 大小排序
package test.jungle.array;
import java.util.Arrays;
// 测试 Comparable 接口:使用 Arrays 类对数组元素进行排序:按照 age 大小排序
public class CompareToTest {
public static void main(String[] args) {
Man[ ] m1 = {new Man(12,"阿jun"),new Man(22,"阿jun修炼手册"),new Man(18,"阿junZzz")};
Arrays.sort(m1);
System.out.println(Arrays.toString(m1));
}
}
class Man implements Comparable{
int age;
int id;
String name;
public Man(int age,String name){
super();
this.age = age;
this.name = name;
}
public String toString(){
return this.name;
}
// 定义 compareTo 方法
public int compareTo(Object o){
Man man = (Man) o;
if (this.age < man.age){
return -1;
}
if (this.age > man.age){
return 1;
}
// 等于时返回 0
return 0;
}
}