一、数组的概念
一组连续的存储空间,存储多个相同数据类型的值。
特点:
- 类型相同
- 长度固定
二、数组的声明与赋值
2.1 四种声明方式
1.先声明、再分配空间: 数据类型[] 数组名; 数组名 = new 数据类型[长度];
int [] array1;//先声明
array1 = new int[12];//再分配空间
2.声明并分配空间: 数据类型[] 数组名 = new 数据类型[长度];
int[] array2 = new int[12];
3.声明并赋值(繁): 数据类型[] 数组名 = new 数据类型[]{value1,value2,value3,…};
int[] array3 = new int[]{};
4.声明并赋值(简): 数据类型[] 数组名 = {value1,value2,value3,…}; //显示初始化,注意:不可换行。
int[] array3 = {5,2,0,1,3,1,4};
public class Shuzu {
public static void main(String[] args) {
int[] nums = new int[10];//默认值为0
nums[0] = 0;
nums[1] = 1;
nums[2] = 2;
System.out.println(nums[0]);
System.out.println(nums[1]);
System.out.println(nums[2]);
//-------------
int [] a;
a = new int[3];
//-------------
int [] b = new int[] {1,2,3,4,5,6};
//-------------
int [] c = {1,2,3,4,5,6};
}
}
2.2 数组的默认值:
数组默认值:
- 整数:0
- 小数:0.0
- 字符:\u0000
- 布尔:false
- 其他:null
三、数组的组成
数组的使用:
下标的范围:不可越界
四、数组的遍历
遍历:从头至尾,逐一对数组的每个元素进行访问。
两种遍历方式:for遍历和for增强。
数组名.length 可动态获得数组长度。
int [] bs = new int[] {11,22,33,44,55};
//-------------
int [] c = {1,2,3,4,5,6};
for (int i = 0; i < c.length; i++) {//for遍历
System.out.println(c[i]);
}
for (int some : bs) {//for增强
System.out.println(some);
}
给定一个整数数组,读入一个整数n,如果n在数组中存在,输出下标,不存在则输出-1。
public class Mean {
public static void main(String[] args) {
int[] array = {12,25,99,99,1,15,25};
int sum = 0;
double average = 0;
for (int i = 0; i < array.length; i++) {
sum+=array[i];
}
average = sum/array.length;
System.out.printf("%.2f",average);
}
}
控制台输入5个城市名称,用数组保存,并遍历输出。
// 控制台输入5个城市名称,用数组保存,并遍历输出
public void saveCity() {
String[] citys = new String[5];
Scanner scanner = new Scanner(System.in);
for(int i = 0; i < citys.length; i++) {
String input = scanner.next();
citys[i] = input;
}
for (String city : citys) {
System.out.print(city + " ");
}
}
五、 复制的方式:
- 循环将原数组中所有元素逐一赋值给新数组。
- System.arraycopy(原数组,原数组起始,新数组,新数组起始,长度);
- java.util.Arrays.copyOf(原数组, 新长度);//返回带有原值的新数组。
5.1 第一种方法:循环:
public void arrayCopy1() {
int[] array1 = {0,2,5};
int[] array2 = new int[array1.length];
for(int i = 0; i < array1.length; i++) {
array2[i] = array1[i];
}
for (int value : array2) {
System.out.print(value + " ");
}
}
5.2 第二种方法:clone
public class ArrayClone {
public static void main(String[] args) {
int [] array1 = {0,2,5};
int [] array2;
array2 = array1.clone();//使用clone
for (int value : array2) {
System.out.println(value+" ");
}
}
}
5.3 第三种方法:Arrays.copyOf()
import java.util.Arrays;
public class ApplyCopy3 {
public static void main(String[] args) {
int [] array1 = {5,2,0};
int [] array2;
//第一个参数:要复制的数组
//第二个参数:新数组的大小(如果新长度>原数组长度,则用0填充)
array2 = Arrays.copyOf(array1, 5);
for (int value:array2) {
System.out.print(value+" ");//结果:5 2 0 0 0
}
}
}
5.4 第四种方法:Arrays.copyOfRange()
public class ArrayCopy4 {
public static void main(String[] args) {
int [] oldArray = {5,2,0,13,14};
int [] newArray;
//复制(0,5)的数组元素到新数组中
//如果复制长度<原数组长度,则就截取部分
//如果复制长度>原数组长度,则用0填充
newArray = Arrays.copyOfRange(oldArray, 0, 5);
//截取的长度
for (int value : newArray ) {
System.out.println(value + " ");
}
}
}
5.5 第五种方法:System.arraycopy()
public void arrayCopy5() {
int[] oldArray = {5, 2, 0, 1, 3, 1, 4};
int[] newArray = new int[oldArray.length];
// 参数: (原数组 原数组起始下标 新数组 新数组起始下标 复制长度)
// 也就是从原数组的0位置开始复制,复制到新数组从0开始的位置
System.arraycopy(oldArray, 0, newArray, 0, oldArray.length);
for (int value : newArray) {
System.out.print(value + " ");
}
}
六、 数组的扩容:
1.创建数组时,必须显示指定长度,并在创建之后不可更改长度。
2.扩容的思路:
- 创建大于原数组长度的新数组。
- 将原数组中的元素依次复制到新数组中。
public static void main(String[] args) {
//数组的扩容
int [] numbs = new int [] {1,2,3,4,5};
//1.再创建新的数组,默认值0
int [] numbs1 = new int [numbs.length*2];
//2.复制旧数组中的元素到新数组中
for (int i = 0; i < numbs.length; i++) {
numbs1[i] = numbs[i];
}//数组位置变换
numbs = numbs1;
for (int i = 0; i < numbs1.length; i++) {
System.out.print(numbs1[i]+" ");
}
System.out.println();
for (int i:numbs1) {
System.out.print(i+" ");
}
}
七、数组的插入删除:
7.1 插入元素:
首先确定插入元素的位置。
如果在数组末尾,未超出最大角标则直接加入;否则需要后移元素后,加入。
//插入
int[] a = {1,2,4,7,5,0,0};//数组下标要足够长,不然插入以后造成下标越界即Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
Scanner input = new Scanner(System.in);
System.out.println("请输入一个新的元素:");
int n = input.nextInt();
System.out.println("请输入插入元素的位置:");
int pos = input.nextInt();
int size = 5;//设置数组有值的长度,便于size++
if (pos>=0&&pos<=size){
for (int j=size-1;j>=pos;j--){
a[j+1] = a[j];
}
a[pos]=n;
size++;
for (int i : a) {
System.out.println(i);
}
}else{
System.out.println("输入有误!");
}
7.2 删除元素:
如果是末尾元素,则直接删除,否则删除后需要前移元素。
//删除
int[] a = {1,2,4,7,5,0,0};
Scanner input = new Scanner(System.in);
int size = 5;
System.out.println("请输入删除的位置:");
int p =input.nextInt();
if (p>0&&p<size){
if (p==size-1){
a[p]=0;
}else {
for (int k = p;k<size;k++)
{
a[k] = a[k+1];
}
a[size]=0;
}
for (int i : a) {
System.out.println(i);
}
}else
{
System.out.println("输出有误");
}
地址的替换:
- 数组作为引用类型之一,其变量中存储的是数组的地址。
- 完成元素复制后,需将新数组地址,赋值给原变量进行替换。
八、数组类型的参数:
-
方法调用时,将nums中的地址赋值给oneArray,此时二者指向同一个数组。
-
传递参数时:基本类型传递的是变量中的值;
-
引用类型传递的是变量中的地址。
如果传递的是基本数据类型,则传递的是值.
如果传递的是引用数据类型,则传递的是引用.
public static void main(String[] args){
int[] nums = {111,222,333,444,555};
printArray(nums);
}
public static void printArray(int[] array) {
for (int value : array) {
System.out.print(value + " ");
}
}
8.1 因为数组是引用类型,传递的是引用
数组类型的返回值:
调用数组类型返回值的方法时,方法执行后,返回的是数组的地址。
- 当在其它地方修改数组时,两者的数据都会变
- 因为实际上修改的那个数组和此数组指向的内存空间是一个地方,所以修改一个,另一个也会变
public class TestArray4 {
public static void main(String[] args) {
int[] nums={18,22,9,16,27,35};
int n=100;
printArray(n, nums);
System.out.println("-----主方法中遍历nums-----");
for (int i = 0; i < nums.length; i++) {
System.out.println(nums[i]);
}
System.out.println("---------");
System.out.println(n);//在这里输出的话,数组也会加5,如果是int的话,则不会发生改变。
}
public static void printArray(int num,int[] arr){
System.out.println(num);
num=200;
System.out.println("-------遍历数组-------");
for (int i : arr) {
System.out.println(i);
}
System.out.println("每个元素加5");
for (int i = 0; i < arr.length; i++) {
arr[i]=arr[i]+5;//如果是int类型的数,这地方syso才会有元素加5,但是如果是数组的话,一个地方加了5,在堆中就会加5,所有每个地方的数组都加5,数组是引用类型。
}
}
}
8.2 可变长参数:
概念:可接收多个同类型实参,个数不限,使用方式与数组相同。
语法:数据类型… 形参名
- 必须定义在形参列表的最后
- 且只能有一个
用for循环来创建可变长参数。
可变长参数可以用来存储数组,或一段数。
public class LongArgs{
public static void main(String[] args){
int [] x = {1,2,3,4,5};
printArray(5,x);
}
public static void printArray(int n,int ...arr) {
for (int i : arr) {
System.out.println(i);
}
}
}
输出:
1
2
3
4
5
public class LongArgs2{
public static void main(String[] args){
int [] x = {1,2,3,4,5};
printArray(5,500,400,300,200,100);
}
public static void printArray(int n,int ...arr) {
for (int i : arr) {
System.out.println(i);
}
}
}
输出:
500
400
300
200
100
九、 数组的排序
9.1 冒泡排序:
相邻的两个数值比较大小,互换位置。
//冒泡算法:两个相邻的元素进行比较,小的向前,大的向后
//n个数字来排列,两两比较小靠前,外层循环n-1,内层循环n-1-i;
public class Maopao {
public static void main(String[] args) {
int[] nums= {20,8,19,15,23,80,13};
System.out.println("排序之前:");
for (int a : nums ) {
System.out.print(a+",");
}
System.out.println();
bubbleSort(nums);
System.out.println("排序之后:");
for (int b : nums) {
System.out.print(b+",");
}
}
public static void bubbleSort(int[] arr) {
for(int i =0; i<arr.length-1;i++) {
for (int j = 0; j < arr.length-i-1; j++) {
if (arr[j]>arr[j+1]) {
int t= arr[j];
arr[j] = arr[j+1];
arr[j+1]= t;
}
}
}
}
}
public static void bubbleSort(int[] sums) {
for (int i = 0; i < sums.length-1; i++) {
boolean flag = true; 设定一个标记,若为true,则表示此次循环没有进行交换,也就是待排序列已经有序,排序已经完成。
for (int j = 0;j < sums.length-i-1;j++){
if (sums[j]>sums[j+1]){
int t = sums[j];
sums[j] = sums[j+1];
sums[j+1] = t;
flag = false;
}
}
if (flag){
break;
}
}
for (int sum : sums) {
System.out.println(sum);
}
}
9.2 选择排序:
固定值与其他值依次比较大小,互换位置。
//选择排序:选择一个元素,让选择依次和后面的元素进行比较,小的向前,大的向后
//选择排序
public static void selectSort(int[] arr){
for(int i=0;i<arr.length-1;i++){//总共需要比较N-1轮。这是代表的轮数
for(int j=i+1;j<arr.length;j++){
if(arr[i]>arr[j]){
int t=arr[i];
arr[i]=arr[j];
arr[j]=t;
}
}
}
}
//优化选择排序
public static void SelectionSort (int [] sums){
//总共要比较N-1轮
for (int i = 0; i < sums.length-1; i++) {
int min = i;
//每轮需要比较的次数N-i
//下标进行比较
for (int j = i+1;j < sums.length ;j++){
if (sums[j] < sums[min]){
//记录目前能找到的最小值元素的下标
min = j;
}
}
//将找到的最小值和i位置所在的值进行交换
//如果i不等于最小值,将此时此时的最小值min与i交换,这样就能保证i为最小值。
if (i!=min){
int t = sums[i];
sums[i] = sums[min];
sums[min] = t;
}
}
for (int sum : sums) {
System.out.println(sum);
}
}
9.3 插入排序:
将一个记录插入到已经排好序的有序表中。
//插入排序:从第二元素开始,向前找到插入的位置,保证前面的元素都有顺序,因为第一个元素默认是有序的。
//插入排序
public static void insertSort(int[] arr){
for(int i=1;i<arr.length;i++){
for(int j=i-1;j>=0;j--){
if(arr[j]>arr[j+1]){
int t=arr[j];
arr[j]=arr[j+1];
arr[j+1]=t;
}
}
}
}
//优化插入排序
public static void insertSort2(int[] arr){
for(int i=1;i<arr.length;i++){
int t=arr[i];
int pos=i-1;
while(pos>=0&&arr[pos]>t){
arr[pos+1]=arr[pos];
pos--;
}
arr[pos+1]=t;
}
}
9.4 JDK排序:
java.util.Arrays.sort(数组名); //JDK提供(升序)。
本身提供的升序。
import java.util.Arrays;
/**
* 使用jdk中提供的排序方式
*
*/
public class TestArray11 {
public static void main(String[] args) {
int[] a = {4,2,7,3,6};
//对数组进行排序
Arrays.sort(a);
//升序输出
// for(int i=0; i<a.length; i++) {
// System.out.println(a[i]);
// }
//降序输出
for(int i=a.length-1; i>=0; i--) {
System.out.println(a[i]);
}
}
}
9.5 希尔排序:
Shell’s Sort,又称“缩小增量排序“,该算法由希尔1959年公布。 • 是直接插入排序算法的一种更高效的改进版本。
9.6 快速排序:
通过一趟排序将要排序的数据分割成独立的两部分;
其中一部分的所有数据都比另外一部分的所有数据都要小;
然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以 此达到整个数据变成有序序列
十、 查找
顺序查找:从头开始遍历所有元素,找出满足条件的数据。
二分查找:二分查找的前提是数据必须有序。
折半查找法也称为二分查找法,它充分利用了元素间的次序关系,采用分治策略,可在最坏的情况下用O(log n)完成搜索任务。它的基本思想是:(这里假设数组元素呈升序排列)将n个元素分成个数大致相同的两半,取a[n/2]与欲查找的x作比较,如果x=a[n/2]则找到x,算法终止;如果x<a[n/2],则我们只要在[数组]a的左半部继续搜索x;如果x>a[n/2],则我们只要在数组a的右半部继续搜索x。
//二分查找
public class TestArray7 {
public static void main(String[] args) {
int[] arr={10,38,65,97,76,13,27,49,78,34,12,64,1};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
int i = binarySearch(arr, 50);
System.out.println(i);
//Arrays.binarySearch(, )
}
public static int binarySearch(int[] arr,int key){
int low=0;
int upper=arr.length-1;
while(low<=upper){
int mid=(low+upper)>>>1;
if(key<arr[mid]){
upper=mid-1;
}else if(key>arr[mid]){
low=mid+1;
}else{
return mid;
}
}
return -1;
}
}
public static int binarySearch(Integer[] srcArray, int des) {
//定义初始最小、最大索引
int start = 0;
int end = srcArray.length - 1;
//确保不会出现重复查找,越界
while (start <= end) {
//计算出中间索引值
int middle = (end + start)>>>1 ;//防止溢出
if (des == srcArray[middle]) {
return middle;
//判断下限
} else if (des < srcArray[middle]) {
end = middle - 1;
//判断上限
} else {
start = middle + 1;
}
}
//若没有,则返回-1
return -1;
}
十一、 二维数组
一维数组中的一维数组;数组中的元素,还是数组
行数代表一位数组的个数,列数代表一位数组中的元素。
// 二维数组
public void ArrayTwoTest() {
// 创建二维数组
int[][] array = new int[5][5];
// 先声明,再分配空间
int[][] array1;
array1 = new int[3][3];
// 创建二维数组并赋值
int[][] array2 = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
// 访问二维数组
System.out.println(array[0][1]);
// 二维数组赋值
array[2][2] = 12;
array[1][3] = 25;
// 二维数组的遍历
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();
}
}
int [][] nums = {{1,2,3},{4,5},{6,7,8}};
System.out.println(nums[1][1]);
结果输出:5
十.数组的内存:
程序运行中有两块重要的存储空间。
栈:
- 存储基本类型数据以及引用类型的地址。
- 空间比较小,存储速度较快,jdk1.5之前256k,之后1M。
- 后进先出。
堆
- 存储引用类型的实际数据。
- 空间比较大,存储速度较慢。
数组是引用类型,实际存储在堆中,栈中存储数据的引用(地址)。
十二、 扩展:
11.1 native 关键字:
本地方法,没有实现的方法,有JVM提供的实现,有C、C++实现。
11.2 使用Arrays.toString(数组)可以直接输出数组中的值。
Scanner sc = new Scanner(System.in);
//动态创建数组
int[] arr = new int[sc.nextInt()];
for(int i = 0 ; i< arr.length ; i++){
arr[i] = sc.nextInt();
}
/********** Begin **********/
for(int i = 0;i<arr.length-1;i++){//执行n-1轮比较
for(int j = i;j<arr.length-1;j++){
if(arr[i]<arr[j+1]){
int temp =arr[i];
arr[i] = arr[j+1];
arr[j+1] = temp;
}
}
}
System.out.println(Arrays.toString(arr));
11.3 输出两位小数:
System.out.printf("%.2f", average);
11.4 数组的应用(杨辉三角)
public class Practice04 {
public static void main(String[] args) {
int[][] nums=new int[7][];
for(int i=0;i<nums.length;i++){
nums[i]=new int[i+1];
//元素赋值
for(int j=0;j<nums[i].length;j++){
if(j==0||i==j){
nums[i][j]=1;
}else{
nums[i][j]=nums[i-1][j]+nums[i-1][j-1];
}
System.out.print(nums[i][j]+" ");
}
System.out.println();
}
}
}