参考 尚学堂 Java 300 集点击打开链接 和一些文档 点击打开链接
概念:
一 、什么是 List
List 是有序、可重复的容器。
有序:List 中每个元素都有索引标记。可以根据元素的索引标记(在List中的位置)访问元素,从而精确控制这些元素。
可重复:List 允许加入重复的元素。更确切地讲,List通常允许满足 e1.equals(e2) 的元素重复加入容器(set就不行,可以将set理解为数学的集合)
List 接口常用的三个实现类 ArrayList、LinkedList、Vector。这三个都满足上面的两个条件
二、List 的基本功能(常用的一些方法)
三、ArrayList、LinkedList、Vector 的特点与不同
ArrayList 底层实现是数组进行存储,因此特点是查询快,增/删操作慢,线程不安全
LinkedList 底层实现是链表进行存储,因此特点是查询慢,增/删操作快,线程不安全
Vector 底层是数组实现的 List ,相关的方法增加了同步检查(synchronized),因此线程安全,效率低
因此运用时,需要线程安全,就用 Vector
不需要考虑线程安全,但是查询较多,使用 ArrayList
不需要考虑线程安全,但是增/删较多,使用 LinkedList
四、代码实现(简单的功能,里面有bug)
ArrayList:
package cn.swu.stormliu.test; public class MyArrayList { int size; Object value[]; public MyArrayList() { this(10); } public MyArrayList(int initCapacity) { if(initCapacity<0) { try { throw new Exception(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } value=new Object[initCapacity]; } public void add(Object obj) { ensureCapacity(); value[size]=obj; size++; } public void ensureCapacity() { if(size==value.length) { Object newArrayList []=new Object [size*2+1]; System.arraycopy(value, 0, newArrayList, 0, value.length ); value=newArrayList; } } public boolean isEmpty() { return size==0; } public boolean remove(Object obj) { if(obj==null) { for(int i=0;i<size;i++) { if(value[i]==null) { fastRemove(i); return true; } } }else { for(int i=0;i<size;i++) { if(obj.equals(value[i])) { fastRemove(i); return true; } } } return false; } public void remove(int index) { rangeCheck(index); fastRemove(index); } public void fastRemove(int index) { int numMoved=size-index-1; if(numMoved>0) System.arraycopy(value, index+1, value, index, numMoved); value[--size]=null; } public Object get(int index) { rangeCheck(index); return value[index]; } private void rangeCheck(int index) { if(index<0||index>=size) { try { throw new Exception(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public Object set(int index,Object obj) { rangeCheck(index); Object oldValue = value[index]; value[index] = obj; return oldValue; } public void add(int index,Object obj) { rangeCheck(index); ensureCapacity(); System.arraycopy(value, index, value, index+1, size-index); value[index]=obj; size++; } public static void main(String[] args) { MyArrayList list=new MyArrayList(4); list.add("456"); list.add("adsf"); list.add("sdf"); list.add("s678"); list.set(3, "sajdfl"); list.add(1, "123465"); for(int i=0 ;i<list.size;i++) { System.out.println(list.get(i)); } } }
运行结果:
ArrayList 每次的增/删操作都需要将原来数组的插入点的后面全部复制一遍,因此,代价很高,
但是查询,只需要将地址传入,很快便能计算出地址,直接查找
LinkedList:
定义链表的节点:
class Node{ Node previous; Object obj; Node next; public Object getPrevious() { return previous; } public Node(Node previous, Object obj, Node next) { super(); this.previous = previous; this.obj = obj; this.next = next; } public Object getObj() { return obj; } public void setObj(Object obj) { this.obj = obj; } public Node getNext() { return next; } public void setNext(Node next) { this.next = next; } public void setPrevious(Node previous) { this.previous = previous; } public Node() { } }
链表的插入:
1、找到插入的位置的节点 temp,获取上一个节点的信息 up
2、将上一个节点的next 指定为新节点,将新节点的 previous 指定为上一个节点
3、将新节点的 next 指向temp;temp的previous 指向新节点
4、将传入的数据存储到新节点的 obj
public void add(int index,Object obj) { rangeCheck(index); Node temp=node(index); //用来存储临时节点 if(temp!=null) { Node newNode=new Node();//创建新节点 newNode.obj=obj; Node up=temp.previous; //找到插入节点的上一个节点 并命名为up up.next=newNode; newNode.previous=up; newNode.next=temp; temp.previous=newNode; size++; } }
链表的删除:
1、找到删除的节点temp(b),获取上一个节点信息 up (a),下一个节点信息 down(c)
2、将up 的 next 指向 down,将down 的previous 指向 up
public void remove(int index) { rangeCheck(index); Node temp=node(index); if(index<size-1) { if(temp!=null) { Node up=temp.previous; Node down=temp.next; up.next=down; down.previous=up; size--; } }else { if(temp!=null) { Node up=temp.previous; Node down=null; up.next=null; last=up; size--; } } }
LinkedList 基本功能和主程序:
public class MyLinkedList { private Node first; private Node last; private int size; public void add(Object obj) { Node n=new Node(); if(first==null) { n.setObj(obj); n.setPrevious(null); n.setNext(null); first=n; last=n; }else { n.setPrevious(last); n.setObj(obj); n.setNext(null); last.setNext(n); last=n; } size++; } public int size() { return size; } public void rangeCheck(int index) { if(index<0||index>=size) { try { throw new Exception(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public Object get(int index) { rangeCheck(index); Node temp=node(index) ; if(temp!=null) { return temp.obj; } return null; } public Node node(int index) { Node temp=null; if(first!=null) { temp=first; for(int i=0;i<index;i++) { temp=temp.next; } } return temp; } public void remove(int index) { rangeCheck(index); Node temp=node(index); if(index<size-1) { if(temp!=null) { Node up=temp.previous; Node down=temp.next; up.next=down; down.previous=up; size--; } }else { if(temp!=null) { Node up=temp.previous; Node down=null; up.next=null; last=up; size--; } } } public void set(int index,Object obj) { rangeCheck(index); Node temp=node(index); if(temp!=null) { Node newnode=new Node(); newnode.obj=obj; Node up=temp.previous; up.next=newnode; newnode.previous=up; Node down=temp.next; down.previous=newnode; newnode.next=down; } } public void add(int index,Object obj) { rangeCheck(index); Node temp=node(index); if(temp!=null) { Node newNode=new Node(); newNode.obj=obj; Node up=temp.previous; up.next=newNode; newNode.previous=up; newNode.next=temp; temp.previous=newNode; size++; } } public static void main(String[] args) { MyLinkedList list =new MyLinkedList(); list.add("aaa"); list.add("bbb"); list.add("5645"); list.add(1,"1564798"); for(int i=0;i<list.size;i++) System.out.println(list.get(i)); } }
运行结果:
链表的 增/删 不涉及数据的移动,只需要将指向指定好即可,因此代价很小。但是查找需要一个一个遍历
(可以比较查找的index 和 链表的 size/2 进行比较 ,大于则从 last 遍历,反之则从 first遍历)。