数组
数组:相同类型数据的集合,内存是连续的
1.创建数组
int[ ] array = {1,2,3,4};
静态初始化,创建array数组,存放1,2,3,4
int[ ] array = new int[ ] {1,2,3,4};
动态初始化,new 一个array数组,"[ ]"中长度不能给定
int[ ] array = new int[10];
动态创建array数组,长度为10,未存放数据,默认值为0
2.数组的访问
1.通过下标索引访问数组,例:array[i], i表示下标、位置
2.使用array.length获取数组长度,"."操作是成员访问操作符
3.下标访问操作不能超过有效范围[0,length - 1],若超出有效范围,会出现下标越界异常
遍历数组的方法:
1.使用for循环遍历数组
//通过访问下标来遍历数组,数组名为array
for(int i= 0;i<array.length;i++){
System.out.print(array[i]+" ");
}
2.使用for each 遍历数组
数组名为array
for(int i : array) {
System.out.print(i + " ");
}
数组遍历时for循环和for each 的区别:for循环中可以获取到数组的下标,而for each中不能。
3.以字符串的形式打印数组
使用Arrays.toString(array)
System.out.println(Arrays.toString(array));
Arrays:java当中数组操作工具类
3.数组作为方法的参数
3.1数组可作为函数的形参或者实参
- 参数传递内置类型时,修改形参的值,并不引起实参值的改变
- 参数传数组类型时,在函数内部修改数组内容,函数外部也发生改变,此时数组名是一个"引用",当传参时,按照引用传参。
什么是引用?
引用相当于一个“别名”,可以理解为一个指针
创建一个引用相当于创建了一个很小的变量,变量中保存了一个整数,整数表示内存中的一个地址。
所谓的“引用”本质上存了一个地址,java将数组设定为引用类型,后续进行数组参数传参时,只是将数组的地址传入到函数形参中,避免对整个数组的拷贝。
- 数组也可以作为返回值
3.2 JVM内存区域划分
JVM包括以下几个部分:
1.Java虚拟机栈
存储局部变量表,int[ ] array这样的存储地址的引用等。
2.本地方法栈
被native修饰的方法叫本地方法,其特点是底层由C/C++代码进行实现,Java只需调用即可,速度快。
3.程序计数器
存放下一条执行的指令地址
4.堆
new 在堆上开辟内存空间
5.方法区
用于存储已被虚拟机加载的类信息、常量、静态变量、方法编译出的字节码等
常量池:一般存放字符串常量,即双引号引起来的数据
- 在JDK1.7之前,常量池存放在方法区;在JDK1.7之后,存放在堆上
每一个线程都会包含:程序计数器,Java虚拟机栈,本地方法栈
4.数组的拷贝方式
4.1使用for循环拷贝数组
public class Test {
public static int[] copyArray(int[] array1,int[] array2 ) {
for(int i=0;i<array1.length;i++){
array2[i] = array1[i];
}
return array2;
}
public static void main(String[] args){
int[] array1 = {1,2,3,4,5};
int[] array2 = new int[array1.length];
int[] array=copyArray(array1,array2); System.out.println(Arrays.toString(array));
}
}
4.2使用 Arrays.copyOf( );
这种方法会返回新的对象,底层调用的是"System.arraycopy( )"
3.使用 System.arraycopy( );
这个方法有5个参数:源数组,源数组开始位置,目标数组,目标数组开始位置,拷贝长度
- 这是一个native 方法,底层由C/C++实现,速度较快
4.3使用 clone( );
定义方法:数组名.clone( );
会返回新的对象
- 以上4种数组拷贝方式:对于内置/简单类型来说,均是深拷贝;对于引用类型,均为浅拷贝
- 浅拷贝:当拷贝完成之后,复制了对象的引用地址,两个对象指向同一个内存地址,所以修改其中任意的值,另一个值都会随之变化
- 深拷贝:对象及值复制过来,两个对象修改其中任意的值另一个值不会改变
5.数组的使用
5.1找数组的最大值
public class Test {
public static int maxNum(int[] array) {
int a= array[0];
for(int i =1;i < array.length;i++){
if(a < array[i]) {
a = array[i];
}
}
return a;
}
public static void main(String[] args) {
int[] array1 = {1,2,3,4,5};
System.out.println (maxNum(array1));
}
}
5.2找数组的最小值
public class Test {
public static int minNum(int[] array) {
int a= array[0];
for(int i =1;i < array.length;i++){
if(a > array[i]) {
a = array[i];
}
}
return a;
}
public static void main(String[] args) {
int[] array1 = {1,2,3,4,5};
System.out.println (minNum(array1));
}
}
5.3求数组的平均数
public class Test {
public static double avg(int[] array){
int sum=0;
for(int i=0;i<array.length;i++){
sum += array[i];
}
return (double)sum/array.length;
}
public static void main(String[] args) {
int[] array1 ={1,2,3,4,5};
System.out.println(avg(array1));
}
}
5.4数组的逆置
public class Test {
public static void reverse(int[] array) {
int i =0;
int j =array.length-1;
while(i<j){
int tmp=array[i];
array[i]=array[j];
array[j]=tmp;
i++;
j--;
}
}
public static void main(String[] args) {
int[] array1={1,2,3,4,5};
reverse(array1);
System.out.println(Arrays.toString(array1));
}
}
5.5将偶数放到奇数前面
public class Test {
public static void func(int[] array) {
int i = 0;
int j = array.length-1;
while (i != j) {
while (i < j && array[i] % 2 == 0) {
i++;
}//在前面碰到奇数 停止
while (i < j && array[j] % 2 != 0) {
j--;
}//在后面碰到偶数 停止
//交换的前提:
if(i != j) {
int tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
}
}
public static void main(String[] args) {
int[] array1={1,2,3,4,5};
func(array1);
System.out.println(Arrays.toString(array1));
}
}
5.6冒泡排序
给定一个数组,让其升序或降序排列
若按升序排列:每次对相邻的两个元素进行比较,若前者大于后者则进行交换,如此一趟下来最后一趟的就是最大元素,重复以上的步骤,除了已经确定的元素。
5.6.1.若使用for循环实现
分析可知
数组中有n个元素;需要进行n-1趟冒泡排序;
第一趟需要比较n-1次
第二趟需要比较n-2次
…
外层for循环控制需要冒泡排序的趟数;
内层for循环控制每次需要进行比较的次数;
public class Test {
public static void bubbleSort(int[] array) {
for(int i=0;i<array.length-1;i++){
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;
}
}
}
}
public static void main(String[] args) {
int[] array1={5,4,2,3,1};
bubbleSort(array1);
System.out.print(Arrays.toString(array1));
}
}
但是这个代码不高效,若给定的数组已按顺序排列,则代码还是会继续执行,冒泡排序的次数和每次比较的趟数还是不会少。
5.6.2.设置标志,判断是否发生交换
为提高效率,我们可以判断每一趟比较完成后,是否有元素发生交换,没有发生交换则说明序列有序,不再进行比较!
public class Test {
public static void bubbleSort(int[] array) {
boolean flg = false;
//趟数
for (int i = 0; i < array.length-1; i++) {
flg = 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;
flg = true;
}
}
if(!flg) {
break;
}
}
public static void main(String[] args) {
int[] array1={5,4,2,3,1};
bubbleSort(array1);
System.out.print(Arrays.toString(array1));
}
}
5.6.3.使用Array.sort(数组名);
该方法的底层是一个快速排序,将数组元素从小到大排列。
使用时直接调用即可
public class Test {
public static void main(String[] args) {
int[] array ={1,4,3,2,5};
Arrays.sort(array);
System.out.println(Arrays.toString(array));
}
}
5.7 使用二分查找来查找指定元素
二分查找(Binary Search)也叫作折半查找。
二分查找有两个要求:一个是数列有序,另一个是数列使用顺序存储结构(比如数组)。
5.7.1 普通二分查找
以升序数列为例:
首先比较数列中间位置元素与指定元素的大小 ;
若中间位置的元素小于指定元素,则继续在后半部分的数列中进行二分查找;若中间位置的元素大于指定元素,则在数列的前半部分进行比较;
如果相等,则找到了元素的位置。
每次比较的数列长度都会是之前数列的一半,直到找到相等元素的位置或者没有找到指定元素。
public class Test {
public static int binarySearch(int[] array,int key) {
int left = 0;
int right = array.length - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (array[mid] == key) {
return mid;
} else if (array[mid] < key) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}
public static void main(String[] args) {
int[] array1={1,2,3,4,5};
System.out.println(binarySearch(array1,5));
}
}
该代码的执行结果返回的是指定元素的位置,即数组下标
5.7.1 使用Arrays.binarySearch();
该方法中有4个参数:数组名,起始下标,终止下标,指定元素
该方法的返回的是指定元素的位置,下标
- 查找的范围: [起始下标,终止下标)
包括起始下标,但不包括终止下标。
public class Test {
public static void main(String[] args) {
int[] array1={1,2,3,4,5};
System.out.println(Arrays.binarySearch(array1,3,6,5));
}
}
5.7.2使用递归的方法实现二分查找
public class Test {
public static int binarySearch(int[] array,int left,int right,int key) {
if (left > right) {
return -1;
}
int mid = (left + right) / 2;
if (array[mid] == key) {
return mid;
} else if (array[mid] > key) {
right = mid - 1;
return binarySearch(array, left, right, key);
} else {
left = mid + 1;
return binarySearch(array, left, right, key);
}
}
public static void main(String[] args) {
int[] array1={1,2,3,4,5};
System.out.println(binarySearch(array1,0,4,6));
}
}
与之前的方法相同,该方法的返回的是指定元素的位置,下标
6. 二维数组
6.1 定义方式
规则数组定义方式:
- int[ ][ ] array={{1,2,3},{4,5,6},{7,8,9}};
- int[ ][ ] array = new int[ ][ ]{{1,2,3}{4,5,6}};
- int[ ][ ] array = new int[3][4];
不规则数组定义方式: - int[ ][ ] array={{1,2},{4,5,6},{7,8,9}};;
- int[ ][ ] array = new int[3][ ];
array[0] = new int[2];
array[1] = new int[3];
array[2] = new int[4];
6.2访问二维数组
array.length 二维数组的行
array[i].length 二维数组的列
通过使用双层for循环来遍历数组:
public class Test {
public static void main(String[] args){
int[][] array={{1,2,3},{4,5,6},{7,8,9}};
for(int i=0;i<array.length;i++){
for(int j=0;j<array[i].length;j++){
System.out.print(array[i][j]+" ");
}
}
}
}
6.2二维数组的内存模型
以 int[ ][ ] array={{1,2,3},{4,5,6},{7,8,9}}; 为例说明二维数组的内存模型:
数组名array,“0,1,2”代表该二维数组有3行,行下标下对应每行数组存储的起始位置。