Java集合之ArrayList和LinkedList的实现原理

ArrayList实现可变数组的原理:

  当元素超出数组内容,会产生一个新数组,将原来数组的数据复制到新数组中,再将新的元素添加到新数组中。

  ArrayList:是按照原数组的50%来延长,构造一个初始容量为10的空列表

用ArrayList模拟数组:

 1 package iterater.patten.design;
 2
 3 //探索ArrayList实现的可变数组的原理,用ArrayList实现一个容器存储对象
 4 public class ArrayList {
 5     Object[] objects = new Object[10];
 6     // 定义计数器,用于计算数组中的元素个数
 7     int index = 0;
 8
 9     public void add(Object o) {
10         // 当数组满时,则创建一个新的数组,将原数组中的元素复制进新数组中,再将新的元素加入到数组中
11         if (index == objects.length) {
12             // 按原数组的2倍长度创建新数组,其实这样不太合理
13             Object[] newObjects = new Object[objects.length * 2];
14             // 将原数组中的元素复制进新数组中,再将新的元素加入到数组中
15             System.arraycopy(objects, 0, newObjects, 0, objects.length);
16             // 数组引用指向新的数组
17             objects = newObjects;
18         }
19
20         // 将新增元素放到数组中
21         objects[index] = o;
22         index++;
23     }
24
25     // 定义size函数获取元素个数
26     public int size() {
27         return index;
28     }
29 }

  用LinkedList模拟数组

 1 package iterater.patten.design;
 2
 3 //探索LinkedList实现的可变数组的原理,用LinkedList实现一个容器存储对象
 4 public class LinkedList {
 5
 6     //定义链表的头指针head以及尾指针tail
 7     Node head = null;
 8     Node tail = null;
 9     int size = 0;
10
11     //添加元素
12     public void add(Object o) {
13         //一个新的结点
14         Node n = new Node(o, null);
15         //当链表为空时,head指向新添加的结点,tail也指向该结点
16         if (head == null) {
17             head = n;
18             tail = n;
19         }
20         //链表不为空时,tail包含的下一个结点的引用指向这个新加入的结点
21
22         tail.setNext(n);
23         tail = n;
24         size++;
25     }
26
27     public int size() {
28         return size;
29     }
30 }

  Node结点的类定义 

 1 package iterater.patten.design;
 2
 3 //定义一个类来存储链表中的结点
 4 public class Node {
 5
 6     private Object data;
 7     private Node next;
 8     public Object getData() {
 9         return data;
10     }
11     public void setData(Object data) {
12         this.data = data;
13     }
14     public Node getNext() {
15         return next;
16     }
17     public void setNext(Node next) {
18         this.next = next;
19     }
20     public Node(Object data, Node next) {
21         super();
22         this.data = data;
23         this.next = next;
24     }
25
26 }

  添加的元素对象所属的类的类定义

 1 package iterater.patten.design;
 2
 3 public class Cat {
 4
 5     private int id;
 6
 7     public int getId() {
 8         return id;
 9     }
10
11     public void setId(int id) {
12         this.id = id;
13     }
14
15     public Cat(int id) {
16         super();
17         this.id = id;
18     }
19 }

  测试类 

 1 package iterater.patten.design;
 2
 3 import iterater.patten.design.*;
 4
 5 public class IteratorTest {
 6
 7     /**
 8      * @param args
 9      */
10     public static void main(String[] args) {
11
12         // ArrayList al=new ArrayList();
13         LinkedList al = new LinkedList();
14         for (int j = 0; j < 15; j++) {
15             al.add(new Cat(j));
16         }
17         System.out.println(al.size());
18     }
19
20 }

  输出结果:15

【温情提示】:我们在测试类中为了提高容器的可替换性,可以定义一个接口Collection,定义add、size方法,只要保证容器类实现该接口,当用户使用add添加元素或者使用size获取元素个数的时候就可以更加方便(因为如果ArrayList中的添加元素方法叫add,而LinkedList中添加元素的方法叫addall,用户在使用的时候就会造成困扰,使用定义接口的方式,我们只对接口进行编程使用,不用去关心具体的实现内容。

  代码实现:

1 public interface Collection {
2
3     public Object add(Object o);
4     public int size();
5 }

  ArrayList和LinkedList实现该接口,并覆盖抽象方法即可,测试类中可以这样使用两个方法:

  Collection c=new ArrayList();

  c.add(object)、c.size();

  父接口引用指向子类对象,并调用子类中覆盖的方法

  但是此时又出现了一个新的问题,当我们想要对数组中的元素进行遍历时,因为不同的集合有不同的遍历方法,用ArrayList模拟的数组可以通过数组的下标索引进行遍历,但是LinkedList使用的确实另外一种方法。

针对这样的问题,解决的方法就是定义Iterator接口,里面封装了遍历数组元素的统一方式,话不多说,代码来验证。

  定义两个接口Collection和Iterator

1 public interface Collection {
2
3     Iterator iterator();
4 }
1 public interface Iterator {
2
3     public boolean hasNext();
4     public Object next();
5 }

  不同的容器实现Iterator接口,获取具体的迭代器对象(即该容器类型的对象)

 1     //具体的实现类,不同的容器,拥有不同的迭代元素的方法
 2     private class ArrayListIterator implements Iterator{
 3         private int currentIndex=0;
 4         @Override
 5         public boolean hasNext() {
 6             if(currentIndex>=index){
 7
 8                 return false;
 9             }
10             else {
11                 return true;
12             }
13         }
14
15         @Override
16         public Object next() {
17             Object object=objects[currentIndex];
18             currentIndex++;
19             return object;
20         }
21     }

  只要ArrayList实现我前面自定义的Collection接口,覆盖iterator方法,就可以获取一个具体的实现类的对象

public class ArrayList implements Collection

1 public Iterator iterator() {
2
3         return new ArrayListIterator();
4     }

  此时测试类可以这样写

 1 package iterater.patten.design;
 2
 3 import iterater.patten.design.*;
 4
 5 public class IteratorTest {
 6
 7     /**
 8      * @param args
 9      */
10     public static void main(String[] args) {
11
12         ArrayList al = new ArrayList();
13         for (int j = 0; j < 15; j++) {
14             al.add(new Cat(j));
15         }
16         Iterator it = al.iterator();
17         while (it.hasNext()) {
18             Object object = it.next();
19             System.out.print(object + " ");
20         }
21         System.out.println();
22     }
23
24 }

  迭代器Iterator的实现原理大致就是这样,尽管不同的集合内部的数据结构不同,统一了遍历集合的方式。

  最后附上ArrayList的类的代码:

 1 package iterater.patten.design;
 2
 3
 4 //探索ArrayList实现的可变数组的原理,用ArrayList实现一个容器存储对象
 5 public class ArrayList implements Collection{
 6     Object[] objects = new Object[10];
 7     // 定义计数器,用于计算数组中的元素个数
 8     int index = 0;
 9
10     public void add(Object o) {
11         // 当数组满时,则创建一个新的数组,将原数组中的元素复制进新数组中,再将新的元素加入到数组中
12         if (index == objects.length) {
13             // 按原数组的2倍长度创建新数组,其实这样不太合理
14             Object[] newObjects = new Object[objects.length * 2];
15             // 将原数组中的元素复制进新数组中,再将新的元素加入到数组中
16             System.arraycopy(objects, 0, newObjects, 0, objects.length);
17             // 数组引用指向新的数组
18             objects = newObjects;
19         }
20
21         // 将新增元素放到数组中
22         objects[index] = o;
23         index++;
24     }
25
26     // 定义size函数获取元素个数
27     public int size() {
28         return index;
29     }
30     //不同的容器,拥有不同的迭代元素的方法
31     private class ArrayListIterator implements Iterator{
32         private int currentIndex=0;
33         @Override
34         public boolean hasNext() {
35             if(currentIndex>=index){
36
37                 return false;
38             }
39             else {
40                 return true;
41             }
42         }
43
44         @Override
45         public Object next() {
46             Object object=objects[currentIndex];
47             currentIndex++;
48             return object;
49         }
50     }
51
52     @Override
53     public Iterator iterator() {
54
55         return new ArrayListIterator();
56     }
57
58 }

时间: 2024-10-13 18:05:18

Java集合之ArrayList和LinkedList的实现原理的相关文章

java集合(ArrayList,Vector,LinkedList,HashSet,TreeSet的功能详解)

说起集合,我们会潜意识里想到另外一个与之相近的名词——数组,OK!两者确实有相似之处,但也正是这点才是我们应该注意的地方,下面简单列出了两者的区别(具体功能的不同学习这篇文章后就会明白了): 数组 长度固定 既可以存储基本数据类型,也能存储引用数据类型 一个数组中的元素类型必一致 集合 长度可变 只能存储引用数据类型 一个集合中的元素类型可以是任意的引用类型 一.集合概述 Collection<E> 父接口 List<E> 子接口 ArrayList<E>类 Vecto

Java中针对 ArrayList和LinkedList 的区别

一般大家都知道ArrayList和LinkedList的大致区别:      1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构.      2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针.      3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据. ArrayList和LinkedList是两个集合类,用于存储一系列的对象引用

从源码看Java集合之ArrayList

Java集合之ArrayList - 吃透增删查改 从源码看初始化以及增删查改,学习ArrayList. 先来看下ArrayList定义的几个属性: private static final int DEFAULT_CAPACITY = 10; private static final Object[] EMPTY_ELEMENTDATA = {}; private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; tra

深入Java集合学习系列:HashMap的实现原理

参考文献 引用文献:深入Java集合学习系列:HashMap的实现原理,大部分参考这篇博客,只对其中进行稍微修改 自己曾经写过的:Hashmap实现原理 1. HashMap概述: HashMap是基于哈希表的Map接口的非同步实现(Hashtable跟HashMap很像,唯一的区别是Hashtalbe中的方法是线程安全的,也就是同步的).此实现提供所有可选的映射操作,并允许使用null值和null键.此类不保证映射的顺序,特别是它不保证该顺序恒久不变. 2. HashMap的数据结构: 在ja

深入Java集合学习系列:LinkedHashMap的实现原理

1. LinkedHashMap概述: LinkedHashMap是Map接口的哈希表和链接列表实现,具有可预知的迭代顺序.此实现提供所有可选的映射操作,并允许使用null值和null键.此类不保证映射的顺序,特别是它不保证该顺序恒久不变.   LinkedHashMap实现与HashMap的不同之处在于,后者维护着一个运行于所有条目的双重链接列表.此链接列表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序.   注意,此实现不是同步的.如果多个线程同时访问链接的哈希映射,而其中至少一个线

转:深入Java集合学习系列:HashSet的实现原理

0.参考文献 深入Java集合学习系列:HashSet的实现原理 1.HashSet概述: HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持.它不保证set 的迭代顺序:特别是它不保证该顺序恒久不变.此类允许使用null元素.HashSet中不允许有重复元素,这是因为HashSet是基于HashMap实现的,HashSet中的元素都存放在HashMap的key上面,而value中的值都是统一的一个private static final Object PRESENT

Java自学-集合框架 ArrayList和LinkedList的区别

ArrayList和LinkedList的区别 步骤 1 : ArrayList和LinkedList的区别 ArrayList ,插入,删除数据慢 LinkedList, 插入,删除数据快 ArrayList是顺序结构,所以定位很快,指哪找哪. 就像电影院位置一样,有了电影票,一下就找到位置了. LinkedList 是链表结构,就像手里的一串佛珠,要找出第99个佛珠,必须得一个一个的数过去,所以定位慢 步骤 2 : 插入数据 package collection; import java.u

Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例

概要  前面,我们已经学习了ArrayList,并了解了fail-fast机制.这一章我们接着学习List的实现类——LinkedList.和学习ArrayList一样,接下来呢,我们先对LinkedList有个整体认识,然后再学习它的源码:最后再通过实例来学会使用LinkedList.内容包括:第1部分 LinkedList介绍第2部分 LinkedList数据结构第3部分 LinkedList源码解析(基于JDK1.6.0_45)第4部分 LinkedList遍历方式第5部分 LinkedL

Java集合类库 ArrayList 源码解析

集合类库是Java的一个重大突破,方便了我们对大数据的操作.其中 Arrays 和 Collections 工具类可以帮助我们快速操作集合类库.下面对Java集合类库的源码分析是基于jdk1.7的.今天我们来看看ArrayList的底层实现原理. ArrayList的继承结构图 继承自 AbstractList 抽象类,在上层是 AbstractCollection 抽象类,直接去 AbstractCollection 类去看看. AbstractCollection 类主要实现了 Collec