ArrayList源码分析一

前言

我们知道如果在一个已知数组 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异常。

克隆的前提条件

  1. 被克隆对象所在的类必须实现Cloneable接口
  2. 必须重写Cloneable方法
public interface Cloneable {
}

 基本使用

 @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);

    }

可以看出克隆出来的内存地址不一样,集合内容一样

案例:已知对象A的姓名为豹子头林冲,年龄为30,需求是将对象A的数据复制给对象B,并且此后对象A和B的数据互不影响

方式一:传统方式

  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);

方式二:浅拷贝

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 + '\'' +
                '}';
    }
}
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就使用顺序访问。

猜你喜欢

转载自blog.csdn.net/jingerlovexiaojie/article/details/109034301