目录
③利用Java中操作数组的工具类 Arrays(数组内容以字符串的形式打印)
认识数组
数组是存储一组相同数据类型的数据的集合。
数组的定义
第一种定义方法且初始化
第二种定义方法但不初始化
第三种定义数组的且初始化的方式
数组的基本用法
1.访问数组内容
数组的长度无需通过计算求得,可以通过arr.length直接求得。当数组下标为负数或者超出数组大小范围,会发生数组越界,编译器会报错,如:
2.在数组上写入数据
3.数组的遍历
①for循环遍历数组
for (int i = 0;i < arr.length;i++){
System.out.print(arr[i] + " ");
}
②for-each遍历数组
for(数组中的数据类型定义一个整型变量用于接受整型变量 : 数组名)
例如:
for (int x : arr){
System.out.print(x + " ");
}
注意:for和for-each的区别:
for:可以拿到数组的下标。
for-each:不可以拿到数组的下标,一般运用与集合中。
③利用Java中操作数组的工具类 Arrays(数组内容以字符串的形式打印)
先导入数据包,用toString形式打印
import java.util.Arrays;
System.out.println(Arrays.toString(arr));
数组作为方法的参数
基本用法
JVM
JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。
1.程序计数器 (PC Register): 只是一个很小的空间, 保存下一条执行的指令的地址.
2.虚拟机栈(JVM Stack): 重点是存储局部变量表(当然也有其他信息). 我们刚才创建的 int[] arr 这样 的存储地址的引用就是在这里保存.
3.本地方法栈(Native Method Stack): 本地方法栈与虚拟机栈的作用类似. 只不过保存的内容是 Native方法的局部变量. 在有些版本的 JVM 实现中(例如HotSpot), 本地方法栈和虚拟机栈是一起的.
堆(Heap): JVM所管理的最大内存区域. 使用 new 创建的对象都是在堆上保存 (例如前面的 new int[]{1, 2,3} )
4.方法区(Method Area): 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据. 方法编译出的的字节码就是保存在这个区域.
5.运行时常量池(Runtime Constant Pool): 是方法区的一部分, 存放字面量(字符串常量)与符号引用. (注意 从 JDK
1.7 开始, 运行时常量池在堆上).
数组在内存中的存储
1.数组内容是对象,数组名相当于引用变量。引用变量指向个对象,引用变量存放对象的地址存储在栈区,对象存储在堆区,可以解释为引用指向对象。
2.当引用变量不指向任何一个对象时,进行计算会报错
int[] Arrays2 = null;//代表Arrays引用不指向任何一个对象,一下操作会报错
System.out.print(Arrays2.length);
System.out.print(Arrays2[2]);
3.数组作为参数打印传递到方法打印数组内容
public static void print(int[] Arrays){
for (int i = 0; i < Arrays.length;i++) {
System.out.print(Arrays[i] + " ");
}
}
public static void main(String[] args) {
int[] Arrays = {1,2,3,4,5,6};
printArray(Arrays);
}
其在内存的操作过程:
4.引用指向对象
public static void func1(int[] array){
array= new int[]{11,22,33,44,55,66};
}
public static void func2(int[] array) {
array[1] = 22;
}
public static void main(String[] args) {
int[] array = {1,2,3,4,5,6};
System.out.println(Arrays.toString(array));
//func1(array);
func2(array);
System.out.println(Arrays.toString(array));
}
func1分析
打印的结果为[1,2,3,4,5,6],[1,2,3,4,5,6]
func2分析
打印结果为[1,2,3,4,5,6],[1,22,3,4,5,6]
注意:1.一个引用只能保存一个对象的地址
2.引用变量不一定在栈上,是由变量的性质决定的,如果是局部变量就保存在栈上;如实例成员变量就不一定在栈上
null
null 在 Java 中表示 "空引用" , 也就是一个无效的引用。null 的作用类似于 C 语言中的 NULL (空指针), 都是表示一个无效的内存位置. 因此不能对这个内存进行任何读写操作. 一旦尝试读写, 就会抛出 NullPointerException.
数组作为方法的返回值
方法实现数组元素交换
//交换数组元素
public static void exchange(int[] array){
int tmp = array[0];
array[0] = array[1];
array[1] = tmp;
}
public static void main(String[] args) {
int[] array = {1,2,3,4,5,6};
System.out.println("交换前:" + array[0] + "," + array[1]);
exchange(array);
System.out.println("交换后:" + array[0] + "," + array[1]);
}
数组作为方法的返回值
写一个方法以数组为形参,返回值为原数组
public static int[] Double2(int[] Array){
for (int i = 0;i <Array.length;i++){
Array[i] = 2 * Array[i];
}
return Array;
}
public static void main(String[] args) {
//int[] Array = {1,2,3,4,5,6,7};
int[] Array = new int[]{1,2,3,4,5,6};
Double2(Array);
System.out.print(Arrays.toString(Array));
}
分析
以方法为参数,返回值为新数组
对比下列代码和结果
可以得出,新开辟数组ret只将Array的值复制处理后存储,直接打印Array其值不变。方法结束,存储在栈上的变量会销毁,堆上的数组内容保存,地址传到主函数进行计算。
模拟实现Arrays.toString()
int[] array = {1,2,3,4,5,6};
System.out.print(Arrays.toString(array));
//打印结果为[1,2,3,4,5,6]
思路:1.[1,2,3,4,5,6]为"["和"]"将数字和逗号括起来。2.除了数组的最后一个元素外,其余的元素都是一个数字加",".
实现代码:
public static String my_toString(int[] array){
String str = "[";
for (int i = 0;i < array.length;i++){
if (i == array.length-1){
str += array[i];
}else {
str += array[i] + ",";
}
}
str += "]";
return str;
}
public static void main(String[] args) {
int[] array = {1,2,3,4,5,6};
System.out.print(myToString(array));
}
在数组中查找元素
遍历查找
public static int findNumber1(int[] array,int key){
if (array == null){
return -1;
}
if (array.length == 0){
return -1;
}
int ret = -1;
for (int i = 0;i < array.length;i++){
if(array[i] == key){
ret = i;
}
}
return ret;
}
public static void main(String[] args) {
int[] array = {1,2,3,4,5,6};
System.out.println("请输入要查找的数字");
Scanner scanner = new Scanner(System.in);
int key = scanner.nextInt();
System.out.println("找到了,坐标为");
System.out.print(findNumber1(array, key));
}
二分查找法(只适用与有序数组)
思路:
代码:
public static void main(String[] args) {
int[] array = {1,2,3,4,5,6};
System.out.println("请输入要查找的数字");
Scanner scanner = new Scanner(System.in);
int key = scanner.nextInt();
System.out.println("找到了,坐标为");
System.out.print(findNumber2(array, key));
}
public static int findNumber2(int[] array,int key){
int ret = -1;
if (array == null) return -1;
if (array.length == 0) return -1;
int left = 0;
int right = array.length-1;
while (left < right) {
int mid = (left + right) / 2;
if (array[mid] < key) {
left = mid + 1;
} else if (array[mid] > key) {
right = mid - 1;
} else {
return mid;
}
}
return -1;
}
可直接通过操作数组工具类Arrays直接实现二分查找
Arrays.binarySearch(int[] array,int key)
数组练习
判断数组是否有序
思路:
遍历数组,数组前一项和后一项大小比较。循环过程中,如遇到无序返回flase,结束循环。
代码:
public static void isOrderly(int[] array){
int flag = 1;
for (int i = 0;i < array.length-1;i++){
if (array[i] < array[i + 1]){
;
}else {
flag = -1;
break;
}
}
if (flag == 1){
System.out.println("该数组有序");
}else {
System.out.println("该数组无序");
}
}
public static void main(String[] args) {
int[] array = {1,2,3,4,5,6,7};
isOrderly(array);
}
注意:
遍历数组过程的for循环判断条件防止数组越界,如:for (int i = 0;i < array.length;i++)后
if (array[i] < array[i + 1]);此时为array[array.length - 1] < array[array.length],而array[arry.length]数组越界,正确做法应该将for(int i = 0;i < array.length - 1;i++).
冒泡排序
思路:
冒泡排序是数组数据从左到右比较来交换后确定顺序的方法。
代码:
public static void Bubble(int[] array){
//i表示趟数
for (int i = 0;i < array.length-1;i++){
boolean flag = false;
for (int j = 0; j < array.length - 1 - i;j++){
if (array[j] > array[j + 1]){
int tmp = array[j];
array[j] = array[j + 1];
array[j + 1] = tmp;
flag = true;
}
}
if (flag == false){
break;
}
}
}
public static void main(String[] args) {
int[] array = {11,2,5,66,7,1,0};
for (int x : array){
System.out.print(x + " ");
}
Bubble(array);
System.out.println("冒泡排序后为");
for (int x : array){
System.out.print(x + " ");
}
}
注意
1.for循环判断条件可以优化,判断趟数和次数可以优化。
2.通过操作数组工具类Arrays直接实现排序,Arrays.sort(array).
数组排序(奇数后,偶数前)
思路
定义左右下标,通过while循环逐渐向中移动,左下标遇奇数停下,右下标遇到偶数停下;停下后,两数组元素交换。
代码
public static void oddAfter(int[] array){
int left = 0;
int right = array.length-1;
while (left < right){
while (left < right && array[left] % 2 != 0){
left++;
}
while (left < right && array[right] % 2 == 0){
right--;
}
int tmp = array[left];
array[left] = array[right];
array[right] = tmp;
}
}
public static void main(String[] args) {
int[] array = {1,2,4,6,8,7,9,11,12};
System.out.print("排序前");
System.out.println(Arrays.toString(array));
oddAfter(array);
System.out.print("排序后");
System.out.println(Arrays.toString(array));
}
注意
左右下标移动的循环应该逻辑与(&&)上条件:左下标始终小于右下标
数组逆序
思路
定义左右下标,通过while循环逐渐向中移动,逐个移动交换。
代码
public static void Reverse(int[] array){
int left = 0;
int right = array.length-1;
while (left < right){
int tmp = array[left];
array[left] = array[right];
array[right] = tmp;
left++;
right--;
}
}
public static void main(String[] args) {
int[] array = {1,2,3,4,5,6,7};
System.out.print("逆序前");
System.out.println(Arrays.toString(array));
Reverse(array);
System.out.print("逆序后");
System.out.println(Arrays.toString(array));
}
数组拷贝
思路
将遍历源数组将数组元素逐个赋值到目标数组。
代码
public static int[] Copy(int[] array){
int[] ret = new int[array.length];
for (int i = 0;i < array.length;i++){
ret[i] = array[i];
}
return ret;
}
public static void main(String[] args) {
int[] array = {1,2,3,4,5,6,7};
System.out.println(Arrays.toString(array));
System.out.println(Arrays.toString(Copy(array)));
}
注意
1.可利用Arrays数组操作工具类实现
//利用Arrays数组工具类实现
int[] ret = Arrays.copyOf(array,array.length);//(源数组,需要拷贝的长度)
System.out.println(Arrays.toString(ret));
2.可利用Arrays数组操作工具类实现
//拷贝局部
int[] ret2 = Arrays.copyOfRange(array,2,7);//(源数组,起始坐标,结束坐标)
System.out.println(Arrays.toString(ret2));
3.利用array调用克隆产生副本
int[] array = {1,2,3,4,5,6,7};
int[] ret3 = array.clone();
System.out.println(Arrays.toString(ret3));
4.拷贝分为深拷贝和浅拷贝
深拷贝:拷贝完成之后,通过一个引用修改目标数组的值时,不会影响源数组的值
浅拷贝:拷贝完成之后,通过一个引用修改目标数组的值时,会影响源数组的值
二维数组
二位数组的定义
第一种定义
int[][] array1 = {{1,2,3},{4,5,6}};
//编译器会默认数组为两行三列,int[][]的括号中不能加数字,否则会报错
第二种定义
int[][] array2 = new int[][]{{1,2,3},{4,5,6}};
//同样在括号中也不能加数字,否则会报错
第三种定义
int[][] array3 = new int[2][3];
//定义一个两行三列的的二位数组
二维数组在内存的存储
二维数组是特殊的一维数组,在内存的存储方式为:
打印其长度:
二维数组的打印
第一种打印方式(遍历)
int[][] array1 = {{1,2,3},{4,5,6}};
for (int i = 0;i < array1.length;i++){
for (int j = 0;j < array1[i].length;j++){
System.out.print(array1[i][j] + " ");
}
System.out.println();
}
第二种打印方式(for-each打印)
//for-each语句打印
for (int[] ret : array1){
for (int x : ret){
System.out.print(x + " ");
}
System.out.println();
}
第三种打印方式(利用Arrays数组操作工具类)
//利用Arrays数组操作工具类打印
System.out.print(Arrays.deepToString(array1));
例子
1、代码:
int[][] array4 = {{1,2},{3,4,5}};
for (int i = 0;i < array4.length;i++){
for (int j = 0;j < array4[i].length;j++){
System.out.print(array4[i][j] + " ");
}
System.out.println();
}
代码运行结果:
1 2
3 4 5
注意:数组正常打印不会在补充元素.
2、代码:
//二维数组省略列打印会报错
int[][] array5 = new int[2][];
for (int i = 0;i < array5.length;i++){
for (int j = 0;j < array5[i].length;j++){
System.out.print(array5[i][j] + " ");
}
System.out.println();
}
代码运行结果:
会报错,以为此时二位数组的行为null,此时会报空引用错误:
注意:省略列不会报错,因此可以利用这一特性定义不规则二维数组
代码:
int[][] array6 = new int[2][];
array6[0] = new int[3];
array6[1] = new int[2];
此时定义是不规则二维数组,第一行长度为3,第二行长度为2,不赋初值打印为:
0 0 0
0 0