关于数组的基础知识
1、 Java语言中的数组是一种引用数据类型,不属于基本数据类型。数组的父类是Object。
2、数组实际上是一个容器,可以同时容纳多个元素。(数组是一个数据的集合)数组:宇面意思是“一组数据”。
3、数组当中可以存储"基本数据类型"的数据,也可以存储"引用数据类型"的数据。
4、数组因为是引用类型,所以数组对象是堆内存当中。(数组是存储在堆当中的)
5、数组在内存方面是怎么样的一个图形?
6、数组当中如果存储的是"java对象”的话,实际上存储的是对象的“引用(内存地址)"。
7、数组一旦创建,在java中规定,数组长度不可变。
8、数组的分类:一维数组、二维数组、三维数组、多维救组... (一维数组较多,二维数组偶尔使用)
所有的数组对象都有Length属性(java自带的),用来获取数组中元素的个数。
9、java中的数组要求数组中元素的类型统一。
比如int类型数组只能存储int类型,Person类型数组只能存储person类型。
10、数组在内存方面存储的时候,数组中的元素内存地址(存储的每一个元素都是有规剧的挨着排列的)是连续的。内存地址连续这是救组存储元素的特点(符色)。数组实际上是一种简单的教据结构。
11、所有的数组都是拿"第一个小方框的内存地址”作为整个数组对象的内存地址。数组中每一个元素都是有下标的,下标从0开始,以1递增。最后一个元素的下标是:Length - 1。下标非常重要,因为我们对数组中元素进行"存取"的时候,都需要通过下标来进行。
例图:
- 数组这种数据结构的优点和缺点
1、优点:
查询/查找/检索某个下标上的元素时效事极高。可以说是查询效率最高的一个数据结构。
为什么检索效率高?
①:每一个元素的内存地址在空间存储上是连续的。
②:每一个元素类型相同,所以占用空间大小一样。
③:知道第一个元素内存地址,知道每一个元素占用空间的大小,又知道下标,所以通过一个数学表达式就可以计算出某个下标上元素的内存地址。直接通过内存地址定位元素,所以数组的检索效率是最高的。数组中存储100个元素,或者存储100万个元素,在元素查询/检索方面,效率是相同的。因为数组中元素查找的时候不会一个一个找,是通过数学表达式计算出来的。(算出一个内存地址,直接定位的。)
2、缺点
①:由于为了保证数组中每个元素的内存地址连续,所以在数组上随机删除或者增加元素的时候效率较低,因为随机增删元素会涉及到后面元素统一向前或者向后位移的操作。
②:数组不能存储大数据量。
因为很难在内存空间上找到一块特别大的连续的内存空间。注意:对于数组中最后一个元素的增删,是没有效率影响的。
一维数组
- 怎么声明/定义一个一维数组?
语法格式:
int [] arrayl;
double[] array2;
boolean[] array3;
String[] array4;
Object[] array5;
- 怎么初始化一个一维数组呢?
包括两种方式:静态初始化一维数组,动态初始化一堆数组。静态初始化语法格式:
int[] array = {100, 2100, 300, 55};
动态切始化语法格式:
int[] array = new int[5];
/*这里的5表示数组的元素个数。初始化
一个5个长度的int类型数组,每个元素默认值0.
*/
String[] names = new String[6];
/*初化6个长度的string类型数组,
每个元素默认值null。*/
- 一维数组中元素的访问
代码示例(静态初始化方式):
public class DemoTest{
public static void main(String[] args) {
//静态初始化方式
int[] a = {1, 2, 3, 4, 5, 6};
//所有的数组对象都有length属性
System.out.println("数组中的元素个数为:" + a.length);
//数组中每一个元素都有下标
// 通过下标对数组中的元素进行存和取。
// 取(读)
System.out.println("第一个元素是:" + a[0]);
System.out.println("最后一个元素是:" + a[5]);
System.out.println("最后一个元素是:" + a[a.length - 1]);
//存(改)
//把第一个元素改为111
a[0] = 111;
System.out.println("修改后的第一个元素是:" + a[0]);
//把最后一个元素改为666
a[a.length - 1] = 666;
System.out.println("修改后的最后一个元素是:" + a[a.length - 1]);
}
}
输出:
- 一维数组遍历
代码示例:
public class DemoTest{
public static void main(String[] args) {
int[] a = {1, 2, 3, 4, 5, 6};
for (int i = 0; i < a.length; i++) {
System.out.println(a[i]);
}
for (int i = a.length; i > 0; i--) {
System.out.println("颠倒顺序输出:" + a[i - 1]);
}
//System.out.println(a[6]);
/*出现著名异常:
ArrayIndexOutOfBoundsException
也就是:下标越界异常
常见著名异常还有:
空指针异常、类型转换异常.
*/
}
}
- 动态初始化一维数组
代码示例:
public class DemoTest{
public static void main(String[] args) {
/*采用动态初始化的方式创建长度为4的int数组,
数组中每个元素的默认值是0*/
int[] a = new int[4];
for (int i = 0; i < a.length; i++) {
System.out.println("数组中下标为" + i + "的元素是:" + a[i]);
}
/*采用动态初始化的方式创建长度为3的Object数组,
数组中每个元素的默认值是null*/
Object[] ob = new Object[3];
for (int i = 0; i < ob.length; i++) {
System.out.println(ob[i]);
}
/*采用静态初始化的方式创建*/
Object object = new Object();
Object object1 = new Object();
Object object2 = new Object();
Object[] o = {object,object1,object2};
/*还可以采用以下方式:
* Object[] o = {new Object(), new Object(), new Object()};
* */
for (int i = 0; i < o.length; i++) {
System.out.println(o[i]);
}
}
}
- 什么时候采用静态初始化方式?什么时候使用动态初始化方式呢?
当创建数组的时候,确定数组中存储那些具体的元素时,采用静态初始化方式。
当创建数组的时候,不确定将来教组中存储那些数据,你可以采用动态初始化的方式,预先分配内存空间。
- 当方法的参数是数组时
1、代码示例:
public class DemoTest{
public static void main(String[] args) {
int[] x = {1,2,3};
String[] s = {"qqq","www","eee"};
printArray(x);
printArray(s);
}
/*
这里使用静态方法比较方便,
不需要new对象
*/
public static void printArray(int[] array){
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
}
public static void printArray(String[] args){
for (int i = 0; i < args.length; i++) {
System.out.println(args[i]);
}
}
}
输出:
2、 直接传递一个静态数组
printArray(new int[]{7,8,9});
- main方法上面的"string[] args"有什么用?
1、JVM负责调用main方法
JVM调用main方法的时候,会自动传一个String数组过来。
2、代码示例:
public class DemoTest{
public static void main(String[] args) {
System.out.println(args.length);//输出:0
/*
* 经过测试,args不是null,而是默认为0.
* 这个数组什么时候里面会有值呢?
* 其实这个数组是留给用户的,
* 用户可以在控制台上输入参数,
* 这个参数自动会被转换为"string[] args"。
* */
for (int i = 0; i < args.length; i++) {
System.out.println(args[i]);
}
/*
* 用命令行这样运行程序: java DemoTest abc def
* 那么这个时候JVM会自动将"abc def"通过空格
* 的方式进行分离,分离完成之后,自动放到
* "String[] args"数组里面
* 在工具里面需要如下设置
* */
}
}
设置前输出:
在IDEA里面设置后:
输出:
- 数组中存储引用数据类型
1、一维数组的深入,数组中存储的类型为:引用数据类型
对于数组来说,实际上只能存储java对象的“内存地址”。数组中存储的每个元素是“引用"。
2、代码示例:
public class DemoTest{
public static void main(String[] args) {
Demo a1 = new Demo();
Demo a2 = new Demo();
Demo[] demos1 = {a1,a2};
for (int i = 0; i < demos1.length; i++) {
Demo a = demos1[i];
a.move();
//也可以采用以下方法,更简洁。
demos1[i].move();
}
Demo[] demos = {new Cat(), new Bird()};
for (int i = 0; i < demos.length; i++) {
//demos[i].move();
if(demos[i] instanceof Cat){
Cat cat = (Cat)demos[i];
cat.catchM();
}else if(demos[i] instanceof Bird){
Bird bird = (Bird)demos[i];
bird.fly();
}
/*这里需要判断类型,然后向下强制转换
* 到相应的类型,才能调用其特有的方法*/
}
}
}
public class Demo{
public void move(){
System.out.println("animal move");
}
}
public class Cat extends Demo{
@Override
public void move() {
System.out.println("猫");
}
public void catchM(){
System.out.println("猫在抓老鼠");
}
}
public class Bird extends Demo{
@Override
public void move() {
System.out.println("鸟儿");
}
public void fly(){
System.out.println("flying");
}
}
- 一维数组的扩容/拷贝
1、在java开发中,数组长度一旦确定不可变,那么数组满了怎么办?
数组满了,需要扩容。
java中对数组的扩容是:先新建一个大容量的数组,然后将小容量数组中的数据一个一个拷贝到大数组当中。
2、结论:数组扩容效率较低。
因为涉及到烤贝的问题。所以在以后的开发中请注意:尽可能少的进行数组的拷贝。
可以在创建数组对象的时候预估计以下多长合适,最好预估准确,这样可以减少数组的扩容次数。提高效率。
3、代码示例:
public class DemoTest{
public static void main(String[] args) {
/*System.arraycopy(5个参数);
5个参数分别为:
(拷贝源数组名,
需要拷贝的起始位置(下标),
目标数组名,
需要拷贝到目标数组的起始位置(下标),
拷贝长度)
拷贝长度可以理解为:
将源数组上的一段剪下来,
覆盖在目标数组的一段相应位置。
*/
//拷贝源
int[] src = {1,2,3,4};
//拷贝到目标数组上
int[] dest = new int[7];
//调用arraycopy方法完成数组的拷贝
System.arraycopy(src, 1, dest, 3, 2);
//拷贝之后遍历目标数组
for (int i = 0; i < dest.length; i++) {
System.out.println(dest[i]);
}
}
}
输出:
public class Demo{
//数组中如果存储的元素是引用也可以拷贝。
public static void main(String[] args) {
Object[] object = {new Object(), new Object(), new Object()};
Object[] newobj = new Object[5];
System.arraycopy(object,0,newobj,0,object.length);
for (int i = 0; i < newobj.length; i++) {
System.out.println(newobj[i]);
}
}
}
输出:
4、相应的内存图:
二维数组
- 关于二维数组的描述
1、二维数组其实是一个特殊的一维数组,特殊在这个一维数组当中的每一个元素是一个一维数组。
2、二维数组的访问:
a[二维数组中的一维教组的下标][一维数组的下标]
- 二维数组静态初始化:
int[][] a = {
{1,2,3},
{4,5,6},
{7,8,9}
};
- 二维数组的length属性及遍历
代码示例:
public class DemoTest{
public static void main(String[] args) {
int[][] a = {
{1,2,3},
{4,5,6},
{7,8,9}
};
/*length属性*/
System.out.println(a.length);
System.out.println("一:" + a[0].length
+ " 二:" + a[1].length + " 三:" + a[2].length);
System.out.println("=====================");
/*遍历*/
for (int i = 0; i < a.length; i++) {
for (int j = 0; j < a[i].length; j++) {
System.out.println(a[i][j]);
}
}
}
}
- 二维数组的动态初始化
public class Demo{
public static void main(String[] args) {
int[][] a = new int[3][4];
a[0] = new int[]{1, 1, 1, 1};
a[1] = new int[]{2, 2, 2, 2};
a[2] = new int[]{3, 3, 3, 3};
printArray(a);
}
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();
}
}
}