Java容器之List
1、Java集合概览
Java的容器有List、Queue、Set、Map等。从上图可以看出,除了以Map结尾的类之外,其他类都实现了Collection接口。以Map结尾的类实现了Map接口。
2、说一说这些容器都有什么区别?
- List:存储的元素是有序的、可重复的。
- Set:存储的元素是无序的、不可重复的。
- Queue:FIFO(先进先出)
- Map:使用键值对(key-value)存储,类似于数学上的函数 y=f(x),“x”代表 key,"y"代表 value,Key 是无序的、不可重复的;value 是无序的、可重复的,每个键最多映射到一个值。
3、List底层的数据结构
- ArrayList:Object[](数组),是List的主要实现类,线程不安全。
- Vector:Object[],是List的古老实现类。与ArrayList不同的是它支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问ArrayList慢,一般也不推荐使用。
- LinkedList:双向链表
4、ArrayList和LinkedList的区别?
、
5、ArrayList实现代码
package JavaContainer; /*ArrayList泛型类的实现*/ import java.util.Arrays; import java.util.Iterator; public class MyList<AnyType> implements Iterable<AnyType> { //list 默认初始容量 private final static int DEFAULT_CAPACITY = 10; //MyList包含元素的个数 private int theSize; //theItems数组 private AnyType[] theItems; //空数组 private static final Object[] EMPTY_ELEMENTDATA = {}; //用于默认大小的共享空数组实例,以知道在添加第一个元素时容量需要增加多少。 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //保存ArrayList数据的数组 transient Object[] elementData; //得到最小扩容量 private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { // 获取“默认的容量”和“传入参数”两者之间的最大值 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } /**判断是否需要扩容**/ private void ensureExplicitCapacity(int minCapacity) { //modCount++; 扩容次数 if (minCapacity - elementData.length > 0) //调用grow方法进行扩容,调用此方法代表已经开始扩容了 grow(minCapacity); } /**要分配的最大数组大小**/ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; /**ArrayList扩容的核心方法**/ private void grow(int minCapacity) { // oldCapacity为旧容量,newCapacity为新容量 int oldCapacity = elementData.length; //将新容量更新为旧容量的1.5倍(位运算速度快) int newCapacity = oldCapacity + (oldCapacity >> 1); //然后检查新容量是否大于最小需要容量,若还是小于最小需要容量,那么就把最小需要容量当作数组的新容量, if (newCapacity - minCapacity < 0) newCapacity = minCapacity; //再检查新容量是否超出了ArrayList所定义的最大容量,若超出了,则调用hugeCapacity()来比较minCapacity和 MAX_ARRAY_SIZE, //如果minCapacity大于MAX_ARRAY_SIZE,则新容量则为Interger.MAX_VALUE,否则,新容量大小则为 MAX_ARRAY_SIZE。 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); elementData = Arrays.copyOf(elementData, newCapacity); } //比较minCapacity和 MAX_ARRAY_SIZE private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; } //默认无参构造 public MyList() { doclear(); } //清空MyList public void clear(){ doclear(); } private void doclear() { theSize = 0; ensureCapcity(DEFAULT_CAPACITY); } //扩容,将Mylist的最大容量扩充到newCapcity public void ensureCapcity(int newCapcity) { if (newCapcity < DEFAULT_CAPACITY) return; AnyType[] old = theItems; theItems = (AnyType[]) new Object[newCapcity]; //直接定义泛型的数组是非法的,只能先用Object定义在转换 for(int i = 0; i < size(); i++) { theItems[i] = old[i]; } } //返回此list的大小 public int size() { return theSize; } //判断list是否为空 public boolean isEmpty() { return size() == 0; } //修改Mylist的容量为当前列表的大小,节省存储空间 public void trimToSize() { ensureCapcity(size()); } //查询下标为idx的值 public AnyType get(int idx){ if(idx < 0 || idx > size()) throw new ArrayIndexOutOfBoundsException(); return theItems[idx]; } //将下标为idx的元素设置为newVal,并返回覆盖前的值 public AnyType set(int idx, AnyType newVal) { if(idx < 0 || idx > size()) throw new ArrayIndexOutOfBoundsException(); AnyType old = theItems[idx]; theItems[idx] = newVal; return old; } public boolean add(AnyType x) { //一个参数的add默认添加到list最后,size()表示可以被放置的位置 add(size(), x); return true; } public void add(int idx, AnyType x){ if (idx < 0 || idx > size()) throw new ArrayIndexOutOfBoundsException(); //list的容量满了,进行扩容 if(theItems.length == size()) ensureCapcity(size() * 2 + 1); //扩容 for(int i = theSize; i > idx; i--) theItems[i] = theItems[i-1]; theItems[idx] = x; theSize++; } //将idex处的元素移除,并返回被移除的元素 public AnyType remove(int idx) { AnyType removeItem = theItems[idx]; for(int i = idx; i < size(); i++) theItems[i] = theItems[i+1]; theSize--; return removeItem; } //内部类ArrayListIterator实现了针对于MyList类的Iterator<AnyType>接口 private class ArrayListIterator implements java.util.Iterator<AnyType> { private int current = 0; //跟踪当前元素的索引 @Override public boolean hasNext() { return current < size(); } @Override public AnyType next() { if(!hasNext()) throw new java.util.NoSuchElementException(); return theItems[current++]; } public void remove() { MyList.this.remove(--current); } } @Override public Iterator<AnyType> iterator() { return new ArrayListIterator(); } public static void main(String[] args) { //test() } }
6、LinkedList的实现代码
package JavaContainer; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.NoSuchElementException; public class MyLinkedList<AnyType> implements Iterable<AnyType> { private static class Node<AnyType> { public AnyType data; public Node<AnyType> prev; //指向前一个节点的指针 public Node<AnyType> next; //指向后一个节点的指针 //构造函数 public Node(AnyType x, Node<AnyType> p, Node<AnyType> q){ data = x; prev = p; next = q; } } //MyLinkedList的数据成员 private int theSize; private int modCount = 0; //add、remove、clear时才修改 private Node<AnyType> beginMarker; //头结点,作额外的标记 private Node<AnyType> endMarker; //尾节点,作额外的标记 //无参构造 public MyLinkedList() { doclear(); } //清理 public void clear() { doclear(); } private void doclear() { //只有两个标记节点 beginMarker = new Node<AnyType>(null, null,null); endMarker = new Node<AnyType>(null, beginMarker,null); beginMarker.next = endMarker; modCount ++; theSize = 0; //这个theSize指的是有效地theSize,标记节点不算 } //获取当前MyLinkedList的大小 public int size() { return theSize; } //判断当前MyLinkedList是否为空 public boolean isEmpty() { return size() == 0; } //在尾部添加新元素x public boolean add(AnyType x) { add(size(), x); return true; } public void add(int idx, AnyType x) { Node<AnyType> p = getNode(idx, 0, size()-1); addBefore(p ,x); //先找到这个节点,然后再插入 } //已经确定插入节点的位置,插入节点 private void addBefore(Node<AnyType> p, AnyType x) { Node<AnyType> newNode = new Node<AnyType>(x, p.prev, p); newNode.prev.next = newNode; p.prev = newNode; theSize++; modCount++; } //得到idx节点处的值(而不是引用) public AnyType get(int idx) { return getNode(idx).data; } //查找(在默认范围内) private Node<AnyType> getNode(int idx) { return getNode(idx, 0 , size() - 1); } //查找(在指定范围内) private Node<AnyType> getNode(int idx, int lower, int upper) { Node<AnyType> p; if(idx < lower || idx > upper) throw new IndexOutOfBoundsException(); //分成两部分能提高查找的效率 if(idx < size() / 2) { //前半部分 p = beginMarker.next; for(int i = 0; i < idx; i++) { p = p.next; } } else { //后半部分 p = endMarker; for(int i = size(); i > idx; i--) { p = p.prev; } } return p; } public AnyType set(int idx, AnyType newVal) { Node<AnyType> p = getNode(idx); AnyType oldVal = p.data; p.data = newVal; return oldVal; } public AnyType remove(int idx) { return remove(getNode(idx)); } //remove idx处的节点,并返回删除的值 private AnyType remove(Node<AnyType> p) { p.prev.next = p.next; p.next.prev = p.prev; theSize--; modCount++; //有人可能会有疑问,没用的节点p占据的空间没有释放,注意因为JVM中有垃圾回收机制,不需要程序员手动释放 return p.data; } //MyLinkedList继承Iterable接口,重写Iterator方法 @Override public Iterator<AnyType> iterator() { return new LinkedListIterator(); } //内部类(迭代器中主要实现三种方法hasNext、next和remove) private class LinkedListIterator implements java.util.Iterator<AnyType> { private Node<AnyType> current = beginMarker.next; //当前位置 /**(之所以要记录这个是因为:为避免迭代器准备给出某一项作为下一项nextItem,而该项以后可能被删除,或者一个新的项正好插入在该项的前面,所以必须要记住这些更改法则)**/ private int expectedModCount = modCount; //检查modCount是否正确 private boolean okToRemove = false; @Override public boolean hasNext() { return current != endMarker; } @Override public AnyType next() { //返回节点的值,然后向后继续推进current if(modCount != expectedModCount) throw new ConcurrentModificationException(); if(!hasNext()) throw new NoSuchElementException(); AnyType nextItem = current.data; current = current.next; okToRemove = true; return nextItem; } public void remove(){ if(modCount != expectedModCount) throw new ConcurrentModificationException(); if(!okToRemove) throw new IllegalStateException(); MyLinkedList.this.remove(current.prev); expectedModCount++; //如果迭代器调用了自己的remove方法,那么这个迭代器仍然是合法的,因为expectedModCount同步更新了 okToRemove = false; } } public static void main(String[] args) { //test() } }
作者:Ryanjie
出处:http://www.cnblogs.com/ryanjan/
本文版权归作者和博客园所有,欢迎转载。转载请在留言板处留言给我,且在文章标明原文链接,谢谢!
如果您觉得本篇博文对您有所收获,觉得我还算用心,请点击右下角的 [推荐],谢谢!