目录
5. 数组的练习: Arrays -> 操作 数组相关的 工具类
5.1 数组转字符串:Arrays.toString(数组名)
5.2 数组拷贝: Arrays.copyOf() 、 Arrays.copyOfRange() 需要拷贝的数组.clone() 、 arraycopy
5.2.1 Arrays.copyOf(original,new length)
5.2.2 Arrays.copyOfRange(original,from,to)
5.2.4 arraycopy(要拷贝哪个数组, 从哪个位置开始拷, 往哪个数组里拷, 拷贝到数组的哪个位置, 拷多长)
5.3.2 Arrays.binarySearch(要查找的数组,要查找的元素)
5.5 比较两个数组是否相等:Arrays.equals(数组名1,数组名2)
5.6.1 全部初始化为同一个值:Arrays.fill(需要初始化的数组,初始化成的值)
5.6.2 部分初始化为同一个值:Arrays.fill(要初始化的数组,from,to,初始化成的值)
1. 数组的定义
数组是一块连续的存储空间,存储的是一组相同类型的元素。
声明一个数组变量,就在栈上分配了一块内存,用new创建对象,在堆上分配内存了,就将变量初始化成了真正的数组。
下面,分配内存指的是 堆上是否分配内存 ;有没有初始化指的都是数组对象有没有初始化。
在Java当中,如果定义了一个数组,在堆上分配了内存,但对象没有进行初始化。那么数组元素会有一个默认值。
2. 数组的使用
2.1 访问数组
2.2 遍历数组
public class TestDemo {
public static void main(String[] args) {
//遍历数组
int[] array = {1,2,3,4};
//(!!!)方法一:for循环
for (int i = 0; i < array.length; i++) {
System.out.print(array[i]+" ");
}
System.out.println();
//(!!!)方法二:foreach也叫作增强for循环 (数组中每个元素的类型定义的一个变量:数组名)
//可以这样理解:遍历array,每拿到一个元素,就存到x中,然后再将x打印出来
for (int x:array) {
System.out.print(x+" ");
}
System.out.println();
//方法一和方法二的区别:foreach是拿不到数组下标的,for循环可以拿到数组下标
//(!!!)方法三:Arrays.toString(数组名)
/* Java当中有一个工具,可以专门用来操作数组,这个工具叫做Arrays。
(用它需要导入包:import java.util.Arrays;)*/
/*toString:返回指定数组的内容的字符串形式
* 把数组转变为字符串形式,然后返回*/
String ret = Arrays.toString(array);
System.out.println(ret);
}
}
2.3 练习:改变原有数组元素的值
实现一个方法 transform, 以数组为参数, 循环将数组中的每个元素 乘以 2 , 并设置到对应的数组元素上. 例如 原数组为 {1, 2, 3}, 修改之后为 {2, 4, 6}
public class TestDemo2 {
public static void transform(int[] array){
for (int i = 0; i < array.length; i++) {
array[i] *= 2;
}
}
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int[] array = new int[n];
//给数组赋值不能使用foreach,目前知识储备做不到
//foreach一般用于遍历数组,输出数组元素
/* for (int x:array) {
x = scan.nextInt();
}*/
//用for循环来做
for (int i = 0; i < n; i++) {
array[i] = scan.nextInt();
}
transform(array);
for (int x:array) {
System.out.print(x+" ");
}
}
}
总结: 遍历输出数组内容可以考虑for循环和foreach,但是给数组初始化要用for循环,因为foreach拿不到数组的下标,且目前我的知识储备做不到。
3. 数组是引用类型
3.1 内存分布
3.1.1 初始JVM的内存分布
JVM对所使用的内存按照功能的不同分为:程序计数器,虚拟机栈,本地方法栈,堆和方法区。
先简单了解一下虚拟机栈和堆。
虚拟机栈:与方法调用相关的一些信息,比如局部变量等。每个方法在执行时,都会先创建一个栈帧。当方法运行结束后,栈帧就被销毁了,即栈帧中保存的数据也被销毁了。
堆:使用 new 创建的对象都是在堆上保存 (例如前面的 new int[]{1, 2, 3} )。堆是随着程序开始运行时而创建,随着程序的退出而销毁,堆中的数据只要还有在使用,就不会被销 毁。
方法区:常量,静态变量等。
3.1.2 与C语言的内存分布简单做个对比
C语言也有5大内存分区:栈区,堆区,全局/静态存储区,文字常量区,程序代码区
栈区:局部变量 函数的形参 每一次函数调用都会在栈区上申请空间
堆区:动态内存管理,malloc,free,calloc,realloc
全局/静态存储区:静态变量,全局变量
文字常量区:常量
3.1.3 在Java和C中数组存储位置有什么区别吗?
- Java中,数组就是对象,是在堆上保存的。对象会有一个地址,地址存在引用变量中,引用变量是一个局部变量,存储在栈上。
- C语言中,数组在{}内创建,此变量就是局部变量,此时数组就在栈区上;在{}外创建,此变量就是全局变量,此时数组在静态区上。
3.2 基本类型变量与引用类型变量的区别
基本数据类型创建的变量,称为基本变量,该变量空间中直接存放的是其所对应的值;
引用数据类型创建的变量,一般称为对象的引用,其空间中存储的是对象所在空间的地址。
数组是对象,对象存储在堆上,会存在一个地址。
array是引用,存储在栈上,里面放的是对象的地址。
通过地址,就能找到对象,进行打印或修改。
若在main函数里,定义一个数组,main函数结束后,引用变量就会被内存回收,但它指向的对象不一定会被内存回收。对象的回收,是没人引用它。
图解:
3.3 通过一些题进一步理解引用
题目一:
import java.util.Arrays;
public class TestDemo {
public static void main(String[] args) {
int[] array = {1,2,3,4};
System.out.println(Arrays.toString(array));
int[] array2 = array;
array2[1] = 99;
System.out.println(Arrays.toString(array));
System.out.println(Arrays.toString(array2));
}
}
题目二:
public class TestDemo {
public static void main(String[] args) {
int[] array = {1,2,3,4};
int[] array2 = {4,5,6,7};
array = array2;
System.out.println(Arrays.toString(array));
System.out.println(Arrays.toString(array2));
}
}
题目三:
4. 数组的应用(引用/数组名的应用)
传的都是数组名,数组名中存的地址,是对象在堆上的地址,通过数组名就可以找到对象了。
4.1 保存数据
可以用for循环,foreach,Arrays.toString()输出数组内容
4.2 作为函数的参数
在Java中,拿不到栈上的地址,只能拿到堆上的地址,就是这么规定的。
4.2.1
public class TestDemo {
public static void func1(int[] array1){
array1 = new int[10];
}
public static void func2(int[] array2){
array2[0] = 99;
}
public static void main(String[] args) {
int[] array = {1,2,3,4};
func1(array);
System.out.println(Arrays.toString(array));
func2(array);
System.out.println(Arrays.toString(array));
}
}
4.2.2 交换两个值:
4.3 作为函数的返回值
5. 数组的练习: Arrays -> 操作 数组相关的 工具类
5.1 数组转字符串:Arrays.toString(数组名)
Arrays.toString(数组名) :把数组转变为字符串形式,然后返回。
有返回值,需要变量接收。
下面模拟实现一个toString:
public class TestDemo {
/*自己实现一个toString - myToString
* 将数组转变为字符串形式,然后输出
*/
public static String myToString(int[] array){
if(array == null){//先判断array是不是为null!!!
return "null";
}
String ret = "[";
for (int i = 0; i < array.length; i++) {
ret += array[i];
if(i< array.length-1){
ret += ",";
}
}
ret += "]";
return ret;
}
public static void main(String[] args) {
int[] array =null;
String ret = myToString(array);
System.out.println(ret);
int[] array2 ={1,2,3,4};
String ret2 = myToString(array2);
System.out.println(ret2);
}
}
5.2 数组拷贝: Arrays.copyOf() 、 Arrays.copyOfRange() 需要拷贝的数组.clone() 、 arraycopy
5.2.1 Arrays.copyOf(original,new length)
因为new length新的长度是int类型的,所以只能是整数倍,不能是小数倍。
有返回值,需要变量接收。
看源码:鼠标定位到+ctrl+鼠标左键 或 鼠标定位到ctrl+b
看方法索引:alt+7
5.2.2 Arrays.copyOfRange(original,from,to)
但它是左闭右开的,最后那个数不在拷贝范围内。此方法支持局部拷贝。
有返回值,需要变量接收。
5.2.3 需要拷贝的数组 . clone()
相当于产生一个副本
有返回值,需要变量接收。
5.2.4 arraycopy(要拷贝哪个数组, 从哪个位置开始拷, 往哪个数组里拷, 拷贝到数组的哪个位置, 拷多长)
arraycopy使用特别自由。但是使用前别忘记先定义一个数组来放拷贝后的数据。
没有返回值,不需要变量接收。
5.2.5 自己实现一个数组拷贝
public class TestDemo {
public static void main(String[] args) {
int[] array = {1,3,5,7,9};
int[] array2 = new int[array.length];//Java中 new int[],[]中允许写变量
for (int i = 0; i < array.length; i++) {
array2[i] = array[i];
}
System.out.println(Arrays.toString(array));
System.out.println(Arrays.toString(array2));
}
}
5.3 查找数组中指定元素:顺序查找、二分查找
5.3.1按顺序查找
使用for循环遍历数组,找到返回下标,找不到返回-1
5.3.2 Arrays.binarySearch(要查找的数组,要查找的元素)
二分查找法:只针对有序数组
有返回值,需要变量接收。默认找到返回下标,找不到返回负数
5.3.3 自己实现一个二分查找
二分查找法:只针对有序数组
public class TestDemo {
public static int binarySearch(int[] array,int k){
//二分查找
int left = 0;
int right = array.length-1;
while(left<=right){
int mid = left+((right - left)>>1);
if(k<array[mid]){
right = mid - 1;
}else if(k>array[mid]){
left = mid +1;
}else{
return mid;
}
}
return -1;
}
public static void main(String[] args) {
int[] array = {1,3,5,7,9,13};
Scanner scan = new Scanner(System.in);
int k = scan.nextInt();
int ret = binarySearch(array,k);
if(ret == -1){
System.out.println("找不到");
}else{
System.out.println("找到了,下标为:"+ret);
}
}
}
5.4 数组排序
5.4.1 Arrays.sort
Arrays.sort(数组名)
默认排成升序
没有返回值,不需要变量接收。
Arrays.sort(数组名,实现了Comparator<T>接口的对象)
5.4.2 冒泡排序
public class TestDemo {
//数组排序
//冒泡排序
public static void bubbleSort(int[] array){
//趟数
for (int i = 0; i < array.length-1; i++) {
boolean flag = true;
//交换
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 = false;
}
}
if(flag == true) {
return;
}
}
}
public static void main(String[] args) {
int[] array = {9,8,7,6,5,4,3,2,1,0};
bubbleSort(array);
System.out.println(Arrays.toString(array));
}
}
5.5 比较两个数组是否相等:Arrays.equals(数组名1,数组名2)
有返回值,需要变量接收。默认相等返回true,不相等返回false
5.6 给数组初始化:Arrays.fill()
没有返回值,不需要变量接收。
5.6.1 全部初始化为同一个值:Arrays.fill(需要初始化的数组,初始化成的值)
5.6.2 部分初始化为同一个值:Arrays.fill(要初始化的数组,from,to,初始化成的值)
左闭右开
5.7 数组逆序
public class TestDemo {
public static void reserve(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, 5, 6, 9, 17, 26, 47, 88, 94};
reserve(array);
System.out.println(Arrays.toString(array));
}
}
6. 二维数组
6.1 二维数组的定义
和一维数组的定义大差不差
6.2 数组的使用
6.2.1 二维数组是特殊的一维数组
6.2.2 一维数组和二维数组在内存中存储的对比图
6.2.3 遍历二维数组
//遍历二维数组
public static void main(String[] args) {
int[][] array = {{1,2,3},{4,5,6}};
//1、for循环
for (int i = 0; i < array.length; i++) {
for (int j = 0; j <array[i].length ; j++) {
System.out.print(array[i][j]+" ");
}
System.out.println();
}
System.out.println();
//2、foreach 增强for循环
/*(数组中每个元素的类型定义的一个变量:数组名)
* 一维数组中的每个元素也是一个一维数组*/
for (int[] arr:array) {
for (int x:arr) {
System.out.print(x +" ");
}
System.out.println();
}
System.out.println();
//3、Arrays.deepToString(数组名)
System.out.println(Arrays.deepToString(array));
}
6.3 不规则的二维数组
列可以省略,后面可以指定它的列(即每个一维数组的长度)
最后(提醒自己):
一定要将Java和C分隔开,学Java时暂时忘记C,这是两种不同的语言,没必要追究为什么语法不同,因为本来设计就不同。
就像同一个汉字 “我”,在不同地区方言念法不同,这是两种不同的方言,你非要追究为什么在我这“我”是这样念的,在你那就不一样了呢。没有可比性,这本来就是两种不同的方言,它们的共性只有一点,都是语言。
Java和C也是这样,它们是两种不同的计算机语言,本来就不一样。