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 @Override16 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 @Override34 public boolean hasNext() {35 if(currentIndex>=index){36 37 return false;38 }39 else {40 return true;41 }42 }43 44 @Override45 public Object next() {46 Object object=objects[currentIndex];47 currentIndex++;48 return object;49 }50 }51 52 @Override53 public Iterator iterator() {54 55 return new ArrayListIterator();56 }57 58 }