数组
数组概述
什么是数组
-
数组是一种 [固定长度] 的容器, 可用来存储同种数据类型的多个值.
- 创建了一个int类型的数组容器, 这个容器就只能存储int类型的数据
- 创建了一个double类型的数组容器, 这个容器就只能存储double类型的数据
- 不可能出现的事 : 容器中同时存在, 小数和字符串.
注意: double类型的数组容器, 内部可以存储double小数, 也可以存储int类型整数.
int类型的数组容器, 内部可以存储 byte, short, charr 类型
- 数组是一种数据类型 — 引用数据类型
- 引用、记录了地址值的变量,所对应的数据类型,就是引用数据类型
数组元素访问
数组名[索引];
使用方式:
存入: 数组名[索引] = 数据;
将等号右边的数据, 存入数组中指定索引的位置
取出:
System.out.println(数组名[索引]);
打印数组中指定索引位置的元素
数据类型 变量名 = 数组名[索引];
将数组中指定索引位置的元素, 赋值给等号左边定义的变量
默认初始化值
- 整数类型 : 0
- 小数类型 : 0.0
- 字符类型 : ‘\u0000’ (空字符)
- 布尔类型 : false
- 引用数据类型 : null
内存分配
区域名称 | 作用 |
---|---|
栈内存 | 方法运行时,进入的内存,局部变量都存放于这块内存当中 |
堆内存 | new出来的内容(数组, 对象)都会进入堆内存,并且会存在地址值 |
方法区 | 字节码文件 (.class文件) 加载时进入的内存 |
本地方法栈 | 调用操作系统相关资源 |
寄存器 | 给CPU使用 |
- 注意 :
-
JDK7 : 方法区 ( 永久代 ) 在堆内存的
-
JDK8 : 方法区成为了一块独立的内存空间 ( 元空间 )
-
数组定义格式
数组定义格式
数据类型[] 数组名;
示例: int[] arr;
数据类型 数组名[];
示例: double arr[];
静态初始化格式 :
完整格式:
数据类型[] 数组名 = new 数据类型[]{元素1, 元素2, 元素3...};
正确范例 : int[] arr = new int[]{11,22,33};
简化格式:
数据类型[] 数组名 = {元素1, 元素2, 元素3...};
范例 : int[] arr = {11,22,33};
动态初始化格式 :
数据类型[] 数组名 = new 数据类型[数组长度];
int[] arr = new int[5];
* 等号左边:
- int:数组的数据类型
- []:代表这是一个数组
- arr:代表数组的名称
* 等号右边:
- new:为数组开辟内存空间
- int:数组的数据类型
- []:代表这是一个数组
- 5:代表数组的长度
-
注意 :
-
打印数组名 , 出现的是数组在内存中的 【内存地址】
- 例如:[I@10f87f48
数组案例
增强for循环 [^遍历数组]
//增强for循环(不能直接获得index)
// for((数组元素)数据类型 变量名称:数组名称){
// //变量名称(是数组里面的任意元素)
// }
//遍历数组
int count = 1;
for (int score : studentScore) {//studentScore每个元素依次赋值给score
System.out.println("第" + count + "元素:"+score);
count++;
}
1.动态录入学生个数 以及学生成绩,获得总成绩,成绩最值
public static void main(String[] args) {
//动态录入学生个数 以及学生成绩,获得总成绩,成绩最值
Scanner input = new Scanner(System.in);
System.out.println("请录入学生的个数:");
int studentCount = input.nextInt();
double totalScore = 0;
double[] studentScore = new double[studentCount];
for (int i = 0; i < studentCount; i++) {
System.out.println("请录入第" + (i + 1) + "个学生的成绩:");
double score = input.nextDouble();
//统一维护studentCount个score
studentScore[i] = score;
totalScore += score;
}
//相邻数组元素相互比较 (三元运算符)
double max = studentScore[0];
double min = studentScore[0];
// for (int i = 1; i < studentScore.length; i++) {
// }
for (double s : studentScore) {
max = (max > s) ? max : s;
min = (min < s) ? min : s;
}
System.out.println("总成绩:" + totalScore);
System.out.println("成绩最大值:" + max);
System.out.println("成绩最小值:" + min);
}
Arrays
提供了很多功能,可以操作数组元素。 java.util.Arrays
Arrays.toString("数组名称");// 将数组元素拼接成字符串。
Arrays.equals(数组1,数组2); //比较2个数组元素是否一致 boolean
Arrays.copyOf(源数组,新数组的空间大小);//将源数组的数据存入指定空间大小的新数组中
Arrays.sort(数组名称);//字面量类型 升序排列数组元素
//Arrays.equals()
System.out.println(Arrays.equals(num1, num2));
//手动扩容 Arrays.copyOf
String[] copyOf = Arrays.copyOf(str, 5);
System.out.println("copyOf:"+Arrays.toString(copyOf));
System.out.println(copyOf==str);//false
排序(了解)
对数组元素按照指定的自然顺序进行排列,(升序,降序)
冒泡排序
原理: 将相邻的两个元素进行比较,最大的值放在右端。N个数字要排序完成,总共进行N-1趟排序,每i趟的排序次数为(N-i)次,所以可以用双重循环语句,外层控制循环多少趟,内层控制每一趟的循环次数。
所有的字面量类型的数组可以使用排序来完成。
int[] array = {89,20,68,10}; length-1轮
第一轮:
指针指向第一个元素89,与20比较 发现 89>20 交换位置 20,89,68,10
指针指定第二个元素89,与68比较,发现89>68 交换位置 20,68,89,10
指针指定第二个元素89, 与10比较,发现89>10 交换位置 20,68,10,89
第二轮:
指针指向第一个元素20,与68比较 20<68 不交换 20,68,10,89
指针指向第一个元素68 与10比较 68>10 交换 20,10,68,89
第三轮:
指针指向第一个元素20,与10比较 20>10 交换 10,20,68,89
for(int i=1,len=array.length;i<len;i++){
for(int index=0;index<len-i;index++){
if(array[index]>array[index+1]){
//交换位置
int temp = array[index];
array[index] = array[index+1];
array[index+1] = temp;
}
}
}
选择排序
从第一个元素开始,分别与后面的元素相比较,找到最小的元素与第一个元素交换位置;从第二个元素开始,分别与后面的元素相比较,找到剩余元素中最小的元素,与第二个元素交换;重复上述步骤,直到所有的元素都排成由小到大为止。
数据量过大的话,使用冒泡排序效率比较低,因为一个元素会参与多次比较 。
交换次数会少于冒泡排序。
最小的元素与当前第几个元素做交换。
最小元素,以及最小元素的索引位置。
int[] array = {89,20,68,10};
第一轮:
指针指向第一个(误认为最小元素是第一个)89,
先于20比,89>20, 最小值变成20
20与68比较 68>20 最小值变成20
20再与10比较 20>10 最小值变成10
交换: 89 与10交换 结果: 10 20 68 89
第二轮:
指针指向第2个20
20先于68 再与89 最小值20
交换: 20与20交换 10 20 68 89
第三轮:
指针指向第三个68
与89
68与68交换 10 20 68 89
for(int i=0,len=array.length;i<len-1;i++){
int min = array[i];
int minIndex = i;
for(int j=i+1;j<len;j++){
//比较 找最小值 最小值的索引
if(min>array[j]){
min = array[j];
minIndex = j;
}
}
//交换位置
int temp = arry[i];
array[i] = min;
array[minIndex] = temp;
}
插入排序
原理: 将指针指向某个==(第二个)==元素,假设该元素左侧的元素全部有序,将该元素抽取出来,然后按照从右往左的顺序分别与其左边的元素比较,遇到比其大的元素便将元素右移,直到找到比该元素小的元素或者找到最左面发现其左侧的元素都比它大,停止.此时会出现一个空位,将该元素放入到空位中,此时该元素左侧的元素都比它小,右侧的元素都比它大;指针向后移动一位,重复上述过程。每操作一轮,左侧有序元素都增加一个,右侧无序元素都减少一个。
int[] array = {89, 200, 68, 10,50};
第一轮:
指针指向第2个: 200 将200抽取 89,__,68,10,50
200先89比较 200>89 89不右移
200填补空位 89,200,68,10,50
第二轮:
指针指向第3个: 68 89,200,__,10,50
68先与200比较 200>68 200右移 89,__,200,10,50
68与89比较 89>68 89右移 __,89,200,10,50
68填补空位 68,89,200,10,50
第3轮:
指针指向第4个: 10
10,68,89,200,50
第4轮:
指针指向第5个: 50
10,50,68,89,200
for(int i=1,len=array.length;i<len;i++){
int temp = array[i];
int leftIndex = i-1;// 索引: 0-length-1
while(leftIndex>=0 && array[leftIndex]>temp){
array[leftIndex+1] = array[leftIndex];//右移
leftIndex--;//负数
}
array[leftIndex+1] = temp;
}
二维数组
二维数组的元素全部都是一维数组。
定义格式
1. 数据类型[][] 数组名称 = new 数据类型[m][n];//初始化二维数组
m:代表的二维数组的元素个数 length
n: 代表的是二维数组中一维数组的元素个数
2. 数据类型[][] 数组名称 = new 数据类型[m][];//常用
m:代表的二维数组的元素个数 length
3. 数据类型[][] 数组名称 = {{1,2,3},{3,4},{1}};//测试
数据类型[][] 数组名称 = new 数据类型[][]{{1,2,3},{3,4},{1}}
操作
public static void main(String[] args) {
// 数据类型[][] 数组名称 = new 数据类型[m][n];
int[][] num = new int[5][3];
System.out.println("长度:"+num.length);
System.out.println(num);//[[I@1b6d3586
System.out.println(Arrays.toString(num));
//[[I@4554617c, [I@74a14482, [I@1540e19d, [I@677327b6, [I@14ae5a5]
//Arrays 提供方法可以操作二维数组元素
System.out.println(Arrays.deepToString(num));
//[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
//操作二维数组元素 index
//取值
// int[] array = num[0];
// System.out.println(array[0]);
int i1 = num[0][0];//获得第一个一维数组的第一个元素
//赋值
num[3][0] = 90;
num[1][2] = 100;
System.out.println(Arrays.deepToString(num));
//遍历
for(int i=0,len = num.length;i<len;i++){
for (int j = 0; j < num[i].length; j++) {
System.out.println("第"+(i+1)+"个一维数组的第"+(j+1)+"元素是:"+num[i][j]);
}
}
for (int[] ints : num) {
for (int anInt : ints) {
System.out.println(anInt);
}
}
}
案例
需求:
动态录入班级数 以及每个班级的人数以及每个学生的成绩
遍历每个学生的成绩
public static void main(String[] args) {
// 1. 动态录入班级数 以及每个班级的人数以及每个学生的成绩
Scanner input = new Scanner(System.in);
System.out.println("录入班级数:");
int classCount = input.nextInt();
int[][] classArray = new int[classCount][];
for (int i = 0; i < classCount; i++) {
System.out.println("请录入第" + (i + 1) + "班级人数:");
int studentCount = input.nextInt();
//对二维数组元素初始化
classArray[i] = new int[studentCount];//重点
for (int j = 0; j < studentCount; j++) {
System.out.println("请录入第" + (i + 1) + "班级,第:" + (j + 1) + "学生的成绩:");
int score = input.nextInt();
//成绩存储到二维数组里面
classArray[i][j] = score;//NPE
}
}
// 2.遍历每个学生的成绩
int count1 = 1;//班级
for (int[] room : classArray) {
int count2 = 1;//学生
for (int score : room) {
System.out.println("第" + count1 + "个班级的第" + count2 + "个学生的成绩是:" + score);
count2++;
}
count1++;
}
}
可变参数
在JDK1.5之后,如果我们定义一个方法需要接受多个参数,并且多个参数类型一致,我们可以对其简化成如下格式:
修饰符 返回值类型 方法名(参数类型... 形参名){ }
其实这个书写完全等价与
修饰符 返回值类型 方法名(参数类型[] 形参名){ }
只是后面这种定义,在调用时必须传递数组,而前者可以直接传递数据即可。
JDK1.5以后。出现了简化操作。… 用在参数上,称之为可变参数。
同样是代表数组,但是在调用这个带有可变参数的方法时,不用创建数组(这就是简单之处),直接将数组中的元素作为实际参数进行传递,其实编译成的class文件,将这些元素先封装到一个数组中,在进行传递。这些动作都在编译.class文件时,自动完成了。
代码演示:
public class ChangeArgs {
public static void main(String[] args) {
int[] arr = { 1, 4, 62, 431, 2 };
int sum = getSum(arr);
System.out.println(sum);
// 6 7 2 12 2121
// 求 这几个元素和 6 7 2 12 2121
int sum2 = getSum(6, 7, 2, 12, 2121);
System.out.println(sum2);
}
/*
* 完成数组 所有元素的求和 原始写法
public static int getSum(int[] arr){
int sum = 0;
for(int a : arr){
sum += a;
}
return sum;
}
*/
//可变参数写法
public static int getSum(int... arr) {
int sum = 0;
for (int a : arr) {
sum += a;
}
return sum;
}
}
tips: 上述add方法在同一个类中,只能存在一个。因为会发生调用的不确定性
注意:如果在方法书写时,这个方法拥有多参数,参数中包含可变参数,可变参数一定要写在参数列表的末尾位置。
方法
方法组成部分
public static void main(String[] args) {
}
- [访问权限修饰符 public protected private 默认(什么都不写)]
- [普通修饰符 static final abstract]
- 返回值类型
有返回值类型:所有的数据类型都可以充当返回值类型
无返回值类型: void
-
方法名 (规范: 小写驼峰 目前唯一的)
-
形式参数(局部变量) 形参—> 规范 规则 方法有几个特定类型的参数
无参: ()
有参: 可以有1个参数 也可以有多个参数(使用,隔开)
可变参数: (数据类型… 参数名称) 出现参数的最后
-
方法体 { }
方法的完整格式
/*
参数列表的格式: 数据类型1 变量名1, 数据类型2 变量名2 ...
修饰符 返回值类型 方法名(参数列表) {
方法体;
return [值]; // []代表可能有也可能没有
}
详细介绍:
修饰符: public static
返回值类型: 我使用了这个功能, 我能得到什么
方法名: 合法的标识符
参数列表: 我使用了这个功能, 我需要提供什么
方法体 : 方法实现功能的代码
return : 返回
*/
方法定义的通用格式
public static 返回值类型 方法名(参数列表) {
方法体;
return 返回值; // 如果返回值类型为void可以省略
}
// 明确两个地方法: 返回值类型, 参数列表
方法分类
主要是参考形式参数和返回值类型来进行分类: 方法与方法之间是同级的关系
1. 无参无返回值方法(测试)
2. 有参无返回值方法(相对较少)
3. 无参有返回值方法
4. 有参有返回值的方法(最多)
定义方法
无参无返回值
//创建
public static void exercise1(){
Scanner input = new Scanner(System.in);
//利用数组维护10个得分
int length = 3;
int[] scores = new int[length];
for (int i = 0; i < length; i++) {
System.out.println("请录入第" + (i + 1) + "个得分");
int score = input.nextInt();
scores[i] = score;
}
//最高分,去掉一个最低分
Arrays.sort(scores);//升序排列
System.out.println(scores);
}
有参无返回值
/**
* 用户登录
*/
public static void userLogin(String username, String password) {
if (username == null || password == null) {
System.out.println("参数不能为null");
return;
}
if(!"admin".equals(username) || !"1234".equals(password) ){
System.out.println("登录失败");
return;
}
username = "administrator";
System.out.println("登录成功,欢迎你:"+username);
}
public static void main(String[] args) {
// 传参: 实际参数--> 实参--> 值(内存地址值)传递(如果参数是字面量数据类型 传递的就是数据)
//其它引用数据类型: 内存地址值
//调用登录的方法
String username = "admin";// string数据不可变
String pass = "1234";
userLogin(username,pass);
System.out.println(username+"====="+pass);
System.out.println("main结束。。。。。。。");
}
// public static void modifyArray(int[] num){
// //数组是引用数据类型 一定要提前预判 ==null 避免出现一些异常
// if (num==null){
// System.out.println("参数不能为null");
// return;
// }
// //length=0
// if (num.length==0){
// System.out.println("是空数组,无法修改");
// return;
// }
/**
* 不知道会传过来多少同类型的参数 使用可变参数替换
* 方法的形参数量一般不会超过4个,方法体代码量不要超过80行
*/
/**
* 用户注册--->提交数据(从页面拿数据都是String)
* @param id 用户id
* @param params 参数
*/
public static void userRegister(int id,String...params){
System.out.println(id);
System.out.println("params:"+Arrays.toString(params));//[Ljava.lang.String;@1b6d3586
System.out.println(params[0]);
}
public static void main(String[] args) {
System.out.println("main开始。。。。。。。");
//调用userRegister
//可变参数: 传参的时候>=0数据 就是一个数组
userRegister(1001,"jim","男","河南郑州");
System.out.println("main结束。。。。。。。");
}
无参有返回值
/**
* 有返回值的方法 必须加 return 数据;
* @return
*/
public static boolean lucky(){
Scanner input = new Scanner(System.in);
System.out.println("请录入会员号:");
int cardNo = input.nextInt();
//随机生成4个幸运的数字 4位数字 1000-10000
for (int i = 0; i < 4; i++) {
int random = (int)(Math.random()*9000+1000);
if(cardNo==random){
return true;
}
}
return false;
}
public static void main(String[] args) {
System.out.println("main开始。。。。。。。");
//调用方法: 对于返回值: 可以接收 可以不接收
// boolean flag = lucky();
// if(flag){
// System.out.println("是幸运的会员");
// }else{
// System.out.println("不是");
// }
System.out.println(lucky());
// System.out.println(Arrays.toString(args));
System.out.println("main结束。。。。。。。");
}
有参有返回值(最重要 用的也最多)
public static boolean userLogin(String username, String password) {
if (username == null || password == null) {
System.out.println("参数不能为null");//红色
//错误的日志 没有控制台的概念 以后的项目都在服务器---> 日志框架--->
return false;
}
if(!"admin".equals(username) || !"1234".equals(password) ){
System.out.println("登录失败");
return false;
}
return true;
}
public static void main(String[] args) {
System.out.println("main开始。。。。。。。");
if(userLogin("admin","1234")){
System.out.println("success");
}else{
System.out.println("error");
}
//调用的其它类的方法
// Arrays.toString(new int[]{1,2,3});
System.out.println("main结束。。。。。。。");
}
调用方法
在方法里面调用方法:
public static void exercise2(){
//dshhsdg
exercise1();
}
public static void main(String[] args) {
System.out.println("main开始。。。。。。。");
//执行exercise1的逻辑
exercise2();//调用方法
System.out.println("main结束。。。。。。。");
}
方法重载
//方法重载: 底层一些工具类的方法
//1. 方法名相同
//2. 参数不同
//3. 不考虑返回值以及修饰符
public static long getMax(long lon1, long lon2) {
return (lon1 > lon2) ? lon1 : lon2;
}
/**
* 求2个整数最大值
*
* @param num1 数字1
* @param num2 数据2
*/
public static int getMax(int num1, int num2) {
return (num1 > num2) ? num1 : num2;
}