Java23种设计模式:Iterator设计模式(三)

Iterator设计模式:不太常用,一般只用于容器。


常用容器:
ArrayList:底层数组实现的
LinkedList:底层节点实现的


分析一下ArrayList集合:
作用:用来存储数据的,任何类型的数据类型都可以存储。


大家听到这里是不是觉得和数组很像,有了数组干嘛还要集合呢。
1.数组有"长度一旦初始化确定以后就不可变","具体数组中存了多少个实际值是不知道的,只知道数组的长度,数据存了多少"


刚好ArrayList集合就弥补了这样的问题:
ArrayList中有个Object类型的数组,长度一开始可以随意定,但是一旦达到了这个数组长度,我们还想存那就有问题了,
就会数组越界,此时我们就引出了一个想法,就是能不能让数组变大。是的,就是这样,数组扩容,一旦达到数组最大长度时候,
就是开始对数组扩容。


写一个伪代码,自定义ArrayList:
public class XPArrayList{
Object[] objs = null;
//size是我们用来记录数组中存了多少个实际值
int size = 0;

public XPArrayList(){
objs = new Object[10];
}

//添加数据方法
public void add(Object o){
//首先看一下是否超过数组长度
if(size >= objs.length()){
//如果实际添加值的长度比数组长度大的时候,就需要扩容
//开始扩容,定义个新数组,扩容长度的规则随意自己定义:我在这里就让它每次扩容都用 “以前数组的长度+20” 吧;
Object[] newObjs = new Object[objs.length()+20];

//把以前数组中存储的实际数组拷贝到新数组中。
//第一个参数:原来的数组,
//第二个参数:原来数组从那个下标开始拷贝,
//第三个参数:新数组 ,
//第四个参数:从新数组的第几个下标开始存放
//第五个参数:有多少个数据需要拷贝
System.arraycopy(objs,0,newObjs,0,objs.length());

//然后把新的数组代替掉以前的数组,然后就达到扩容
objs = Objs;
}
//开始往数组中存放内容
objs[size] = o;
//记录一下存的真实数据的个数,数组长度可以用length得到,但是存了多少个数据,就用size记录
size++;
}

//该集合中有多少个真实数据
public int size(){
return size;
}
}


以上就是一个简单的ArrayList的自定义,我只写了一个add方法和size方法,其他的大家去写吧,思路已清晰了。






LinkedList底层是以节点的形式存储的,也就是当前这个节点不但存了数据对象,还存了下一个节点的引用。
查询:只要知道上一个,就可以根据下一个的节点引用找到下一个真实节点对象,依次类推,直到下个节点引用为空
添加:只要把最后一个节点的下一个引用指向你要添加的那一个真实节点即可。




写一个伪代码,自定义LinkedList:
节点类:
class Node{
Object object;
Node nextNode;
}




LinkedList类:
public class XPLinkedList {
Node lastNode = null;
Node firstNode = null;
int size = 0;

//添加
public void add(Node node){
//如果第一个节点为空,代表当前添加的是第一个节点。第一个节点和最后一个都是这一个
if (firstNode == null) {
firstNode = node;
lastNode = firstNode;
}
lastNode.nextNode = null;
lastNode = node;
size++;
}

//获取长度
public int size(){
return size;
}
}




从上面不知有没有人发现,两个都用add方法和size方法,那么,我们能不能给这两个类定义统一规范呢???
当然可以,所以我们就可以抽象出抽象类或者抽象接口:那到底是抽象类还是接口呢??
我在“面向对象设计思路”的博客中说过,定规范就用接口。
那我们就可以提出一个抽象接口:XPList


public interface XPList{
void add();
int size();
}


以上那两个类都去实现这个就可以了。然后就可以使用多态了。
说了这么多好像又回到多态了,是的,多态才是面向对象的核心。


 
*************大家注意重点来了*************
上面说了集合设计:
那我们要查看集合怎么办呢?????
当然你ArrayList用遍历数组的形式遍历,但是LinkedList却是另外的一种遍历方式。
如果又有一种集合可能又是另一种遍历,非常不兼容。这太违背程序设计思路了。


那我们就该好好想想能不能使用统一的一种遍历。当然可以,那就是迭代器。
我们就可以使用迭代器来遍历集合元素!那什么是迭代器!
1.既然说了是统一遍历,就代表是集合的统一遍历规范,根据“面向对象设计思路”,我们就把 XPIterator设计成一个接口。


public interface XPIterator{
boolean hasNext();//是否还有下一个元素的接口
Object next();//获取当前下标的数据,同时把下标下移以为,然后跑到上一个接口去判断时候还有数据
}


2.迭代器接口设计好了,因为我们的集合基本都要用到,那我们就可以吧获取迭代器放到集合父类中,也就是放到XPList接口中。
修改上面的XPList:
public interface XPList{
void add();
int size();
XPIterator iterator();
}


3.外界要遍历集合时候就可以用了
XPList xl = new XPArrayList();
xl.add("abc");
xl.add(123);
xl.add(new Car("宝马"));


XPIterator iterator = xl.iterator();
while(iterator.hasNext()){
Object o = iterator.next();
}


XPList lk = new XPLinkedLIst();
lk.add("abc");
lk.add(123);
lk.add(new Car("宝马"));


XPIterator iterator = xl.iterator();
while(iterator.hasNext()){
Object o = iterator.next();
}




4.但是此时你就可以发现我们不管是什么类型的集合都可以用迭代器遍历了,但是此时你可能也发现了
迭代器的接口方法没有任何地方实现。大家思考一下,应该在哪里现实?没错,应该在具体的集合中现实,
因为每一个集合都遍历和取值方式都不一致,所以需要每个集合自己去实现。


我们来实现一下,以ArrayList为例:
两种方式:一种内部类,一种匿名内部类
第一种:私有内部类
public class XPArrayList implements XPList{
Object[] objs = null;
//size是我们用来记录数组中存了多少个实际值
int size = 0;

public XPArrayList(){
objs = new Object[10];
}

//添加数据方法
public void add(Object o){
//首先看一下是否超过数组长度
if(size >= objs.length()){
//如果实际添加值的长度比数组长度大的时候,就需要扩容
//开始扩容,定义个新数组,扩容长度的规则随意自己定义:我在这里就让它每次扩容都用 “以前数组的长度+20” 吧;
Object[] newObjs = new Object[objs.length()+20];

//把以前数组中存储的实际数组拷贝到新数组中。
//第一个参数:原来的数组,
//第二个参数:原来数组从那个下标开始拷贝,
//第三个参数:新数组 ,
//第四个参数:从新数组的第几个下标开始存放
//第五个参数:有多少个数据需要拷贝
System.arraycopy(objs,0,newObjs,0,objs.length());

//然后把新的数组代替掉以前的数组,然后就达到扩容
objs = Objs;
}
//开始往数组中存放内容
objs[size] = o;
//记录一下存的真实数据的个数,数组长度可以用length得到,但是存了多少个数据,就用size记录
size++;
}

//该集合中有多少个真实数据
public int size(){
return size;
}

@Override
public XPIterator iterator(){
return new XPArrayListIterator();
}

private class XPArrayListIterator implements XPIterator{
//当前操作到第几个下标了
private int currentIndex = 0;

//是否还有下一个元素的接口
@Overried
public boolean hasNext(){
//如果遍历的长度大于等于真实存储值的长度时就没有元素了
if(currentIndex >= size){
return false;
}else{
return true;
}
}

//获取当前下标的数据,同时把下标下移以为,然后跑到上一个接口去判断时候还有数据
@Overried
public Object next(){
Object object = objs[currentIndex];
currentIndex++;
return object;
}
}
}




第二种:匿名内部类
public class XPArrayList implements XPList{
Object[] objs = null;
//size是我们用来记录数组中存了多少个实际值
int size = 0;

public XPArrayList(){
objs = new Object[10];
}

//添加数据方法
public void add(Object o){
//首先看一下是否超过数组长度
if(size >= objs.length()){
//如果实际添加值的长度比数组长度大的时候,就需要扩容
//开始扩容,定义个新数组,扩容长度的规则随意自己定义:我在这里就让它每次扩容都用 “以前数组的长度+20” 吧;
Object[] newObjs = new Object[objs.length()+20];

//把以前数组中存储的实际数组拷贝到新数组中。
//第一个参数:原来的数组,
//第二个参数:原来数组从那个下标开始拷贝,
//第三个参数:新数组 ,
//第四个参数:从新数组的第几个下标开始存放
//第五个参数:有多少个数据需要拷贝
System.arraycopy(objs,0,newObjs,0,objs.length());

//然后把新的数组代替掉以前的数组,然后就达到扩容
objs = Objs;
}
//开始往数组中存放内容
objs[size] = o;
//记录一下存的真实数据的个数,数组长度可以用length得到,但是存了多少个数据,就用size记录
size++;
}

//该集合中有多少个真实数据
public int size(){
return size;
}

@Override
public XPIterator iterator(){
return new XPIterator() {

//当前操作到第几个下标了
private int currentIndex = 0;

//是否还有下一个元素的接口
@Override
public boolean hasNext(){
//如果遍历的长度大于等于真实存储值的长度时就没有元素了
if(currentIndex >= size){
return false;
}else{
return true;
}
}

//获取当前下标的数据,同时把下标下移以为,然后跑到上一个接口去判断时候还有数据
@Override
public Object next(){
Object object = objs[currentIndex];
currentIndex++;
return object;
}
};
}
}


迭代器实现是需要每个集合自己现实的。




另外多提一个点:接口使用的时候尽量不要多重传递。









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值