数组的定义
- 数组的定义
数组就是用来存储同一类型若干数据的容器。它其实就是内存中一块连续的存储空间 - 语法
数据类型 [] 数组名 = new 数据类型 [长度]; //一般这么使用
数据类型 数组名 [] = new 数据类型 [长度];
数据类型 [] 数组名 = new 数据类型 [] {xx,aa,dd,…}
注意: 数组名实际就是变量名,数组是一种引用类型:数据类型 [] 。new 运算符在堆中分配一组连续的存储空间,可以存储 长度 个数据类型的元素. 把这块存储空间的引用(起始地址)赋值给数组名, 数组名存储的是堆中数组的起始地址 - 数组初始化
- 先定义数组,再给数组的元素赋值,这叫数组的动态初始化
- 在定义数组的同时就给数组元素赋值,这叫数组的静态初始化
int [] data3 = new int [] {4,5,6,7}; 还可以简化为int [] data3 = {4,5,6,7};
注意:- 静态初始化时,不需要指定数组的长度, 数组长度由数组初始化元素的个数决定
- 简化形式仅用于数组的静态初始化,不能用于数组的重新赋值
- 定义了数组之后 ,系统会给数组的元素默认初始化
数值类型数组,所有元素默认初始化为0
字符类型数组,所有元素默认初始化为码值为0的字符,’\u0000’
布尔类型数组,所有元素默认初始化为false
引用类型数组,所有元素默认初始化为null
数组元素的访问
通过数组的索引值(下标)访问数组的元素
数组的索引值是从0开始的, 即数组的最大的元素下标是: 数组的长度-1
访问数组元素:数组名[索引值] = 值;
为什么通过索引值可以访问到数组的元素?
可以通过数组的索引值(下标)计算出每个数组的元素的地址(偏移量).
数组作为参数与返回值类型
public class Test04ArrayArgs{
public static void main(String[] args) {
//调用getArray()方法,该方法返回int[]类型的数组, 可以定义int[]类型的 数组保存方法的返回值
int [] data = getArray();
//打印data数组中的元素
printArray( data );
};
//2) 定义方法,打印整数数组的元素, 需要通过参数来接收一个数组
public static void printArray(int[] data) {
//把data参数接收的数组元素打印到屏幕上
for (int x : data) {
System.out.print( x + " ");
}
System.out.println();
}
//1)定义方法, 返回一个长度为10的整数数组,该数组进行随机初始化[0,100)范围内的整数
public static int [] getArray(){
int [] data = new int[10];
//给data数组元素进行初始化
for (int i = 0; i < data.length; i++) {
double xx = Math.random(); //Math类的静态方法random()返回[0,1)范围内的随机小数
data[i] = (int)(xx*100);
}
//返回data数组
return data; //data是int []类型, 方法返回值类型应该是int[]
}
}
变长参数
特点:
- 变长参数可以接收任意个数据,在 方法体中,可以把 变长参数 当作数组使用
- 在定义方法时, 在方法参数列表中最后一个参数可以定义为变长参数
- 一个方法最多只能有一个变长参数
- 变长参数在参数类型与参数名中间使用三小点 表示
public class Test05args {
public static void main(String[] args) {
//在调用方法时,可以传递任意个数据
sum();
sum(1);
sum(1,2,3,4,5);
//在调用方法时,也可以传递数组
int [] arr = {6,6,6,6,6};
sum(arr);
}
//定义方法,可以计算任意个整数的和, 需要通过参数来接收任意个整数, 就可以使用变长参数
public static void sum( int ... data){
int sum = 0 ; //保存累加和
//在 方法体中,可以把 变长参数 当作数组使用
for (int i = 0; i < data.length; i++) {
sum += data[i];
}
System.out.println("sum=" + sum);
}
}
数组扩容
定义了数组之后 ,数组存储元素的个数就确定了, 如果想要存储更多的数据, 就需要对数组进行扩容.
原理:定义一个更大的数组, 把原来数组中的元素复制到新数组中
数组扩容使用到的两个方法:
- Arrays.copyOf( 源数组, 新数组的长度) //这个方法的实现也是依靠System.arraycopy这个方法
- System.arraycopy(源数组, 源数组起始位置, 目标数组, 目标数组的起始位置, 复制元素的个数);
public class Test06ArrayCopy {
public static void main(String[] args) {
//完全手工扩容
//test01();
//System.arraycopy()数组复制
//test02();
//直接调用Arrays.copyeOf()
test03();
}
//方式一:手动扩容2倍大小
private static void test01() {
//1)定义数组
int [] data = {1,2,3,4,5};
//定义更大的数组
int [] anotherArray = new int[data.length * 2 ]; //新数组的长度是原来数组长度的两倍
//把原来数组中的内容复制到新数组中
for(int i = 0 ; i<data.length; i++){
anotherArray[i] = data[i];
}
//让原来的数组名指向新的数组
data = anotherArray;
//打印data数组
System.out.println(Arrays.toString(data));
}
//方式二:使用System.arraycopy
private static void test02() {
//1)定义数组
int [] data = {1,2,3,4,5};
//定义更大的数组, 新数组的长度是原来数组长度的1.5倍
int [] anotherArray = new int[data.length + data.length / 2 ];
//把原来数组中的内容复制到新数组中,
// System.arraycopy(源数组, 源数组起始位置, 目标数组, 目标数组的起始位置, 复制元素的个数);
System.arraycopy(data, 0, anotherArray, 0, data.length);
/*
System类的arraycopy方法使用native修饰, 只有方法的声明没有方法体,该方法的方法体可能是用C++实现的
Java的JNI(Java native Interface)技术,可以使得在java程序中可以调用其他语言编写的代码
*/
//让原来的数组名指向新的数组
data = anotherArray;
//打印data数组
System.out.println(Arrays.toString(data));
}
//方式三:使用Arrays.copyOf
private static void test03() {
//1)定义数组
int [] data = {1,2,3,4,5};
//Arrays.copyOf( 源数组, 新数组的长度)
data = Arrays.copyOf(data, data.length * 2 );
System.out.println(Arrays.toString(data));
}
}
数组特点
- 优点:
访问快. 通过索引值计算出每个元素的地址 - 缺点:
插入/删除慢. 在插入/删除时,可能需要扩容,复制/移动大量的元素
适用于以访问为主,较少添加/删除的场景.
数组插入元素
public class Test07ArrayInsert {
public static void main(String[] args) {
int [] data = {11,22,33,44,55,66};
//调用insert方法, 向数组中插入元素, 再让原来的data数组名指向insert方法返回的新数组
data = insert(data, 2, 666);
System.out.println(Arrays.toString(data));
}
//定义方法,向数组的指定位置插入元素,需要通过参数来接收一个数组, 接收一个位置, 接收一个要插入的元素
public static int[] insert(int [] array, int i , int key){
//向array数组中的i位置插入元素key
//1)定义一个更大的数组
int [] anotherArray = new int[array.length + 1 ];
//2)把原数组[0,i)范围的元素复制到新数组[0,i)范围中
System.arraycopy(array, 0, anotherArray, 0, i );
//3)在新数组i位置插入元素key
anotherArray[i] = key;
//4)把原数组[i,length)范围的元素复制到 新数组[i+1, lenght+1)
System.arraycopy(array, i, anotherArray, i+1, array.length - i);
//5)让原来的即数组名指向新的数组, 对形参array重新赋值,对实参没有关系
// array = anotherArray;
// System.out.println("in method: " + Arrays.toString(array));
//5)需要返回新的数组
return anotherArray;
}
}
示意图
数组的相关算法
冒泡排序算法
代码实现:
public class Test01 {
public static void main(String[] args) {
int [] data = {55,33,66, 44,22,11};
System.out.println(Arrays.toString(data));
System.out.println("=======================================");
//由小到大
//从前向后两两比较,如果前面的数比后面的大,就交换, 把大的数交换到后面
//数组 有 n 个数, 比较 n-1 轮
for(int x = 0 ; x < data.length -1 ; x++){ // 比较的轮次
for(int i = 0 ; i < data.length-1- x ; i++){
if ( data[i] > data[i+1]){
int t = data[i];
data[i] = data[i+1];
data[i+1] = t;
}
System.out.println(Arrays.toString(data));
}
System.out.println("--------------------------------------");
}
}
}
选择排序算法
代码实现
public class Test02 {
public static void main(String[] args) {
int[] data = {55, 33, 66, 44, 22, 11};
System.out.println(Arrays.toString(data));
System.out.println("=======================================");
//在当前数中选择最小的交换到前面
for( int x = 0 ; x < data.length-1; x++){
int min = x; //保存当前数最小元素的下标
for(int i = min + 1 ; i< data.length; i++){
if ( data[i] <data[min]){
min = i;
}
}
//把min标识的元素与x位置的元素交换
if (min != x ){
int t = data[min];
data[min] = data[x];
data[x] = t;
}
System.out.println( Arrays.toString(data));
}
}
}
二分查找算法
代码实现
public class Test03 {
public static void main(String[] args) {
int [] data = {11,22,33,44,55,66,77};
System.out.println( binarySearch(data, 11));
System.out.println( binarySearch(data, 77));
System.out.println( binarySearch(data, 44));
System.out.println( binarySearch(data, 50));
}
//定义方法,二分查找,返回数组中指定元素的索引值
public static int binarySearch(int [] array, int key ){
int from = 0 ;
int to = array.length - 1;
int mid = (from+to)/2;
while ( from <= to ){
if ( array[mid] == key ){
return mid;
}else if (array[mid] > key){ //在左一半
to = mid-1;
mid = (from+to)/2;
}else { //右一半
from = mid + 1 ;
mid = (from+to)/2;
}
}
return -1;
}
}
Arrays工具类
java.util.Arrays类,提供了一组操作数组的方法
返回值类型 | 方法名 | 用途 |
---|---|---|
static List | asList(T… a) | 把数组转换成list列表 |
static int | binarySearch(int [] a,int key) | 二分查找 |
static boolean [] | copyOf(boolean [] original ,int newlength) | 数组复制 |
static int [] | copyOfRange(int [] original , int from , int to) | |
static String | deepToString(Object [] a) | 打印二维数组 |
static void | fill(int[] a, int fromIndex, int toIndex, int val) | |
static void | parallelSort(int[] a) | 并行排序,社和数据元素非常多的情况 |
static void | sort(int[] a) | 排序 |
static String | toString(int[] a) | 将数组元素转换成字符串 |
public class Test04 {
public static void main(String[] args) {
//1)定义数组
int [] data = {55,33,11 ,77,22 };
//2)对数组排序
Arrays.sort(data);
//3)打印数组元素
System.out.println( Arrays.toString(data)); //[11, 22, 33, 55, 77]
//4)数组扩容
data = Arrays.copyOf(data, data.length * 2 );
System.out.println( Arrays.toString(data)); //[11, 22, 33, 55, 77, 0, 0, 0, 0, 0]
//5)填充 把数组中 [ 4,8 ) 范围的元素填充为66
Arrays.fill(data, 4, 8, 66);
System.out.println( Arrays.toString(data)); //[11, 22, 33, 55, 66, 66, 66, 66, 0, 0]
//6)数组容量缩减
data = Arrays.copyOf(data, data.length - 3);
System.out.println( Arrays.toString(data)); //[11, 22, 33, 55, 66, 66, 66]
//7)对排序数组进行二分查找
System.out.println( Arrays.binarySearch(data, 11)); //0
System.out.println( Arrays.binarySearch(data, 66)); //5
System.out.println( Arrays.binarySearch(data, 50)); //-4 返回负数表示不存在
}
}
二维数组
在定义时使用一对方 括弧[] 就是一维数组;
在定义时使用两对方括弧[][] 就是二维数组.
二维数组的每个元素又是一个一维数组
二维数组的定义格式: 数据类型[][] 数组名 = new 数据类型[二维数组长度][];
public class Test05 {
public static void main(String[] args) {
//定义一维数组
int [] data1 = {1,2,3,4,5};
int [] data2 = {6,7,8};
int x = 10;
int y = 20;
//在静态初始化时,可以是常量,也可以是变量, 只要数组中元素是int类型即可
int [] data3 = { x, y };
//数据类型就是存储元素 的类型
int [] [] mydata = { data1, data2, data3};
//1)当前mydata数组中存储的data1,data2,data3又是一个一维数组 ,mydata数组称为二维数组
//2)二维数组的定义格式: 数据类型[][] 数组名 = new 数据类型[二维数组长度][];
//定义二维数组, 长度是5, 数组中存储int类型数据
int [][] mydata2 = new int[5][];
//mydata2数组 有 5个元素, 每个元素是int []类型的, 它 是一种引用类型, 各个元素默认默认为null
for (int[] ints : mydata2) {
System.out.println( ints ); //null
}
//3)在定义二维数组时,也可以指定一维 数组的长度, 系统会给一维数组进行默认初始化
int [][] mydata3 = new int[5][3];
for (int[] ints : mydata3) {
// System.out.println( ints ); //ints是一维数组名, 存储的是一维数组的引用,不为null
//mydata3的每个元素ints是一维数组 , 还可以继续遍历ints一维数组中的元素
for (int anInt : ints) {
System.out.print( anInt + " \t ");
}
System.out.println(); //换行
}
//4)给二维 数组元素赋值, 赋值int[]一维数组
mydata3[0] = data1;
mydata3[1] = data2;
mydata3[2] = new int[]{6,6,6,6,6,6};
// mydata3[3] = {8,8,8}; //这种简化形式仅用于数组的静态初始化
//5)使用for循环遍历
for (int i = 0; i < mydata3.length; i++) {
//mydata3[i]二维数组元素是一维 数组, 还需要继续for循环遍历,就把mydata3[i]看作是一个一维数组名即可
for (int j = 0; j < mydata3[i].length; j++) {
System.out.print( mydata3[i][j] + "\t");
}
System.out.println();
}
//6)二维数组的静态初始化, 就是在定义数组的同时给数组元素赋值,静态初始化不需要指定数组的长度
int [][]mydata4 = new int[][]{ data1, data2, data3};
//可以简化为
int [][] mydata5 = {data1, data2, data3};
//或者:
int [][] mydata6 = { {1,2,3,4,5,6,7} , {4,5,6,7,8}, {9,9,9}};
//或者
int [][] mydata7 = { data1, new int[]{6,6,6,6,6}, {7,8,9}};
//7)可以调用Arrays.deeptoString()把二维数组的元素转换为字符串
System.out.println(Arrays.deepToString(mydata5));
System.out.println(Arrays.deepToString(mydata6));
System.out.println(Arrays.deepToString(mydata7));
}
}
对象数组
数组中存储的是对象, 称为对象数组
public class Test06 {
public static void main(String[] args) {
//1)定义数组存储Student对象
Student[] data = new Student[100];
//一般情况下, 会定义一个变量记录数组中元素的数量, 每次添加元素时,size就加1
int size = 0 ;
//2)添加元素
//注意,对象数组元素其实存储的是对象的引用
//数组一般顺序存储
data[size] = new Student("lisi", 20, 98); //new创建一个新的Student对象,把该对象的引用赋值给data[0]元素
size++; //1
data[size] = new Student("wangwu", 40, 68);
size++; //2
data[size] = new Student("zhaoliu", 30, 88);
size++; //3
data[size++] = new Student("feifei", 18, 59 );
data[size++] = new Student("laodu", 35, 10 );
//3)打印数组中存储的Student对象, 只需要遍历添加的元素即可, 不需要遍历所有的元素
for (int i = 0; i < size ; i++) {
System.out.println( data[i]);
}
}
}