前言
我们知道如果在一个已知数组 int[] array ={1,2,3,4}里面再追加一个元素5,我们不能直接把元素添加到数组后面,因为数组的长度是不可以改变的,常见的方法是创建一个新的数组,而且需要把原来数组里面的元素一个一个的拷贝进来,然后在新数组后面添加元素。这个过程非常的麻烦,既要创建新的数组又要移动数组的元素。
如果我们想要删除一个索引下面的元素;例如一个数组 int[] array ={1,2,3,4},我想把索引2下面的元素3删除掉,数组长度变成3,使数组变成 int[] array ={1,2,4},我们需要把2索引对应的元素3移除,把3索引对应的元素4向前移动。
如果我们需要修改或者查询某个索引的值是比较简单的。
数组结构的优缺点:
- 增删慢:每次都需要创建新数组且移动元素位置。
- 查询快:由于数组在内存中是一块连续空间,因此可以通过地址加索引的方式快速获取对应位置上的元素。
ArrayList集合介绍
List接口的可调整大小的数组实现,而数组一旦初始化长度就不可以改变
ArrayList继承关系
1.Serializable标记性接口
不实现此接口的类将不会使任何状态序列化或反序列化。可序列化的所有子类型都是可序列化的。比如Student类是可以被序列化的,那么它的子类都是可以被序列化的。
序列化:将对象的数据写入到文件(写对象)
反序列化:将文件中对象的数据读取出来(读对象)
public interface Serializable {
}
2.Cloneable标记性接口
如果不实现Cloneable接口,使用对象的clone方法会抛CloneNotSupportedException异常。
克隆的前提条件
- 被克隆对象所在的类必须实现Cloneable接口
- 必须重写Cloneable方法
public interface Cloneable {
}
基本使用实现ArrayList里面的数据复制到另一个集合
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main5);
ArrayList<String> list = new ArrayList<>();
list.add("apple1");
list.add("apple2");
list.add("apple3");
//调用方法克隆
Object clone = list.clone();
System.out.println(clone == list);
System.out.println(list);
}
可以看出克隆出来对象和ArrayList集合的内存地址不一样,集合内容一样
看下ArrayList里面的clone方法
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
最终返回了一个ArrayList
案例:已知对象A的姓名为豹子头林冲,年龄为30,需求是将对象A的数据复制给对象B,并且此后对象A和B的数据互不影响
方式一:传统方式
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Student stu1 = new Student("豹子头林冲",30);
Student stu2 = new Student();
stu2.setName(stu1.getName());
stu2.setAge(stu1.getAge());
System.out.println(stu1 == stu2);
System.out.println(stu1);
System.out.println(stu2);
System.out.println("------此时不管修改哪个对象的内容,stu1和stu2都不会受到影响-----" );
stu1.setName("花和尚鲁智深");
stu1.setAge(35);
System.out.println(stu1);
System.out.println(stu2);
}
下面是运行结果,我们修改stu1,那么stu2不能受到影响
2021-04-29 10:58:47.557 7316-7316/com.example.arraylistdemo I/System.out: false
2021-04-29 10:58:47.557 7316-7316/com.example.arraylistdemo I/System.out: Student{name='豹子头林冲', age=30}
2021-04-29 10:58:47.557 7316-7316/com.example.arraylistdemo I/System.out: Student{name='豹子头林冲', age=30}
2021-04-29 10:58:47.557 7316-7316/com.example.arraylistdemo I/System.out: ------此时不管修改哪个对象的内容,stu1和stu2都不会受到影响-----
2021-04-29 10:58:47.557 7316-7316/com.example.arraylistdemo I/System.out: Student{name='花和尚鲁智深', age=35}
2021-04-29 10:58:47.557 7316-7316/com.example.arraylistdemo I/System.out: Student{name='豹子头林冲', age=30}
方式二:浅拷贝
class Skill {
private String skillName;
public Skill() {
}
public Skill(String skillName) {
this.skillName = skillName;
}
public String getSkillName() {
return skillName;
}
public void setSkillName(String skillName) {
this.skillName = skillName;
}
@Override
public String toString() {
return "Skill{" +
"skillName='" + skillName + '\'' +
'}';
}
}
被clone的对象一定要实现Cloneable 接口实现clone方法
class Student implements Cloneable {
private String name;
private int age;
private Skill skill;
public Student() {
}
public Student(String name, int age, Skill skill) {
this.name = name;
this.age = age;
this.skill = skill;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Skill getSkill() {
return skill;
}
public void setSkill(Skill skill) {
this.skill = skill;
}
/**
*
* 1.权限修饰符更改为public
* 2.方法的返回值可以改为当前类的类名
*/
@NonNull
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", skill=" + skill +
'}';
}
}
//方式二:浅拷贝
try {
Skill skill = new Skill("倒拔垂杨柳");
Student stu1 = new Student("魯智深",30,skill);
Object stu2 = stu1.clone();
System.out.println(stu1 == stu2);
System.out.println(stu1.toString());
System.out.println(stu2.toString());
skill.setSkillName("拳打镇关西");
stu1.setAge(33);
System.out.println(stu1.toString());
System.out.println(stu2.toString());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
存在的问题:修改完了以后2个对象的技能是完全一样的,所以浅拷贝基本数据类型可以达到完全复制,引用数据类型不可以,因为仅仅是拷贝了一份引用
方式二:深拷贝
class Skill implements Cloneable {
private String skillName;
public Skill() {
}
public Skill(String skillName) {
this.skillName = skillName;
}
public String getSkillName() {
return skillName;
}
public void setSkillName(String skillName) {
this.skillName = skillName;
}
@NonNull
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Skill{" +
"skillName='" + skillName + '\'' +
'}';
}
}
class Student implements Cloneable {
private String name;
private int age;
private Skill skill;
public Student() {
}
public Student(String name, int age, Skill skill) {
this.name = name;
this.age = age;
this.skill = skill;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Skill getSkill() {
return skill;
}
public void setSkill(Skill skill) {
this.skill = skill;
}
/**
* 1.权限修饰符更改为public
* 2.方法的返回值可以改为当前类的类名
*/
@NonNull
@Override
public Object clone() throws CloneNotSupportedException {
// return super.clone();//深拷贝不能简单的调用父类的方法
//先clone一个学生对象
Student stu = (Student) super.clone();
//调用Skill中的clone方法,克隆出一个Skill对象
Skill skill = (Skill) this.skill.clone();
//将clone出来的对象赋值给stu对象的成员变量的skill
stu.setSkill(skill);
return stu;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", skill=" + skill +
'}';
}
}
可以解决浅拷贝的局限性
3.RandomAccess标记性接口
标记接口由 List 实现使用,以表明它们支持快速(通常为恒定时间)随机访问。 此接口的主要目的是允许通用算法更改其行为,以便在应用于随机访问列表或顺序访问列表时提供良好的性能。而随机访问比顺序访问的效率要高。
随机访问:
for (int i=0, n=list.size(); i < n; i++) {
list.get(i);
}
顺序访问:
for (Iterator i=list.iterator(); i.hasNext(); ){
i.next();
}
在ArrayList中 随机访问比顺序访问更快。
实际应用:
//在遍历集合取出结果集之前面临一个问题,使用普通for遍历好 还是使用迭代器(增强for)? //特别是数据量特别大的时候一定要考虑!
//对返回的集合进行判断,如果返回的集合实现了 RandomAccess 就使用 普通for
//否则使用迭代器(增强for)
//下面的list是一个集合
if(list instanceof RandomAccess){
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}else {
for (Stutb stutb : list) {
System.out.println(stutb);
}
}
如果是ArrayList使用随机访问,如果是LinkedList就使用顺序访问。