数据结构 二 :列表
一。先扯点别的。今日在TX微博上一则消息传的沸沸扬扬,说是‘TX DNF游戏团队成员年终奖为48个月工资,其中甚至是客服最低年薪也达到了90万’,在群里讨论的一发不可收拾,其中既引来了多数身为其中一员的无限自豪感,也招来了无数板砖,很快,TX人力部高层也对此做了澄清,然后又是巴拉巴拉巴拉,总之最后是不了了之,对于此事我没多大感冒,不管是真是假,踏实做事才是真!二。再扯点别的。
(1)在一篇技术博客上看到一些技术知识点。说:程序运行其实就是数据串的不断拷贝,比如当你写了一段代码功能是输出‘123 ,把代码文件保存至硬盘,则运行程序效果为:数据串‘123 ’先从硬盘拷贝至主存即内存,再从内存拷贝至高速缓存如L1,L2,再从高速缓存拷贝至CPU中,再从CPU拷贝至显示器。到此结束,整个过程就是一个拷贝过程,其中时间与空间消耗都花在了拷贝上,所以作为程序员可以做的就是在拷贝上做文章,即在高速缓存上进行优化。
(2)一直觉得迅雷的技术是很牛逼牛逼的,原因是去年参加了迅雷的笔试,基本不会,很是被鄙视了一把,所以这两天也稍微看了一点比如断点下载的一些博客,至于BT下载,去年有了解了一下,貌似是一个p2p协议的应用(不是很记得了),在这里稍微说一下断点下载的思路:有服务器S,客户端C,C向S下载文件,C先生成一个文件Info以记录如需下载的文件的字节大小,是否下载完成的标志位以及已经下载到C的文件字节数,当C暂停了下载,待到下次下载,S先查看C端的Info,然后再根据根据Info的信息进行交互。思路大抵如此,不过貌似代码实现起来可没那么简单。
三。言归正传。
(1)列表即List,在java有ArrayList及LinkedList,而谈到这两个类,最先想到了就是他们的对比(去年为找工作这类笔试题做了不少,而且惊人的相似),即ArrayList底层实现是数组,增加或删除元素消耗大,但查询元素速度快,而LinkedList底层实现为链表,增加或删除元素消耗小,但查询元素速度慢。
(2)对于List的了解仅限于以上,而其真正的原理尚未了解,今天的目标就是初步实现List的两个子类ArrayList和LinkedList;
四。还是废话少说。直接贴代码:
(1)ArrayList
package com.ds.test4;import java.util.Arrays;public class ArrayList<E> {private int index = -1; //索引初始为-1private int capacity = 10; //容量初始为10E[] os = null;public ArrayList(){ //无参构造方法this(10); //容量初始为10}public ArrayList(int initCapacity){ //自行设置初始容量if(initCapacity<=0)throw new RuntimeException("初始容量必须大于0");capacity = initCapacity;os = (E[]) new Object; //初始数组}public synchronized boolean add(E o){checkLength();index++; //索引增加os = o; //赋值return true;}public synchronized E get(int where){ //pop操作checkIndexAndWhere(where);E oo = (E) os; //赋值返回值return oo;}public synchronized boolean set(int where,E o){ //set操作 checkIndexAndWhere(where); //检查索引os = o; //赋值return true;}public synchronized E remove(int where){ //remove操作checkIndexAndWhere(where); //检查索引E oo = (E) os; //赋值返回值E[] newOs = (E[]) new Object;for(int i=0;i<where;i++){newOs = os; //依次赋值}for(int i=where;i<os.length-1;i++){newOs = os; //依次赋值}os = newOs;index--; return oo;}private void checkLength() {if(index>=capacity-1){ //超过容量capacity+=10; //每次递增10E[] newOs = (E[]) new Object;for(int i=0;i<os.length;i++)newOs = os; //依次赋值os = newOs;}}private void checkIndexAndWhere(int where){ //检查索引if(index == -1){ //没有任何元素时抛出异常 throw new RuntimeException("不存在元素");}if(where <0 || where>index){//索引有误时抛出异常throw new RuntimeException("索引有误"); }}public int size(){return index+1;}public String toString(){return Arrays.toString(os);}}
(2)LinkedList
package com.ds.test4;public class LinkedList<E> {private Node<E> node = null; //头结点private int index = 0; //索引private int nextIndex = 0; //用来next的一个索引private Node<E> nextNode = null;public LinkedList(){node = new Node<E>(); //初始化头结点}public synchronized boolean add(E e){ //add操作if(index == 0){node.addNext(new Node(e));index++;}else{Node<E> newNode = new Node<E>(e);Node<E> lastNode = getLastNode();lastNode.addNext(newNode);index++;}return true;}public E get(int where){E e = null;if(where >= index || where < 0){ //索引越界throw new RuntimeException("索引越界-"+where);}else{Node<E> tempNode = node; //把头结点赋值给一个暂时头结点,以防直接操纵头结点for(int i = 0;i<=where;i++){tempNode = tempNode.getNext();}e = tempNode.getData();tempNode = null; //适当考虑一下内存}return e; //我感觉是返回E,而不是一个Node}public E next(){if(hasNext()){if(nextNode == null){nextNode = node.getNext();nextIndex++;}else{nextNode = nextNode.getNext();nextIndex++;}}else{throw new RuntimeException("没有元素");}return nextNode.getData(); //我感觉是返回E,而不是一个Node}public synchronized boolean remove(int where){Node previous = null; //取得whrere前一个结点Node next = null; //取得whrere后一个结点if(where >= index || where < 0){ //索引越界throw new RuntimeException("索引越界-"+where);}else{Node<E> tempNode = node; //把头结点赋值给一个暂时头结点,以防直接操纵头结点for(int i = 0;i<=where-1;i++){ //取的where-1,即where前一结点,即previoustempNode = tempNode.getNext();}previous = tempNode; //取得whrere前一个结点Node current = previous.getNext(); //取得whrere当前结点next = current.getNext(); //取得whrere前一个结点previous.addNext(next); //前一结点直接与后一结点相连current = null; //where结点 设为空index -- ; //即当前尺寸减一}return true;}public synchronized boolean set(int where,E e){if(where >= index || where < 0){ //索引越界throw new RuntimeException("索引越界-"+where);}else{Node<E> tempNode = node; //把头结点赋值给一个暂时头结点,以防直接操纵头结点for(int i = 0;i<=where;i++){ tempNode = tempNode.getNext();}Node current = tempNode;current.setData(e);}return true;}public synchronized boolean insert(int where,E e){Node current = null; //取得whrere当前结点Node next = null; //取得whrere后一个结点if(where >= index || where < 0){ //索引越界throw new RuntimeException("索引越界-"+where);}else{Node<E> tempNode = node; //把头结点赋值给一个暂时头结点,以防直接操纵头结点for(int i = 0;i<=where;i++){ tempNode = tempNode.getNext();}current = tempNode; //取得whrere当前结点Node insertNode = new Node(e);//构造要插入的Nodecurrent.addNext(insertNode);//使current的Node指向新的Nodeif(where != index){//当where等于index时将会是直接插入到链表最后,所以不存在next这个Nodenext = current.getNext();//取得whrere后一个结点insertNode.addNext(next);//使新的Node指向next的Node}index ++ ; //即当前尺寸加一}return true;}public boolean hasNext(){return (index==0?false:true) && (index>nextIndex);}private Node<E> getLastNode() {Node<E> tempNode = node; //把头结点赋值给一个暂时头结点,以防直接操纵头结点 for(int i = 0;i<index;i++){tempNode = tempNode.getNext();}return tempNode;} public int size(){return index;}}class Node<E>{private Node<E> next = null;private E data = null;public Node(){}public Node(E newData){data = newData;}public void addNext(Node<E> newNode){next = newNode;}public Node<E> getNext(){return next;}public E getData(){return data;}public void setData(E data){this.data = data;}}
五。sun的ArrayLIst和LinkedList源码就不贴了,在这里简单说几个问题:
(1)发现sun的这两个类没有使用synchronized关键字,猛然想起来,原来Vector与Stack已比较少使用,原因就是涉及的多线程问题,因为考虑到安全问题,则势必会降低效率,而且也确实一般情况下不会说向列表中插入元素会有先后问题的考虑。
(2)在这次编码中有一个循环赋值问题,即使用for循环依次赋值,而sun源码使用的是System.arraycopy,一个方法就搞定,貌似以前也了解了下深复制与浅复制的概念,这个问题在此做一标注,留待以后学习。
六。说明:
(1)ArrayList稍微简单一些,其中checkLength和checkIndexAndWhere这两个方法是核心,主要是涉及索引的越界判断。
(2)LinkedList稍微复杂一些,主要还是之前对链表只是一个模糊的了解,当真正自己用代码实现起来,却也有不少麻烦事,在这说几点对链表的理解:<1>一个链表包括一个头结点
<2>而结点其实是对你要加入元素的一个封装(这里使用的内部类,技术上没什么说明,只是对于Node与LinkedList关系的一个反映)
<3>结点(Node),包括指针域(next,即下一个结点的引用)和数据域(data,即真正加入的元素)
(3)LinkedList各方法的实现无非是围绕索引对应的结点,即previous(前一结点),next(后一结点)和current(当前结点)。
七。总结:
可能很多大牛或者内行看来我这些代码甚于简单而且不够严谨,不过还是那句话:权当是一个笔记,记录一个学习的过程,以后回头看来,能有所收获就够了,打住,明日继续。
页:
[1]