一维数组
概述
- Java语言中的数组是一种引用数据类型,不属于基本数据类型,数组的父类是Object。
- 数组实际上是一个容器,可以同时容纳多个元素(数组是数据的集合)
- 元素的类型要统一
- 数组中的元素内存地址是连续的
- 数组中可以存储“基本数据类型”和“引用数据类型”(对象的引用,即内存地址,4个字节长度)的数据
- 因为数组是引用数据类型,所以数组对象是在堆内存当中(数组存储在堆内存当中)
- 数组一旦创建,在java中规定,长度不可变(数组长度不可变)
- 所有数组对象都有 length 属性(java自带的),用来获取数组中的元素个数
- 数组是一种数据结构
- 所有数组都是将首元素的内存地址作为自己的内存地址
- 数组中的每一个元素都有下标,下标从0开始,以1递增,最后一个元素的下标是:length-1
特点总结:连续、统一、不变
内存结构图
优点
- 每一个元素的内存地址在空间储存上是连续的
- 每一个元素类型相同,所以占用空间大小一样
- 知道第一个元素内存地址,知道每一个元素占用空间的大小,又知道下标,所以通过一个数学表达式就可以计算出某个下标上元素的内存地址。直接通过内存地址定位元素,所以数组的检索效率是最髙的。
数組中存储100个元素,或者存储100万个元素,在元素査询/检索方面,效率是相同的,因为数組中元素査找的肘候不会一个一个找,是通过数学表达式计算出来的。
缺点
- 由于为了保证数组中每个元素的内存地址连续,所以在数组上随机删除或者増加元素的时候,效率较低,因为随机増删元素会涉及到后面元素统一向前或者向后位移的操作。注意:对于数组中最后一个元素的増删,是没有效率形响的。
- 数组不能存储大數据量,因为很难在内存空间上找到一块特别大的连续的内存空闾。
声明/定义
局部变量定义好后要显式地初始化
//Java风格
int[] array01;
//c++的方式,不建议
//c中这样回报错:storge size不知道
int array01[];
//eg.main方法中的参数
String[] args
//或者
String args[]
//显示初始化
int[] nums={1,2,3};
array01=nums;
//以下不是显式地初始化
//在init类中的静态方法doSome()中进行初始化
init.doSome(array01);
初始化
静态初始化
int[] array01={1,2,3,4,5};
//假设如下初始化
//array02.length=0
//array02有地址
int[] array02={};
动态初始化
//每个元素默认值是0
int[] array01=new int[5];
//每个元素默认值是null
String[] array02=new String[5];
当你创建数组的时候,确定数组中存储哪些具体的元素时,采用静态初始化方式,不确定的时候,采用动态初始化,预先分配内存空间
遍历数组
int[] a={1,2,3,4,5};
//从第一个元素开始遍历
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]);
}
方法参数是数组
public class ArraysTest01 {
public static void main(String[] args) {
int[] a={1,2,3};
printArray(a);
System.out.println("-------------");
printArray(new int[3]);
System.out.println("-------------");
printArray(new int[]{6,7,8});
}
public static void printArray(int[] array){
for(int i=0;i<array.length;i++){
System.out.println(array[i]);
}
}
}
结果:
解析mian方法参数
" String[ ] args "有什么作用?JVM调用main方法时,会自动传一个String数组过来,主要用来接收用户输入参数的。
这个数组什么时候里面会有值呢?其实这个数组是留给用户的,用户可以在控制台上输入参数,这个参数自动会披转换为" String[ ] args " 。例如这样运行程序∶java ArrayTest05 abc def xyz,那么这个时候JVM会自动将abc def xyz通过空格的方式进行分离,分离完成之后,自动放到" String[ ] args "数组。(把abc def xyz转换成字符串数组:{ "abc", "def", "xyz" } )
public class ArraysTest02 {
public static void main(String[] args) {
//这里测试得出两个结论
//1.args数组长度默认值为0
//2.args不为null,可能JVM内部已进行初始化
System.out.println("args数组长度为"+args.length);
String[] strs={ };
printLength(strs);
for(int i=0;i< args.length;i++){
System.out.println(args[i]);
}
}
public static void printLength(String[] array){
System.out.println("strs数组长度为"+array.length);
}
}
怎么在IDEA中传入abc、def、xyz等参数? run→ edit configuration →在 Program arguments 上打上abc、edf、xyz,结果如下:
实际应用:模拟一个系统,假设这个系统要使用,必须输入用户名和密码
public class ArrayTest03 {
public static void main(String[] args) {
if(args.length!=2){
System.out.println("要使用此系统,必须输入用户名和密码,如 zhangsan 123");
return;
}
//程序执行到这里说明正确输入了信息
//取出用户名
String username=args[0];
//取出密码
String password=args[1];
//表示用户名为admin,密码是123时登录成功,其他一律失败
//username.equals("admin")&&password.equals("123")
//以下写法能避免空指针异常
if("admin".equals(username)&&"123".equals(password)){
System.out.println("登录成功,欢迎["+username+"]回来");
System.out.println("您可以继续使用该系统....");
}else{
System.out.println("验证失败,用户名不存在或者密码错误!");
}
}
}
存储引用数据类型
public class ArraysTest04 {
public static void main(String[] args) {
Bird a1=new Bird();
Cat a2=new Cat();
Plant a3=new Plant();
//能放子类,放不了Plant类
Animal[] ans= {a1,a2};
//Animal中没有Scratch()方法
//ans[0].Scratch();
for(int i=0;i< ans.length;i++){
if(ans[i] instanceof Cat){
Cat cat=(Cat)ans[i];
cat.scratch();
}else if(ans[i] instanceof Bird){
Bird bird=(Bird)ans[i];
bird.sing();
}
}
}
}
class Animal{
public void move(){
System.out.println("Animal is moving.");
}
}
class Cat extends Animal{
@Override
public void move() {
System.out.println("Cat is walking");
}
public void scratch(){
System.out.println("疯狂乱抓");
}
}
class Bird extends Animal{
@Override
public void move() {
System.out.println("Bird is flying.");
}
public void sing(){
System.out.println("Bird is singing songs.");
}
}
class Plant{
public void absorb(){
System.out.println("Plant is obsorbing CO2");
}
}
数组扩容
在java开发中,数组长度一旦确定不可变,那么数组满了时需要扩容。 java中对数组的扩容是∶先新建—个大容量的数组,然后将小容量数组中的数据一个一个拷贝到大数组当中。
结论:数组扩容效率较低。因为涉及到拷贝的问题。所以在以后的开发中请注章∶尽可能少的进行数组的拷贝。可以在创建数组对象的时候预估计一下多长合适,最好预估准确,这样可以减少数组的扩容次数。提高效率。
public class ArraysTest05 {
public static void main(String[] args) {
//数组拷贝
//拷贝源
int[] src={1,11,22,3,4};
//拷贝目标
int[] dest=new int[20];//动态初始化,每个元素默认值为0
//调用JVM System类中的arraycopy方法,来完成数组的拷贝
System.arraycopy(src,1,dest,3,2);
for(int i=0;i< dest.length;i++){
System.out.println(dest[i]); //0 0 0 11 22 ... 0
}
//完全拷贝
System.arraycopy(src,0,dest,0,src.length);
Object[] obj={new Object(),new Object(),new Object()};
Object[] newObj=new Object[5];
//这里拷贝的是对象的地址,而不是对象
System.arraycopy(obj,0,newObj,0,obj.length);
for(int i=0;i< newObj.length;i++){
System.out.println(newObj[i]);
//java.lang.Object@16b98e56 java.lang.Object@7ef20235 java.lang.Object@27d6c5e0...null
}
}
}
二维数组
二维数组其实是一个特殊的一维数组,特殊在这个一维数组当中的每一个元素是一个一维数组。三维数组是一个特殊的二维数组,特殊在这个二维数组中每一个元素是一个一维数组。
遍历
public class ArraysTest06 {
public static void main(String[] args) {
//静态初始化
int[][] a={
{1,2,3},
{4,5,6,7},
{8,9,10}
};
System.out.println(a[2][2]);
System.out.println("--------------------");
printArray(a);
System.out.println("--------------------");
printArray(new int[][] { {11,22},{33,44,55} });
}
public static void printArray(int[][] array){
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();
}
}
}
算法
冒泡排序算法
- 每一次循环结束之后,都要找出最大的数据,放到参与比较的这堆数据的最右边。(冒出最大的那个气泡)
- 左边和右边比大小,左边>右边时交换位置
/*
冒泡排序法:
原始数据:3 2 8 7 1 最外层循环4次 内层循环最大4次递减
第一次循环:
2 3 8 7 1(3比2,交换)
2 3 8 7 1(3比8,不交换)
2 3 7 8 1(8比7,交换)
2 3 7 1 8(8比1,交换)
第二次循环:2 3 7 1
2 3 7 1(2比3,不交换)
2 3 7 1(3比7,不交换)
2 3 1 7(7比1,交换)
第三次循环:2 3 1
2 3 1(2比3,不交换)
2 1 3(3比1,交换)
第四次循环:2 1
1 2(2比1,交换)
*/
public class BubbleSort {
public static void main(String[] args) {
int[] a={3 ,2 ,8 ,7 ,1};
//5个数据,循环4次
//for(int i=0;i<a.length-1;i++){}
//定义一个比较次数
int count=0;
//采用递减的方式与内层循环有联系
for(int i=a.length-1;i>0;i--){
for(int j=0;j<i;j++){
count++;
if(a[j]>a[j+1]){
int temp;
temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
}
}
//比较次数
System.out.println("比较次数:"+count);
//输出结果
for(int i=0;i<a.length;i++){
System.out.println(a[i]);
}
}
}
选择排序算法
每一次从这堆参与比较的数据当中找出最小值,拿着这个最小值和最前面的元素交换位置,选择排序比冒泡排序好在:每一次交换位置都是有意义的。关键点在于找出最小,先假设一开始最左边最小,假设
- 第一个3最小,3和2比,发现2更小,所以此时最小的是2
- 继续拿着2往下比对,2和6比较,2仍然是最小的
- 继续拿着2往下比对,2和1比较,发现1更小,所以此时最小的是1
- 继续拿着1往下比对,1和5比较,1仍然是最小
与冒泡排序算法相比:比较次数一样、交换次数减少
/*
第一次循环:参与 3 1 6 2 5 (参与比较的最左边的元素下标为0)
1 3 6 2 5(排序后)
第二次循环:参与 3 6 2 5(参与比较的最左边的元素下标为1)
2 6 3 5(排序后)
第三次循环: 参与 3 6 5(参与比较的最左边的元素下标为2)
3 6 5(排序后)
第四次循环:参与 6 5(参与比较的最左边的元素下标为3)
5 6(排序后)
5条数据,循环4次
*/
public class SelectSort {
public static void main(String[] args) {
int[] a={3,1,6,2,5};
int count=0;
for(int i=0;i<a.length-1;i++){
//这时输出i正好是0 1 2 3 对应着起点下标
//假设起点i下标位置上的元素是最小的
int min=i;
for(int j=i+1;j<a.length;j++){
count++;
//输出j 对应i=0时 j为1 2 3 4
if(a[j]<a[min]){
min=j;
}
}
//当i和min相等,说明一开始的假设是对的
//如果不相等,说明有更小的元素,需要交换位置
if(min!=i){
int temp;
temp=a[min];
a[min]=a[i];
a[i]=temp;
}
}
//比较次数
System.out.println("比较次数:"+count);
//排序之后遍历
for(int k=0;k<a.length;k++){
System.out.println(a[k]);
}
}
}
二分法查找
线性查找
//需求:找出89的下标,如果没有,返回-1
public class ArraySearch {
public static void main(String[] args) {
int[] a = {1, 6, 89, 55, 34};
int index = arraySearch(a, 55);
System.out.println(index == -1 ? "该元素不存在" : "该元素下标是" + index);
}
//参数1:传入的数组 参数2:要找的元素值
//返回值返回下标,没有返回-1
public static int arraySearch(int[] array,int x){
for(int i=0;i<array.length;i++){
if(x==array[i]){
return i;
}
}
return -1;
}
}
二分法查找(基于排序的基础上)
public class ArrayUtil {
public static void main(String[] args) {
int[] a = {1, 6, 11, 13, 17,19};
int index = binarySearch(a, 11);
System.out.println(index == -1 ? "该元素不存在" : "该元素下标是" + index);
}
//参数1:传入的数组 参数2:目标元素
//返回值返回下标,没有返回-1
public static int binarySearch(int[] array,int dest){
//开始下标
int begin=0;
//结束下标
int end=array.length-1;
//开始元素的下标只要在结束元素下标的左边,就有机会循环
while (begin<=end){
//中间元素下标
int mid=(begin+end)/2;
if (array[mid] == dest) {
return mid;
} else if (array[mid] < dest) {
//到这里说明目标在中间的右边
begin = mid + 1;
} else {
//目标在中间的左边
end = mid - 1;
}
}
return -1;
}
}
数组工具类
Arrays
java.util.Arrays 工具类中的方法大多都是静态的,以下两个都为静态方法,直接使用类名调用就行,使用Arrays.binarySearch()的前提是先使用Arrays.sort(数组名) ,把数组中的元素排好序(实践中发现如果不先排好序,返回的下标不准),其返回值示要查的值而定
import java.util.Arrays;
public class Test01 {
public static void main(String[] args) {
int[] a={11,34,5,43,33,44,44};
//排序
Arrays.sort(a);
//遍历
for(int i=0;i<a.length;i++){
System.out.print(a[i]+" ");//5 11 33 34 43 44 44
}
System.out.println();
//二分法查找
int index01=Arrays.binarySearch(a,5);
int index02=Arrays.binarySearch(a,4); //返回-1
int index03=Arrays.binarySearch(a,12); //搜索值在范围里但不存在,返回"-插入点索引值" 这里33为插入点
//即5 11 12 33 34 43 44 44 此时33下标为3,3即为插入点索引值
int index04=Arrays.binarySearch(a,45); //返回"– (length + 1)"
System.out.println(index01+" "+index02+" "+index03+" "+index04);//0 -1 -3 -8
}
}