文章目录
七.数组和数据存储
学习目标:
1.数组定义
数组是相同类型数据的有序集合。其中,每一个数据称作一个元素,每个元素可以通过一个索引(下标)来访问它们。
数组的四个基本特点:
- 长度是确定的。数组一旦被创建,它的大小就是不可以改变的。
- 其元素的类型必须是相同类型,不允许出现混合类型。
- 数组类型可以是任何数据类型,包括基本类型和引用类型。
- 数组变量属于引用类型,数组也是对象,数组中的元素相当于对象的属性!
2.创建数组初始化
数组的声明方式(以一维数组为例)
type[ ] arr_name; //方式一
type arr_name[ ]; //方式二
注:
- 声明的时候并没有实例化任何对象,只有在实例化数组对象时,JVM 才分配空间,这时才与长度有关。
- 声明一个数组的时候并没有数组真正被创建。
- 构造一个数组,必须指定长度。
【示例】创建基本类型一维数组
public class test01 {
public static void main(String[] args) {
int[] s;//声明数组;
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 age;
private int id;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void setId(int id) {
this.id = id;
}
public Man(int id, int age) {
super();
this.age = age;
this.id = id;
}
}
public class test02 {
public static void main(String[ ] args) {
Man[ ] mans; //声明引用类型数组;
mans = new Man[10]; //给引用类型数组分配空间;
Man m1 = new Man(1,11);
Man m2 = new Man(2,22);
mans[0]=m1;//给引用类型数组元素赋值; 将地址赋给它
mans[1]=m2;//给引用类型数组元素赋值; 将地址赋给它
System.out.println(mans[0].getAge());
System.out.println(mans[1]);
System.out.println(m2);
}
}
运行结果:
3.初始化
数组的初始化方式总共有三种:静态初始化、动态初始化、默认初始化。
1.静态初始化
除了用 new 关键字来产生数组以外,还可以直接在定义数组的同时就为数组元素分配空间并赋值。
int [ ] a = { 1, 2, 3 };// 静态初始化基本类型数组;
Man[ ] mans = { new Man(1, 1), new Man(2, 2) };// 静态初始化引用类型数组;
2.动态初始化
数组定义与为数组元素分配空间并赋值的操作分开进行。
int[ ] a1 = new int[2];//动态初始化数组,先分配空间;
a1[0]=1;//给数组元素赋值;
a1[1]=2;//给数组元素赋值;
3.数组的默认初始化
数组是对象,它的元素相当于对象的属性;每个元素也按照属性的方式被默认初始化。
int a2[ ] = new int[2]; // 默认值:0,0
boolean[ ] b = new boolean[2]; // 默认值:false,false
String[ ] s = new String[2]; // 默认值:null, null
4.数组常见操作
遍历与拷贝
-
遍历指的就是“通过循环遍历数组的所有元素”。
-
拷贝指的是将某个数组的内容拷贝到另一个数组中
注:实质上,后面大家学容器的扩容就是“数组的拷贝”。
1.数组的遍历
数组元素下标的合法区间:[0, length-1]。我们可以通过下标来遍历数组中的元素,遍历时可以读取元素的值或者修改元素的值。
【示例】使用循环初始化和遍历数组
public class test03 {
public static void main(String[] args) {
int[] a=new int[4];
//初始化数组元素的值
for(int i=0;i<a.length;i++){
a[i]=100*i;
}
//读取元素的值
for(int i=0;i<a.length;i++){
System.out.println(a[i]);
}
}
}
执行结果:
for-each循环
for-each专门用于读取数组或容器中所有的元素。
【示例】使用增强 for 循环遍历数组
public class test03 {
public static void main(String[] args) {
int[] a=new int[4];
//初始化数组元素的值
for(int i=0;i<a.length;i++){
a[i]=100*i;
}
for(int t:a){
System.out.println(t);
}
}
}
执行结果同上
public class Test03 {
public static void main(String[ ] args) {
String[ ] ss = { "aa", "bbb", "ccc", "ddd" };
for (String temp : ss) {
System.out.println(temp);
}
}
}
执行结果:
注意:
- for-each增强for循环在遍历数组过程中,仅可以读取,不能修改数组中某元素的值;
- for-each仅适用于遍历,不涉及有关索引(下标)的操作。
2.数组的拷贝
System.arraycopy(object src,int srcpos,object dest,int destpos,int length)
该方法可以将src数组里的元素赋值给dest数组的元素,其中srcpos指定从src数组的第几个元素开始赋值,length参数指定将src数组的多少个元素赋给dest数组的元素。
【示例】数组的拷贝
public class test04 {
public static void main(String[] args) {
String[] s={"阿里","尚学堂","京东","搜狐","网易"};
String[] sBak=new String[6];
System.arraycopy(s,0,sBak,0,s.length);
for(int i=0;i<sBak.length;i++){
System.out.println(sBak[i]+"\t");
}
}
}
3.java.util.Arrays类
Arrays类包含了:排序、查找、填充、打印内容等常见的数组操作。
使用Arrays类的一些操作
import java.util.Arrays;
public class test05 {
public static void main(String[] args) {
//使用Arrays类输出数组中的元素
int[] a={1,2};
System.out.println(a); //打印数组引用的值;
System.out.println(Arrays.toString(a)); //打印数组元素的值;
// 注: 此处的Arrays.to.String()fan方法是Arrays类的静态方法,不是Object的toString()方法。
System.out.println(" ");
//使用Arrays类对数组元素进行排序
int[] b={1,2,3,7,6,5,4};
System.out.println(Arrays.toString(b));
Arrays.sort(b);
System.out.println(Arrays.toString(b));
System.out.println(" ");
//使用Arrays类实现二分法查找法
int[] c={1,2,3,7,6,5,4,3,2,1};
System.out.println(Arrays.toString(c));
Arrays.sort(c); //使用二分法查找,必须先对数组进行排序;
System.out.println(Arrays.toString(c));
//返回排序后新的索引位置,若未找到返回负数;
System.out.println("该元素的索引:"+Arrays.binarySearch(c,6));
System.out.println(" ");
//使用Arrays类对数组进行填充
int[] d={1,5,4,2,3};
System.out.println(Arrays.toString(d));
Arrays.fill(d,2,4,100); //将2到4索引的元素替换为100;(不包括4)
System.out.println(Arrays.toString(d));
}
}
输出结果:
4.多维数组
**多维数组可以看成以数组为元素的数组。**可以有二维、三维、甚至更多维数组,但是实际开发中用
的非常少。最多到二维数组(学习容器后,我们一般使用容器,二维数组用的都很少)。
【示例】二维数组的声明
import java.util.Arrays;
public class test06 {
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[][4];//非法
System.out.println(Arrays.toString(a[0]));
System.out.println(Arrays.toString(a[1]));
System.out.println(Arrays.toString(a[2]));
}
}
输出:
【示例】二维数组的静态初始化
public class test06 {
public static void main(String[ ] args) {
int[ ][ ] a = { { 1, 2, 3 }, { 3, 4 }, { 3, 5, 6, 7 } };
System.out.println(a[2][3]); // 7
}
}
【示例】二维数组的动态初始化
import java.util.Arrays;
public class test06 {
public static void main(String[ ] args) {
int[ ][ ] a = new int[3][ ];
// a[0] = {1,2,5}; //错误,没有声明类型就初始化
a[0] = new int[ ] { 1, 2, 3, 4 };
a[1] = new int[ ] { 2, 2 };
a[2] = new int[ ] { 2, 2, 3, 4 };
System.out.println(a[2][3]);
System.out.println(Arrays.toString(a[0]));
System.out.println(Arrays.toString(a[1]));
System.out.println(Arrays.toString(a[2]));
}
}
输出结果:
5.数组存储表格数据
雇员表
ID | 姓名 | 年龄 | 职能 | 入职日期 |
---|---|---|---|---|
1001 | K | 18 | 讲师 | 2-14 |
1002 | Z | 19 | 助教 | 4-21 |
1003 | Y | 20 | 班主任 | 5-21 |
观察表格,发现每一行可以使用一个一维数组存储:
Object[ ] a1 = {1001,“高淇”,18,“讲师”,“2-14”};
Object[ ] a2 = {1002,“高小七”,19,“助教”,“10-10”};
Object[ ] a3 = {1003,“高小琴”,20,“班主任”,“5-5”};
注:此处基本数据类型”1001”,本质不是 Object 对象。JAVA 编译器会自动把基本数据类型“自动装箱”成包装类对象。
我们只需要再定义一个二维数组,将上面 3 个数组放入即可:
Object[ ][ ] emps = new Object[3][ ];
emps[0] = a1;
emps[1] = a2;
emps[2] = a3;
1.使用二维数组保存表格数据
import java.util.Arrays;
public class test07 {
public static void main(String[] args) {
Object[] a1={1001,"K",18,"讲师","2-14"};
Object[] a2={1002,"Z",19,"助教","4-21"};
Object[] a3={1003,"Y",20,"主任","5-21"};
Object[][] emps=new Object[3][];
emps[0]=a1;
emps[1]=a2;
emps[2]=a3;
System.out.println(Arrays.toString(emps[0]));
System.out.println(Arrays.toString(emps[1]));
System.out.println(Arrays.toString(emps[2]));
System.out.println("===============");
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();
}
}
}
输出结果:
2.使用 javabean 和一维数组保存表格信息
/**
* 使用 javabean 和一维数组保存表格信息
*/
import java.util.Arrays;
public class test08{
public static void main(String[] args) {
Emp[] emps={
new Emp(1001,"K",18,"讲师","2-14"),
new Emp(1002,"Z",19,"助教","4-21"),
new Emp(1003,"Y",20,"主任","5-21")
};
for(Emp e:emps){
System.out.println(e);
}
}
}
class Emp{
private int id;
private String name;
private int age;
private String job;
private String hiredate;
@Override
public String toString() {
return "Emp{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", job='" + job + '\'' +
", hiredate='" + hiredate + '\'' +
'}';
}
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;
}
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;
}
}
执行结果:
6.Comparable接口
多个对象做比较,就要有“比较规则”,然后实现排序。
事实上,java中排序算法的底层也依赖Comparable接口。
Comparable接口中只有一个方法:
public int compareTo(Object obj) // obj为要比较的对象
方法中,将当前对象和obj这个对象进行比较难,如果大于返回1,等一百返回0,小于返回-1.(此处的 1 也可以是正整数,-1 也可以是负整数)。 compareTo 方法的代码也比较固定:
public int compareTo(Object o) {
Man man = (Man) o;
if (this.age < man.age) {
return -1;
}
if (this.age > man.age) {
return 1;
}
return 0;
}
【测试 Comparable 接口】使用 Arrays 类对数组元素进行排序二
/**
* 测试Comparable 接口
*/
import java.util.Arrays;
public class test09 {
public static void main(String[] args) {
Man2[] msMans={new Man2(3,"a"),new Man2(60,"b"),new Man2(2,"c")};
System.out.println(Arrays.toString(msMans));
Arrays.sort(msMans);
System.out.println(Arrays.toString(msMans));
}
}
class Man2 implements Comparable{
int age;
int id;
String name;
public Man2(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return this.name;
}
@Override
public int compareTo(Object o) {
Man2 man = (Man2) o;
if (this.age < man.age) {
return -1;
}
if (this.age > man.age) {
return 1;
}
return 0;
}
}
输出结果:
7.常见的算法
动画演示网站:https://visualgo.net/
1.冒泡排序算法
a.冒泡排序的基础算法
冒泡排序算法重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就
把他们交换过来,这样越大的元素会经由交换慢慢“浮”到数列的顶端。
冒泡排序算法的运作如下:
- 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
- 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
- 针对所有的元素重复以上的步骤,除了最后一个。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
【示例】冒泡排序的基础算法
/**
* 测试冒泡排序基础算法
*/
import java.util.Arrays;
public class TestBuddleSort {
public static void main(String[] args) {
int[] values={3,1,6,8,9,0,7,4,5,2};
bubbleSort(values);
System.out.println(Arrays.toString(values));
}
public static void bubbleSort(int[] values){
int temp;
for(int i=0;i<values.length;i++){
for(int j=0;j<values.length-1-i;j++){
if (values[j] > values[j+1]){
temp=values[j];
values[j]=values[j+1];
values[j+1]=temp;
}
}
}
}
}
执行结果:
b.冒泡排序的优化算法
我们可以把上面的冒泡排序的算法优化一下,基于冒泡排序的以下特点:
- 整个数列分成两部分:前面是无序数列,后面是有序数列。
- 判断每一趟是否发生了数组元素的交换,如果没有发生,则说明此时数组已经有序,无需再进行后续趟数的比较了。此时可以中止比较。
【示例】冒泡排序的优化算法
/**
* 冒泡排序的优化算法
*/
import java.util.Arrays;
public class TestBuddleSort2 {
public static void main(String[] args) {
int[] values={3,1,6,8,9,0,7,4,5,2};
bubbleSort2(values);
System.out.println(Arrays.toString(values));
}
public static void bubbleSort2(int[] values){
int temp;
for(int i=0;i<values.length;i++){
//定义一个布尔类型的变量,标记数组是否已达到有序状态
boolean flag=true;
/*内层循环:每一趟循环都从数列的前两个元素开始比较,比较到无序
数组的最后*/
for(int j=0;j<values.length-1-i;j++){
//如果前一个元素大于后一个元素,则交换两个元素的值;
if(values[j]>values[j+1]){
temp=values[j];
values[j]=values[j+1];
values[j+1]=temp;
//本趟发生了交换,说明该数组在本趟处于无序状态,需要继续比较;
flag=false;
}
}
//根据标记量的值判断是否有序,若有序,则退出;若无序,则继续循环
if(flag){
break;
}
System.out.println((i+1)+"趟排序:"+Arrays.toString(values));
}
}
}
执行结果:
2.二分法查找
二分法检索(binary search)又称折半检索。
二分法检索的基本思想是设数组中的元素从小到大有序地存放在数组(array)中,首先将给定值 key 与数组中间位置上元素的关键码(key)比较,如果相等,则检索成功;否则,若 key 小,则在数组前半部分中继续进行二分法检索; 若 key 大,则在数组后半部分中继续进行二分法检索。
这样,经过一次比较就缩小一半的检索区间,如此进行下去,直到检索成功或检索失败。二分法检索是一种效率较高的检索方法。
import java.util.Arrays;
public class TestBinarySearch {
public static void main(String[ ] args) {
int[ ] arr = { 30,20,50,10,80,9,7,12,100,40,8};
int searchWord = 20; // 所要查找的数
Arrays.sort(arr); //二分法查找之前,一定要对数组元素排序
System.out.println(Arrays.toString(arr));
System.out.println(searchWord+"元素的索引:"+binarySearch(arr,searchWord));
}
public static int binarySearch(int[ ] array, int value){
int low = 0;
int high = array.length - 1;
while(low <= high){
int middle = (low + high) / 2;
if(value == array[middle]){
return middle; //返回查询到的索引位置
}
if(value > array[middle]){
low = middle + 1;
}
if(value < array[middle]){
high = middle - 1;
}
}
return -1; //上面循环完毕,说明未找到,返回-1
}
}
执行结果: