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 {
}

 基本使用实现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就使用顺序访问。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值