数组简介
Array:
1、java语言当中的数组是一种引用数据类型。不属于基本数据类型。数组的父类是Object.
2、数组实际上是一个容器,可以同时容纳多个元素。(数组是一个数据的集合)
3、数组当中可以存储基本数据类型的数据,也可以存储引用数据类型的数据。
4、数组因为是引用类型,所以数组对象是堆内存当中。(数组是存储在堆当中的)
5、数组当中如果存储的是"java对象"的话,实际上存储的是对象的"引用(内存地址)"。数组中不能直接存储java对象。
6、数组一旦创建,在java中规定,长度不可变。
7、数组的分类:一维数组、二维数组、三维数组、多维数组…
8、所有的数组对象都有length属性,用来获取数组中元素的个数。
9、java中的数组要求数组中元素的类型统一。比如int类型数组只能存储int类型,Person类型数组只能存储Person类型。
10、数组在内存方面存储的时候,数组中的元素内存地址是连续的.
11、数组中首元素的内存地址作为整个数组对象的内存地址。
12、数组中每一个元素都是有下标的,下标从0开始,以1递增。最后一个元素的下标是:length-1
通过下标对数组中元素进行"存取"。
13、数组这种数据结构的优缺点:
优点:查询/查找/检索某个下标上的元素时效率极高,可以说是查询效率最高的一个数据结构。
为什么检索效率高:
(1)、每一个元素的内存地址在空间存储上是连续的。
(2)、每一个元素类型相同,所以占用空间大小一样。
(3)、知道第一个元素内存地址,知道每一个元素占用空间的大小,又知道下标,所以通过一个数学表达式就可以计算出某个下标
上元素的内存地址。直接通过内存地址定位元素,所以数组的检索效率是最高的。
缺点:
(1)、由于为了保证数组中每个元素的内存地址连续,所以在数组上随机删除或者增加元素的时候,效率较低。
因为随机删除或者增加元素会涉及到后面元素统一向前或者向后位移的操作。
(2)、数组不能存储大数据量,为什么?
因为很难在内存空间上找到一块特别大的连续的内存空间。
注意:对于数组中左后一个元素的增删是没有效率影响的。
一维数组
1、怎么声明/定义一个以一维数组?
语法格式:
int[] array1;
double[] array2;
boolean[] array3;
String[] array4;
Object[] array5;
2、怎么初始化一个一维数组?
包括两种方式:静态初始化一维数组、动态初始化一维数组
静态初始化语法格式:
int[] array = {100,200,300,55};
动态初始化语法格式:
int[] array = new int[5];//这里的5表示数组的元素个数
//初始化一个5个长度的int类型数组,每个元素默认值是0。
String[] names = new String[6];//初始化6个长度的String类型数组,每个元素默认值null。
3、什么时候采用静态初始化方式,什么时候采用动态初始化方式
当创建数组的时候,确定数组中存储哪些具体元素时,采用静态初始化方式。
当创建数组的时候,不确定将来数组中存储哪些数据,采用动态初始化的方式,预先分配内存。
public class Test01 {
public static void main(String[] args) {
//声明一个int类型的数组,使用静态初始化的方式
int[] a1={1,100,10,20,65};
//所有的数组对象都有length属性 数组对象是数组类型的一个对象,数组元素时数组对象的一个元素
System.out.println("数组中元素的个数"+a1.length);
//声明一个int类型的数组,使用动态初始化的方式
int[] a=new int[5];
//初始化一个Object类型的数据,采用动态初始化方式
Object[] obj = new Object[3];//3个长度,动态初始化,所以默认每个元素的值是null
for(int i = 0;i<obj.length;i++){
System.out.println(obj[i]);
}
//存储object,采用静态初始化
Object o1 =new Object();
Object o2 =new Object();
Object o3 =new Object();
Object[] objects= {o1,o2,o3};
for(int i = 0;i<objects.length;i++){
System.out.println(objects[i]);
}
public class Test02 {
public static void main(String[] args) {
//静态初始化一维数组
int[] x = {1,2,3,4};
printArray(x);
System.out.println("==============");
//没有这种语法
//printArray({1,2,3,4});
//如果直接传一个静态数组的话,语法必须这样写
printArray(new int[]{1,2,3,4});
//动态初始化一维数组
int[] x2 = new int[4];
printArray(x2);
System.out.println("================");
printArray(new int[4]);
String[] st = {"abc","haha","hehe"};
printArray(st);
}
//为什么要用静态方法?方便,不需要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[] array){
for(int i = 0;i<array.length;i++){
System.out.println(array[i]);
}
}
}
关于main()方法
main方法上面的"String[] args"有什么用?
谁负责调用main方法(JVM)
JVM调用main方法的时候,会自动传一个String数组过来
public class Test03 {
//这个方法程序员负责写出来,JVM负责调用。JVM调用的时候一定会传一个String数组过来
public static void main(String[] args) {
System.out.println("JVM给传递过来的String数组参数,它这个数组的长度是"+args.length);//0
//这个数组什么时候里面会有值呢?
//其实这个数组是留给用户的,用户可以在控制台上输入参数,这个参数会自动被转换成"String[] args"
//所以main反复上面的String[] args数组主要是用来接收用户输入参数的。
//以下这行代码表示了:数组对象创建了,但是数组中没有任何数据。
String[] strs1 = new String[0];
String[] strs2 = {};//静态初始化数组,里面没东西
printLength(strs1);//0
printLength(strs2);//0
}
public static void printLength(String[] args){
System.out.println(args.length);//0
}
}
关于一维数组的例子
(1)、 模拟一个系统,假设这个系统要使用,必须输入用户名和密码
public class Test04 {
//用户名和密码输入到String[] args当中
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的时候表示登录成功,其他一律失败。
//判断两个字符串是否相等,需要使用equals方法
//if(userName.equals("admin") && password.equals("123"))
if("admin".equals(userName) && "123".equals(password))//这样写可以避免空指针异常
{
System.out.println("登陆成功,欢迎[" + userName + "]回来");
System.out.println("可以继续使用该系统......");
}
else {
System.out.println("验证失败,用户名不存在或者密码错误!");
}
}
}
(2)、创建一个Animal类型的数组
public class Test07 {
public static void main(String[] args) {
//创建一个Animal类型的数组
Animal a1 = new Animal();
Animal a2 = new Animal();
Animal[] animals = {a1,a2};
//对Animal数组进行遍历
for(int i = 0; i < animals.length; i++){
/* Animal a = animals[i];
a.move();*/
animals[i].move();
}
Animal[] ans = new Animal[2];
//创建一个Animal对象,放到数组的第一个盒子中
ans[0] = new Animal();
ans[0].move();//Animal move......
//Animal数组中可以存放Cat类型的数组,因为Cat是一个Animal.
//Cat是Animal的子类
ans[1] = new Cat();
//创建一个Animal类型的数组,数组当中存储Cat和Bird
Cat c = new Cat();
Bird b = new Bird();
Animal[] anis ={c,b};
//Animal [] anis = {new Cat(), new Bird()};//该数组中存储了两个对象的内存地址
for (int i = 0 ; i <anis.length; i++){
//这个取出来的可能是Cat,也可能是Bird,不过肯定是一个Animal
//如果调用的方法是父类中存在的方法不需要向下转型。直接使用父类型引用调用即可。
// Animal an = anis[i];
// an.move();
//调用子对象特有方法的话,需要向下转型!!!
if(anis[i] instanceof Cat){
Cat cat = (Cat)anis[i];
cat.catMouse();
}else if(anis[i] instanceof Bird){
Bird bird = (Bird)anis[i];
bird.sing();
}
}
}
}
class Animal{
public void move(){
System.out.println("Animal move......");
}
}
class Cat extends Animal{
public void move(){
System.out.println("猫在走猫步!!!");
}
//特有方法
public void catMouse(){
System.out.println("猫捉老鼠!!!");
}
}
class Bird extends Animal{
public void move(){
System.out.println("Bird fly!!!");
}
//特有方法
public void sing(){
System.out.println("鸟儿在歌唱!!!");
}
}
一维数组的扩容
关于一维数组的扩容:
在java开发中,数组长度一旦确定不可变,那么数组满了怎么办?
数组满了,需要扩容。
java中对数组扩容:
先创建一个大容量的数组,然后将小容量数组中的数据一个一个拷贝到大数组当中。
结论:数组扩容效率低,因为涉及到拷贝的问题。
public class Test08 {
//java中的数组是怎么进行拷贝的呢?
public static void main(String[] args) {
//拷贝源(从这个数组中拷贝)
int[] src = {1,11,22,3,4};
//拷贝目标(拷贝到这个目标数组上)
int[] dest = new int[20];//动态初始化一个长度为20的数组,每一个元素默认值是0
//调用JDK System类中的arraycopy方法,来完成数组的拷贝。
System.arraycopy(src,1,dest,3,2);
/*
arraycopy()方法用于将一个数组的元素快速拷贝到另一个数组。其中的参数如下
System.arraycopy(src, srcPos, dest, destPos, length);
src表示源数组
srcPos表示源数组中拷贝元素的起始位置。(索引)
dest表示目标数组
destPos表示拷贝到目标数组的起始位置
length表示拷贝元素的个数
*/
//遍历目标数组
for(int i = 0;i<dest.length;i++){
System.out.println(dest[i]);//0 0 0 11 22 0 ... 0
}
int[] dest2 = new int[20];
System.arraycopy(src,0,dest2,0,src.length);
//数组当中如果存储的元素是引用,可以拷贝吗?可以
String[] strs = {"hello","world","study"};
String[] newStr = new String[5];
System.arraycopy(strs,0,newStr,0,strs.length);
for(int i = 0;i<newStr.length;i++){
System.out.println(newStr[i]);//hello world study null null
}
Object[] objs={new Object(),new Object(),new Object()};
Object[] newobj= new Object[5];
//这里拷贝的时候拷贝的不是对象,而是拷贝对象的地址
System.arraycopy(objs,0,newobj,0,objs.length);
for(int i = 0;i<newobj.length;i++){
System.out.println(newobj[i]);//java.lang.Object@1c53fd30 java.lang.Object@50cbc42f java.lang.Object@75412c2f null null
}
}
}
二维数组
关于java中的二维数组
1、二维数组其实是一个特殊的一维数组,特殊在这个一维数组当中的每一个元素是一个一维数组。
2、三维数组是一个特殊的二维数组,特殊在这个二维数组中每一个元素都是一个一维数组。
3、二维数组静态初始化
int[][] array = {{1,1,2},{2,3,4,5,6},{1,3,6,7}};
4、关于二维数组中元素的:读和改
a[二维数组中的一维数组的下标][一维数组中元素的下标]
a[0][0]:表示第1个一维数组中的第1个元素
public class Test09 {
public static void main(String[] args) {
//一维数组
int[] array = {100,200,300};
//二维数组
int[][] a = {{100,200,300,800,600},
{30,20,40,80},
{6,7,9,1,0}
};
System.out.println(a.length);//3
System.out.println(a[0].length);//5
//二维数组遍历
for (int i = 0;i<a.length;i++){
for(int j = 0;j<a[i].length;j++){
System.out.println(a[i][j]);
}
System.out.println();
}
}
}
/*
动态初始化二维数组
*/
public class Test10 {
public static void main(String[] args) {
//3行4列
//3个一维数组,每个一维数组当中有4个元素。
int[][] array = new int[3][4];
int[][] a = {{100,200,300,800,600},
{30,20,40,80},
{6,7,9,1,0}};
printArray(a);
printArray(new int[][]{{1,2,3},{3,4,5,0,9},{12,13,15,9,7}});
}
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();
}
}
}
模拟栈数据结构
1、这个栈可以存储java中的任何引用类型的数组。
2、在栈中提供push方法模拟压栈。(栈满了要有提示信息)
3、在栈中提供pop方法模拟弹栈。(栈空了要有提示信息)
4、假设栈的默认初始化容量是10。
public class MyStack{//栈类
//提供一个数组来存储栈中的元素
private Object[] elements;
//栈帧,永远指向栈顶部元素
//注意:最初的栈是空的,一个元素都没有
//private int index = 0;//如果index采用0,表示栈帧指向了顶部元素的上方。
//private int index = -1;//如果index采用-1,表示栈帧指向了顶部元素。
private int index;
public MyStack() {
//一维数组动态初始化,默认初始化容量是10
this.elements = new Object[10];
this.index = -1;
}
/**
* 压栈的方法
* @param obj 被压入的元素
*/
public void push(Object obj){
if(this.index>=this.elements.length-1){
System.out.println("压栈失败,栈已满!");
return;
}
//程序走到这里,说明栈没满
//向栈中加1个元素,栈帧向上移动一个位置
this.index++;
this.elements[index]=obj;
//elements[++index]=obj;
//所有的System.out.println()方法执行时,如果输出引用的话,自动调用引用的toString()方法
System.out.println("压栈"+obj +"元素成功,栈帧指向"+index);
}
/**
* 弹栈的方法,从数组中往外取元素。每取出一个元素,栈帧向下移动一位。
* @return
*/
public void pop(){
if(index < 0){
System.out.println("弹栈失败,栈已空!");
}else{
System.out.print("弹栈"+this.elements[index] +"元素成功" );
//栈帧向下移动一位
index--;
System.out.println("栈帧指向"+index);
}
return ;
}
public Object[] getElements() {
return elements;
}
public void setElements(Object[] elements) {
this.elements = elements;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
}
public class MyStackTest {
public static void main(String[] args) {
//创建一个栈对象
MyStack stack=new MyStack();
//调用方法压栈
stack.push(new Object());//压栈java.lang.Object@39ba5a14元素成功,栈帧指向0
//弹栈
stack.pop();//弹栈java.lang.Object@39ba5a14元素成功栈帧指向-1
}
}