数组
一、数组这种数据结构的优点和缺点是什么 ?
优点:
- 每一个元素的内存她址在空间存储上是连续的。
- 每一个元素类型相同,所以占用空间大小一样。
- 知道第一个元素内存地址,知道每一个元素占用空间的大小,又知道下标,所以通过一个数学表达式就可以计算出某个下标上元素的内存地址。直接通过内存地址定位元素,所以数组的检索效率是最高的。
补充:
数组中存储100个元素,或者存储109 万个元素,在元素查询/检索方面,效率是相同的因为数组中元素查找的时候不会一个一个找,是通过数学表达式计算出来的。(算出一个内存地址,直接定位的)
缺点:
1.由于为了保证数组中每个元素的内存地址连续,所以在数组上随机删除或者增加元素的时候效率较低,因为随机增删元素会涉及到后面元素统一向前或者向后位移的操作。
2.数组不能存储大数据量,因为很难在内存空闻上龙到一块特别大的连续的内存空间。
注意:
对于数组中最后一个元素增删,是没有效率影响的。
二、怎么声明一个数组?
语法格式:
- int [ ] array1;
- double [ ] array2;
- boolean [ ] array3;
- String [ ] array4;
- Object [ ] array5;
三、怎么初始化一个数组?
1.静态初始化语法格式:
int [ ] array = {100, 200, 300};
2.动态初始化语法格式:
int [ ] array = new int [5]; 这里的5表示数组的元素个数。
初始化一个5个长度的int类型数组,每个元素默认值为0。
String [ ] name = new String [6]; 初始化6个长度的string类型数组,每个元素默认值为null。
四、数组的读取、存储与修改
public class Text {
public static void main(String[] args) {
//声明一个int类型的数组,使用静态初始化的方式
int [] a = {100, 200, 300, 400, 500, 600};
//所有的数组对象都有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;
// 把最后一个元素修改为0
a[a.length - 1] = 0;
System.out.println("第一个元素" + a[0]);
System.out.println("最后一个元素" + a[a.length - 1]);
}
五、数组的遍历
public class Text {
public static void main(String[] args) {
int [] a = {100, 200, 300, 400, 500, 600};
//从前往后遍历
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 Text {
public static void main(String[] args) {
//创建一个int类型的数组
int[] x = {1, 2, 3, 4, 5, 6};
printArray(x);
printArray(new int[] {10, 11, 12}); //静态方法直接传入到方法中
//创建一个字符串类型的数组
String[] str = {"abc", "qwe", "zxc"};
printArray(str);
//动态初始化一个字符串类型的数组
String[] str1 = new String[3];
printArray(str1); //3个null
//动态初始化一个int类型的数组
printArray(new int[6]); //动态方法直接传入到方法中
}
public static void printArray(int[] array){ //这里的形参是一个int类型数组
for (int i = 0; i < array.length; i++){
System.out.println(array[i]);
}
}
public static void printArray(String[] array){ //这里的形参是一个String类型的数组
for (int i = 0; i < array.length; i++){
System.out.println(array[i]);
}
}
}
八、main方法的String数组
在idea运行配置中可以修改程序实参。
public static void main(String[] args) {
}
//这里是一个名为mian的静态方法。方法内需要传入一个String类型的数组。我们可以通过修改运行配置中的实参来实现。
public static void main(String[] args) {
if(args.length != 2){
System.out.println("对不起请输入符合规定的长度");
}
String userName = args[0];
String passKey = args[1];
//把"tom"放在前面可以避免空指针异常
if("tom".equals(userName) && "123456".equals(passKey)){
System.out.println("欢迎您进入系统");
} else {
System.out.println("对不起,用户名或密码错误");
}
}
九、数组中存放引用数据类型
对于数组来说实际上只能存储Java对象的"内存地址"。数组中存储的每个元素是“引用”。
1.静态创建引用数据存放
public class Text {
public static void main(String[] args) {
//创建对象
Animal a1 = new Animal();
Animal a2 = new Animal();
//创建一个名为array的Animal类型数组,里面存放了两个引用
Animal[] array = {a1, a2};
for (int i = 0; i < array.length; i++){
//通过for循环来调用Animal类中的move()方法
array[i].move();
}
}
}
class Animal{
public static void move(){
System.out.println("Animal move....");
}
}
2.动态创建引用数据存放
public class Text {
public static void main(String[] args) {
//动态初始化一个长度为2的Animal类型数组
Animal[] array = new Animal[2];
//创建一个Animal对象,把它放到数组的第一个盒子中。
array[0] = new Animal();
//Animal数组中只能存放Animal类型,不能存放与Animal无关的类型。
//例:array[0] = new product(); 这种写法是错误的
//Animal数组中可以存放Cat类型数据,因为Cat是一个Animal
//Cat是Animal的子类。
array[1] = new cat();
for (int i = 0; i < array.length; i++){
Animal a = array[i];
a.move();
}
}
}
class Animal{
public void move(){
System.out.println("Animal move....");
}
}
class product{
}
class cat extends Animal{
public void move(){
System.out.println("cat move.......");
}
}
3.引用数据在遍历中的多种情况
调用的方法是父类中存在的方法
public class Text {
public static void main(String[] args) {
Cat c = new Cat();
Bird b = new Bird();
Animal[] anis = {c, b}; //该数组中存储了两个对象的内存地址。
for (int i = 0; i < anis.length ; i++) {
Animal an = anis[i];// 这个取出来的可能Cat(引用c) ,也可能是Bird(引用b),
//Animal an = new Cat(); 或 Animal an = new Bird(); //不过肯定一个Animal类型.
an.move(); //如果调用的方法是父类中存在的方法不需要向下转型。
//直接使用父类型引用调用即可。
//如果想要调用子类中独有的方法则不行
}
}
}
class Animal{
public void move(){
System.out.println("Animal move....");
}
}
class Cat extends Animal{
public void move(){
System.out.println("猫在走猫步");
}
}
class Bird extends Animal{
public void move(){
System.out.println("鸟儿在飞翔");
}
}
调用的方法是父类中不存在的方法(子类中独有的方法)
public class Text {
public static void main(String[] args) {
Cat c = new Cat();
Bird b = new Bird();
Animal[] anis = {c, b}; //该数组中存储了两个对象的内存地址。
for (int i = 0; i < anis.length ; i++) {
//调用子类中特有的方法的话需要向下转型!!!
if(anis[i] instanceof Cat){
Cat cat = (Cat)anis[i];
cat.catchMouse();
} 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 catchMouse(){
System.out.println("猫在抓老鼠");
}
}
class Bird extends Animal{
public void move(){
System.out.println("鸟儿在飞翔");
}
//独有方法
public void sing(){
System.out.println("鸟儿在唱歌");
}
}
十、数组的扩容
在java开发中,数组长度一旦确定不可变,那么数组满了怎么办 ?
数组满了,需要扩容。
java中对数组的扩容是:
先新建一个大容量的数组,然后小容量数组中的数据一个一个拷贝到大数组当中。
结论: 数组扩容效率较低。因为涉及到拷贝的问题。所以在以后的开发中请注意:尽可能少的进行数组的拷贝。可以在创建数组对象的时候预估计一下多长合适,最好预估准确,这样可以减少数组的扩容次数。提高效率。
数组的扩容(拷贝)代码
public class Text {
public static void main(String[] args) {
//拷贝源(从这个数组中拷贝)
int[] src = {1, 22, 44, 55};
//拷贝目标(拷贝到这个目标数组上)
int[] arrge = new int[10]; //动态初始化一个长度为10的数组,每一个的默认元素为0.
//调用JDK System类中的arraycopy方法,来完成数组的拷贝
System.arraycopy(src, 1, arrge, 4, 2); //src表示拷贝源数组,
//1表示从src数组下标为1开始拷贝。
//arrge表示拷贝目标,
//4表示从arrge数组下标为4开始完成拷贝。
//2表示拷贝过来的元素个数一共有两个。
//System.arraycope(src, 0, arrge, 0, src.length);
for (int i = 0; i < arrge.length; i++){
System.out.println(arrge[i]);
}
}
}
十一、二维数组
1、二维数组其实是一个特殊的一维数组,特殊在这个一维数组当中的每一个元素是一个一维数组。
2、三维数组是什么 ?
三维数组是一个特殊的二维数组,特殊在这个二维数组中每一个元素是一个一维数组。实际的开发中使用最多的就是一维数组。二维数组也很少使用。三维数组几平不用
3、二维数组静态初始化
int[][] array = {{1, 2, 3}, {1, 1, 1}, {4, 5, 6}};
4、读取二维数组的长度
array[0].length; //获取二维数组中第一个数组的长度
5、二维数组中的读和改
读
public class Text {
public static void main(String[] args) {
//初始化一个二维数组
int[][] a = {
{1, 1, 1},
{2, 2, 2},
{3, 3, 3},
{4, 4, 4}
};
//a[二维数组中一维数组的下标][一维数组的下标];
System.out.println(a[3][2]);
}
}
改
public class Text {
public static void main(String[] args) {
//初始化一个二维数组
int[][] a = {
{1, 1, 1},
{2, 2, 2},
{3, 3, 3},
{4, 4, 4}
};
//a[二维数组中一维数组的下标][一维数组的下标];
System.out.println(a[3][2]);
a[3][2] = 200; //将[3][2]位置的元素改为200。
System.out.println(a[3][2]);
}
}
6、二维数组的遍历
public class Text {
public static void main(String[] args) {
//初始化一个二维数组
int[][] a = {
{1, 1, 1},
{2, 2, 2},
{3, 3, 3},
{4, 4, 4}
};
for (int i = 0; i < a.length; i++){ //外层循环三次(负责纵向)。
//负责遍历一遍一维数组
for (int j = 0; j < a[i].length; j++){
System.out.print(a[i][j] + "");
}
//输出换行符
System.out.println();
}
}
}
7、动态初始化一维数组
int[][] array = new int[3][4]; //三行四列,3个一维数组,每个一维数组中有4个元素。
8、方法的参数是一个二维数组
public class Text {
public static void main(String[] args) {
//初始化一个二维数组
int[][] a = { //也可以这么写
{12, 15, 13}, //printArray(new int[][]{{1, 1, 1}, {2, 3, 4}, {3, 6, 8}});
{25, 278, 26},
{3345, 389, 34},
{434, 495, 467}
};
//传入参数
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();
}
}
}
数组的编程练习题
编写程序,使用一维数组,模拟栈数据结构。
要求:
1.这个栈可以存储Java中的任何引用类型的数据。
2.在栈中提供push方法模拟压栈。(栈满了,要有提示信息。)
3.在栈中提供pop方法模拟弹栈。 (栈空了,也有有提示信息。)
4.编写测试程序,new栈对象,调用push pop方法来模拟压栈弹栈的动作。
题解:
MyStack类
public class MyStack {
//创建一个私有的Object类型数组。
private Object[] elements;
//设置一个栈帧,用来对数组个数进行判断。
//此时index = 0;代表数组内一个元素都没有
int index = 0;
//编写无参构造,初始化数组的长度(栈容量)为10。
public MyStack(){
this.elements = new Object[10];
};
//编写有参构造,可以让用户初始化数组的长度。
public MyStack(Object[] elements){
this.elements = elements;
}
//对Object类型数组设置set,get方法(只要私有化了,就要写上set,get方法)
public Object[] getElements() {
return elements;
}
public void setElements(Object[] elements) {
this.elements = elements;
}
//编写push方法实现压栈动作
public void push(Object pushNum){ /*这里的形式参数为Object类型是
为了让引用数组类型可以传入其中*/
if (this.index == elements.length){ /*我们设置了栈帧为0所以说此时的栈帧指向了数组长度只要 栈帧增加到与数组长度相等时就无法压栈*/
System.out.println("对不起元素已满,压栈失败");
return;
}
this.index++; /*如果通过了上述if语句代表数组还没满,可以继续压栈。
每进行一次压栈,栈帧就会自加1(也就是代表数组中的元素加1)。*/
this.elements[index - 1] = pushNum; /*将新的引用存放入数组中。栈帧减1就代表了数组的下标, 随着栈帧的变化,存放"引用"也是被顺序存放*/
System.out.println(elements[index - 1] + "压栈成功,栈帧指向" + index);
}
//弹栈的方法,从数组中往外取元素。每取出一个元素栈帧向下移一位。弹出后需要返回一个类型方便以后我们使用
public Object pop(){
if (this.index == 0){ /*通过if语句来判读栈帧的指向。如果栈为0
则代表数组内没有元素了,提醒用户并返回空值*/
System.out.println("对不起栈已空,弹栈失败");
return null;
}
this.index--; //如果没有运行上述if语句则代表数组内还有元素,可以继续弹栈
System.out.println(elements[index] + "弹栈成功,栈帧指向" + index);
return elements[this.index] = null; //用null覆盖住原先的元素便于在使用时区分
}
}
Text类
package 数组.静态数组的创建;
public class Text {
public static void main(String[] args) {
//初始化一个长度为3的动态数组
MyStack stack = new MyStack(new Object[3]);
//创建几个对象测试用
Tack tack = new Tack();
Tack tack1 = new Tack();
Tack tack2 = new Tack();
//进行压栈测试,调用push方法把三个引用放入数组中
stack.push(tack);
stack.push(tack1);
stack.push(tack2);
//调用pop方法,来实现弹栈动作。
stack.pop();
stack.pop();
stack.pop(); //最后压入的最先弹出来(这个才符合栈的数据结构。)
}
}
Tack类
public class Tack {}
重要提示:System.out.println()方法执行时,如果想输出引用的话,会自动调用引用的toString方法。
第二题:
为某个酒店编写程序:酒店管理系统,模拟订房、退房、打印所有房间状态等功能。
1、该系统的用户是:酒店前台。
2、酒店使用一个二维数组来模拟。"Room[] [] rooms;"
3、酒店中的每一个房间应该是一个Java对象: Room。
4、每一个房间Room应该有: 房间编号、房间类型、房间是否空闲。
5、系统应该对外提供的功能:
可以预定房间:用户输入房间编号,订房。
可以退房:用户输入房间编号,退房。
可以查看所有房间的状态: 用户输入某个指令应该可以查看所有房间状态。
题解:
HotelMgSystem类
import java.util.Scanner;
public class HotelMgSystem {
public static void main(String[] args) {
Hotel hotel = new Hotel();
//接受用户键盘输入
Scanner input = new Scanner(System.in);
//一个简单的欢迎界面
System.out.println("欢迎使用酒店管理系统,请阅读以下使用说明");
System.out.println("功能编号对应功能");
System.out.println("1 ------> 查看房间列表");
System.out.println("2 ------> 表示订房");
System.out.println("3 ------> 表示退房");
System.out.println("0 ------> 退出系统");
while (true){
System.out.println("请输入功能编号:");
int i = input.nextInt();
if(i == 1){
//查看房间列表
hotel.print();
} else if (i == 2){
//订房
System.out.println("请输入您想要预定的房间号");
int j = input.nextInt();
hotel.order(j);
System.out.println("预定成功");
} else if (i == 3){
//退房
System.out.println("请输入您想要退房的房间号");
int j = input.nextInt();
hotel.exit(j);
System.out.println("退房成功");
} else if (i == 0){
//退出程序
System.out.println("程序已安全退出");
return;
} else {
System.out.println("对不起您输入的指令有误,请重新输入");
}
}
}
}
Hotel类
//模拟酒店对象,酒店中有二维数组,二维数组模拟大厦。
public class Hotel {
//二维数组模拟大厦所有房间
private Room[][] rooms;
//通过构造方法来盖楼
public Hotel(){
//一共有三层,每一层有十个房间。
this.rooms = new Room[3][10];
for(int i = 0; i < rooms.length; i++){ //i是楼层,j是房间
for (int j = 0; j < rooms[i].length; j++){
if (i == 0){
//一层
rooms[i][j] = new Room(100*(i+1)+j, "单人间", true);
} else if (i == 1){
//二层
rooms[i][j] = new Room(100*(i+1)+j, "双人间", true);
} else if (i == 2){
//三层
rooms[i][j] = new Room(100*(i+1)+j, "电竞房", true);
}
}
}
}
//在酒店对象上提供一个打印房间列表的方法
public void print(){
//打印所有房间状态就是遍历二维数组
for (int i = 0; i < rooms.length; i++){
//里面的for循环负责输出一层
for (int j = 0; j < rooms[i].length; j++){
System.out.print(rooms[i][j]);
}
//换行
System.out.println("");
}
}
//订房的方法。调用此方法时需要传一个房间编号过来。
public void order(int roomNum){
//订房最主要的是将对象的status修改为false。
//Room对象的status修改为false。
//可以假设一个房间号,然后通过演算获取房间对象
Room room = rooms[roomNum / 100 - 1][roomNum % 100 -1];
//修改为占用
room.setStatus(false);
}
//退房
public void exit(int roomNum){
Room room = rooms[roomNum / 100 - 1][roomNum % 100 -1];
//修改为空闲
room.setStatus(true);
}
}
Room类
package HotelMgSystem;
import HotelManagementSystem.RoomStatus;
public class Room {
//房间编号
private int id;
//房间类型
private String type;
//房间状态
private boolean status;
public Room(){}
public Room(int id, String type, boolean status) {
this.id = id;
this.type = type;
this.status = status;
}
//set,get方法的创建
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public boolean isStatus() { //is是boolean类型的get方法可以修改为get
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
public String toString(){ //toString方法就是将Java对象转换成字符串形式。目的:简单,明了
return "[" + this.id + " " + this.type + " " + (this.status ? "空闲":"占用") + "]";
}
//你认为两个房间的房间编号相同,就代表同一个房间,那么写代码比较房间编号就行。
public boolean equals(Object obj){
if (obj == null || !(obj instanceof Room)) return false;
if (this == obj) return true;
Room room = (Room)obj;
//当前房间编号等于传过来的房间编号。认为是同一个房间。
if (this.id == room.getId()) return true;
return false;
}
}