数组
day6
数组概述
- 数组Array,是多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理
- 数组相关的概念:
- 数组名
- 元素
- 角标、下标、索引
- 数组的长度
- 数组的特点:
- 数组是有序排列的
- 数组属于引用数据类型的变量,数组的元素,既可以是基本数据类型,也可以是引用数据类型
- 创建数组对象会在内存中开辟一整块连续的空间,而数组中引用的是这块连续空间的首地址
- 数组的长度一旦确定,就不能修改
- 数组的分类
- 按照维度:一维数组、二维数组、三维数组
- 按照元素的数据类型分:基本数据类型元素的数组、引用数据类型元素的数组(即对象数组)
- 在这里涉及到地址值的数值,与c语言中的指针不太一样的地方是java中的地址不是内存中的地址,而是jvm虚拟算出来的哈希值,相当于屏蔽了底层真实的地址
一维数组的使用
-
一维数组的声明和初始化
- 静态初始化: 数组的初始化和数组的元素的赋值操作同时进行
- 动态初始化:数组的初始化和数组的元素的赋值操作分开进行
-
总结:数组一旦初始化完成,其长度就确定了
int num;//声明 num = 10;//初始化 int id = 1001;//声明+初始化 int[] ids;//声明 //1.1 静态初始化: 数组的初始化和数组的元素的赋值操作同时进行 ids = new int[]{1001,1002,1003,1004}; //1.2 动态初始化:数组的初始化和数组的元素的赋值操作分开进行 String[] names = new String[5]; //总结:数组一旦初始化完成,其长度就确定了
-
如何调用数组的指定位置的元素
-
通过角标的方式调用
-
数组的角标(索引)从0开始,到数组的长度-1结束
names[0] = "Qiang"; names[1] = "Ming"; names[2] = "Hong"; names[3] = "Long"; names[4] = "Wang";//charAt(0);需要找字符串也是从0开始 // names[5] = "Out"//注意这里会有超出数组的范围的错误
-
-
如何获取数组的长度
-
array.length
System.out.println(names.length);//5 System.out.println(ids.length);
-
-
如何遍历数组
for(int i = 0;i < names.length;i++) { System.out.println(names[i]); }
-
数组元素的默认初始化值
-
数组元素是整型:0
-
数组元素是浮点型:0.0
-
数组元素是char型:0或'\u0000'(不是'0')
-
数组元素是boolean型:false (0)
-
数组元素是引用数据类型(String):null
//5. 数组元素的默认初始化值 int[] arr = new int[4]; for(int i = 0;i < arr.length;i++) { System.out.println(arr[i]); } System.out.println("------------"); short[] arr1 = new short[4]; for(int i = 0;i < arr1.length;i++) { System.out.println(arr1[i]); } System.out.println("------------"); float[] arr2 = new float[5]; for(int i = 0;i < arr2.length;i++) { System.out.println(arr2[i]); } System.out.println("------------"); char[] arr3 = new char[5]; for(int i = 0;i < arr3.length;i++) { System.out.println("--" + arr3[i] + "--"); } System.out.println(0==arr3[0]); System.out.println('0'==arr3[0]); System.out.println("------------"); //不能使用静态初始化 boolean[] arr4 = new boolean[5]; for(int i = 0;i < arr4.length;i++) { System.out.println(arr4[i]); } System.out.println("------------"); //不能使用静态初始化 String[] arr5 = new String[5]; for(int i = 0;i < arr5.length;i++) { System.out.println(arr5[i]); } System.out.println(arr5[0] == null); System.out.println(arr5[0] == "null");
-
-
数组的内存解析
-
栈(stack):局部变量
-
堆(heap):new出来的结构:对象、数组
-
内存简化结构
当arr1不再指向0x12ab时,这里0x12ab这个数组会在不定的时间内给jvm的垃圾回收机制清除。从广义的角度来看数据的结构是这样存放,但具体的内存结构会有所不同
左边为栈,右边为堆,当程序允许完毕,就会出栈,出栈后,右边堆的元素就会给jvm的内存回收机制清楚
-
一维数组练习
-
练习1,判断输出
public class ArrayDemo1 { public static void main(String[] args) { int[] arr = new int[] { 8, 2, 1, 0, 3 }; int[] index = new int[] { 2, 0, 3, 2, 4, 0, 1, 3, 2, 3, 3 }; String tel = ""; for (int i = 0; i < index.length; i++) { tel += arr[index[i]]; } System.out.println("联系方式:" + tel); } }
-
练习2
- 从键盘读入学生成绩,找出最高分,并输出学生成绩等级,成绩>=最高分-10, A,成绩>=最高分-20, B,成绩>=最高分-30, C,其余,D
import java.util.Scanner; public class ArrayDemo2 { public static void main(String[] args) { // 1. 使用Scanner,读取学生个数 Scanner scanner = new Scanner(System.in); System.out.print("请输入学生人数:"); // 2. 创建数组,存储学生成绩,动态初始化 int num = scanner.nextInt(); int[] scores = new int[num]; // 3. 给数组中的元素赋值 int max = 0; System.out.println("请输入" + num + "个学生成绩"); for (int i = 0; i < scores.length; i++) { scores[i] = scanner.nextInt(); // 4. 获取数组中元素的最大值:最高分 if (scores[i] > max) max = scores[i]; } System.out.println("最高分是:" + max); // 5. 根据每个学生成绩与最高分的差值,得到每个学生的等级,并输出等级和成绩 char level; for (int i = 0; i < scores.length; i++) { if (scores[i] >= max - 10) { level = 'A'; } else if (scores[i] >= max - 20) { level = 'B'; } else if (scores[i] >= max - 30) { level = 'C'; } else { level = 'D'; } System.out.println("student " + i + " score is " + scores[i] + ",grade is " + level); } } }
二维数组的使用
- java语言里提供支持多维数组的语法
- 二维数组相当于是一个表格,他有行与列
- 理解
- 对于二维数组的理解,我们可以看成是一维数组array1又作为另一个一维数组array2的元素二存在。其实从数组底层的运行机制来看,其实没有多维数组
- 二维数组的使用
-
二维数组的声明和初始化
//一维数组的静态初始化 int[] arr = new int[] {1,2,3}; //二维数组的静态初始化 int[][] arr1 = new int[][]{{1,2,3},{4,5},{6,7,8}}; int[][] arr4 = new int[][]{{1,2,3},{4,5,9,10},{6,7,8}}; //也是正确的写法 int[][] arr11 = {{1,2,3},{4,5},{6,7,8}}; //二维数组的动态初始化1 String[][] arr2 = new String[3][2];//3行2列 //二维数组的动态初始化2 String[][] arr3 = new String[3][];//3行n列 String[] arr31[] = new String[3][];//3行n列 String arr32[][] = new String[3][];//3行n列
-
如何调用数组的指定位置的元素
System.out.println(arr1[0][1]);//2 System.out.println(arr2[1][1]);//null,第二行的第二个元素 //System.out.println(arr3[1][1]);//直接输出是空指针异常报错,因为arr3指向的数组[1]内没有指向的数组 arr3[1] = new String[4]; System.out.println(arr3[1][0]);//null
-
如何获取数组的长度
System.out.println(arr4.length);//3,因为内存指向的是三个长度的数组 System.out.println(arr4[0].length);//3 System.out.println(arr4[1].length);//4,这里指向的是arr4[1]指向的数组长度
-
如何遍历二维数组
for(int i = 0;i < arr4.length;i++) { for(int j = 0;j < arr4[i].length;j++) { System.out.print(arr4[i][j] + " "); } System.out.println(); }
-
数组元素的默认初始化值
- 规定:二维数组分为外层数组的元素,内层数组的元素
int[][] arr = new int[4][3];
- 外层元素:
arr[0],arr[1]
等 - 内层元素:
arr[0][0],arr[1][2]
等
- 外层元素:
[I@75a1cd57
[
一维的数组I
int型的@75a1cd57
地址在@后面的数
[[I@515f550a
[[
二维数组I
int型@515f550a
地址在@后面的数
int[][] arr = new int[4][3]; System.out.println(arr[0]);//地址值 [I@75a1cd57 `[`一维的 `I`int型的 `@75a1cd57`在@后面的地址 System.out.println(arr[0][0]);//0 // System.out.println(arr);//[[I@515f550a System.out.println("-------------"); float[][] arr1 = new float[4][3]; System.out.println(arr1[0]);//地址值 [F@515f550a System.out.println(arr1[0][0]);//0.0 System.out.println("-------------"); String[][] arr2 = new String[4][3]; System.out.println(arr2[0]);//地址值 [Ljava.lang.String;@123a439b System.out.println(arr2[0][0]);//null System.out.println("-------------"); double[][] arr3 = new double[4][]; System.out.println(arr3[1]);//null // System.out.println(arr3[1][0]);//报错-空指针异常
-
二维数组的内存解析
int[][] arr1 = new int[4][]; arr1[1] = new int[]{1,2,3}; arr1[2] = new int[4]; arr1[2][1] = 30;
-
数组的练习
数组的复制
- 在下面代码中,其实就
array2=array1
这个操作就相当于将array1的地址复制到array2,这里只new了一次,所以在内存中就只有一个数组,所以对array2修改其实就是对array1修改 - array1和array2地址值相同,都指向了堆空间的唯一的一个数组实体
/*
创建两个int数组,存入8个素数
输出array,将array2=array1,修改array2中的偶数的值让其等于索引值,并打印array1
*/
class cvArrayExr {
public static void main(String[] args) {
int[] array1, array2;
array1 = new int[]{2,3,5,7,11,13,17,19};
for(int i = 0;i < array1.length;i++){
System.out.print(array1[i] + "\t");
}
System.out.println();
//这个操作不能称作数组的复制,他只是将array1的值复制给array2
//这里自始自终就只有一个数组,因为上面就new了一次
array2 = array1;
for (int i = 0;i < array2.length;i++){
if(i % 2 == 0){
array2[i] = i;
}
}
for(int i = 0;i < array1.length;i++){
System.out.print(array1[i] + "\t");
}
}
}
真正意义上的复制
- 这里是new了两个数组,真正意义上的复制
/*
数组的复制
*/
class cvArrayExr1 {
public static void main(String[] args) {
int[] array1, array2;
array1 = new int[]{2,3,5,7,11,13,17,19};
for(int i = 0;i < array1.length;i++){
System.out.print(array1[i] + "\t");
}
System.out.println();
//这个操作不能称作数组的复制,他只是将array1的值复制给array2
//这里自始自终就只有一个数组,因为上面就new了一次
//array1和array2地址值相同,都指向了堆空间的唯一的一个数组实体
array2 = new int[array1.length];
for (int i = 0;i < array2.length;i++){
if(i % 2 == 0){
array2[i] = i;
}
else{
array2[i] = array1[i] + 1;
}
}
for(int i = 0;i < array1.length;i++){
System.out.print(array1[i] + "\t");
}
System.out.println();
for(int i = 0;i < array2.length;i++){
System.out.print(array2[i] + "\t");
}
}
}
考察
-
写出一维数组初始化的两种方式
int[] arr = new int[5];//动态初始化 String[] arr1 = new int[]{'ming','qiang','mi'};//静态初始化
- 数组一旦初始化,其长度就是确定的,且数组长度一旦确定就不可修改
- 获取数组长度:arr.length
-
写出二维数组初始化的两种方式
int[][] arr2 = new int[4][3];//动态初始化1 int[][] arr3 = new int[4][3];//动态初始化2 int[][] arr3 = new int[][]{{1,2,3},{4,5,6},{7,8}};//静态初始化
-
如何遍历如下的二维数组
int[] arr = new int[][]{{123},{4,5},{6,7,9}} for(int i = 9;i < arr.length;i++){ for(int j = 0;j < arr[i].length;j++){ System.out.print(arr[i][j] + "\t"); } System.out.println(); }
-
不同类型的一维数组元素的默认初始化值各是多少
整形: 0 浮点型: 0.0 char: 0 boolean: false 引用类型: null
-
一维数组的内存解析
String[] strs = new String[5]; strs[2] = "Tom"; strs = new String[3];
引用类型的遍历,要么是null,要么是地址值(存放于常量池里面)
关于数组的拓展
day7
数组的排序算法
十大内部排序算法
-
选择排序
- 直接选择排序、堆排序
-
交换排序
- 冒泡排序、快速排序
-
插入排序
- 直接插入排序、折半插入排序、shell排序
-
归并算法
-
桶式算法
-
基数算法
-
满足确定性的算法也称为:确定性算法。选择人们也关注更广泛的概念,例如考虑葛洪非确定性的算法,如并行算法、概率算法等。另外,人们也关注并不要求终止的计算描述,这种描述有时候被称为过程(procedure)
理解
-
衡量排序算法的优劣
- 时间复杂度、空间复杂度、稳定性
-
排序的分类
- 内部排序与外部排序(需要借助于磁盘)
-
不同排序算法的时间复杂度
- 从平均时间而言:
- 快速排序最佳,但再最坏情况下时间性能不如堆排序和归并排序
- 从算法简单性看:
- 由于直接选择排序、直接插入排序和冒泡排序的算法比较简单,将其认为是简单算法。
- 对于shell排序、堆排序、快速排序和归并排序算法,其算法比较复杂,认为是复杂排序
- 从稳定性看:
- 直接插入排序、冒泡排序和归并排序是稳定的;
- 而直接选择排序、快速排序、shell排序和堆排序是不稳定排序
- 从待排序的记录数n的大小看:
- n较小时,宜采用简单排序;
- 而n较大时宜采用改进排序
- 排序算法的选择
- 若n较小(n<=50),可采用直接插入或直接选择排序
- 当记录规模较小时,直接插入排序较好;否则因为直接选择移动的记录数少于直接插入,应选直接选择排序为宜
- 若文件初始状态基本有序(指正序),则一ing有直接插入、冒泡或随机的快速排序为宜
- 若n较大,则应才有时间复杂度为O(nlogn)的排序方法:快速排序、堆排序或归并排序
- 若n较小(n<=50),可采用直接插入或直接选择排序
-
冒泡排序
int[] arr = new int[]{43,32,76,-98,0,64,33,-21,32,99}; //冒泡排序 for(int i = 0;i < arr.length - 1;i++){ for(int j = 0;j < arr.length - 1 - i;j++){ if(arr[j] > arr[j + 1]){ int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } }
Arrays工具类的使用
java.uril.Arrays
类即为操作数组的工具类,包含了用来操作数组(比如排序和搜素哦)的各种方法boolean equals(int[] a,int[] b)
,判断两个数组是否相等String toString(int[] a)
,输出数组信息void fill(int[] a,int val)
,将指定值填充到数组之中void sort(int[] a)
,对数组进行排序int binarySearch(int[] a,int key)
,对排序后的数组进行二分法检索指定的值
Eclipse的使用
此部分为day6的部分
关于Eclipse建议的设置
- 窗口,其他的根据具体需求使用
- navigator
- package explorer
- outline
- console
- 设置(window->Proferences
- 编码设置
- General->Wrokspace: 最下面找到Text file encoding 将default改为other(UTF-8)
- 如果不改就会出现乱码问题
- 字体放大
- General->Appearance->Colors and Fonts: Basic->Text Fone
- 然后点击右边的Edit将字体大小改为12-16
- 编码设置
- 设置新建的文件
- window->perspective->customize perspective
- file->new
- 将new的勾选全取消
- 将以下打勾
- java project
- package
- class
- interface
- enum
- annotation
- junit test case
- folder
- file
- jsp file
- dynamic web project
- servlet
- filter
- listener
- html file
- xml file
- file->new
- 如果上述的文件格式你找不到,那你需要在设置里面稍作修改
- window->perspective->customize perspective
- 如果你要将上面描述的保存
- window->perspective->save perspective as
- 你可以将默认的给替换掉
- window->perspective->save perspective as
- 如果后续编辑界面乱了,你可以在
window->perspective->reset perspective
中恢复
Hello实例
- 新建project,new->java project
- 添加名字,版本自选,后面默认下一步即可
- 在
src
中新建包package
- 包里面放源文件,包的概念是将代码变成一个一个的包
- 格式:
com.[域名].[功能]
- 在包
package
中创建class- 增加名字即可
关于使用
- 在eclipse中也有快捷键
main
+alt+/
:自动写public static void main(String[] args)
syso
+alt+/
:自动写System.out.println();
- 运行:右键找到run as->java application
- 如果你没写main,eclipse就会找不到入口
常见问题
- 双击eclipse不能正常启动:
- 启动失败有很多种原因
- 查看环境变量是否正确,cmd输入javac.exe或java.exe进行检查
- 是否正确的安装了jdk和jre
- 安装的jdk版本(32位或64位),必须与eclipse一致
- 修改eclipse安装目录下的eclipse.ini配置文件
- 启动失败有很多种原因
考察
-
使用冒泡排序
int[] arr = new int[]{34,5,22,-98,6,-76,0,-3} for(int i = 0;i < arr.length-1;i++){ for(int j = 0;j < arr.length - 1 - i;j++){ if (arr[j] > arr[j + 1]){ arr[j] += arr[j + 1]; arr[j + 1] = arr[j] - arr[j + 1]; arr[j] = arr[j] - arr[j + 1]; } } }
- String按照ascii的大小排序
- 冒泡排序的时间复杂度:O(N^2)
- 快速排序时间复杂度:O(nlogn)
- 堆排序、归并排序
-
如何反转数组
int[] arr1 = {-24,-4,4,4,6,23,39,41,95,98}; for(int i = 0,j = arr1.length - 1;i < j;i++;j--){ arr[j] += arr[i]; arr[i] = arr[j] - arr[i]; arr[j] = arr[j] - arr[i]; }
-
复制数组
//复制地址 arr2 = arr1; //复制内容 int[] arr2 = new int[arr1.length]; for(int i = 0;i < arr1.length;i++){ arr2[i] = arr1[i]; }
-
线性查找
int dest = 23; //boolean isFlag = true; for(int i = 0;i < arr2.length;i++){ if(dest == arr2[i]){ System.out.println("地址值为" + i); //isFlag = false; break; } } //if (isFlag) // System.out.println("未找到"); //第二种方法 if (i == arr2.length) System.out.println("未找到");
-
说出数组的常见异常
-
ArrayIndexOutOfBoundsException
: 数组角标越界异常- 合理范围:
[0, arr.length - 1]
- 越界:
arr[-1], arr[arr.length]
- 合理范围:
-
NullPointerException
: 空指针异常int[] arr = null; arr[0];
-