赞
踩
集合的主要作用是存储对象的容器.,常用的容器(集合)类,有ArrayList,HashMap,HashSet.等等
不同的集合容器类,他的特点不一样,或者说不同的集合类,可以基于不同的场景进行选择。集合(容器)的底层是由于数据组织的方式不一样,或者这么理解,出现不同的集合是因为他们的数据结构不一样,从而出现了不同的特性.
Java 中集合类分为2 大主要派系,Collection
和Map
我们从ArrayList 入手整理出Collection 的派系
Collection 是一个接口,是高度抽象出来的集合,它包含了集合的基本操作和
属性。Collection 包含了List 和Set 两大分支。
Collection 接口中定义了针对集合元素的基本增删查的相关操作.
增:
boolean add(E e);
boolean addAll(Collection<? extends E> c);
删:
boolean remove(Object o);
boolean removeAll(Collection<?> c);
void clear();
查:
containsAll(Collection<?> c);
实现了Iterable 接口来完成集合中元素的遍历能力
Iterator<T> iterator();
Iterator 是一个接口,它是集合的迭代器。集合可以通过Iterator 去遍历集合中的元素。Iterator 提供的API 接口,包括:是否存在下一个元素、获取下一个元素、删除当前元素
abstract boolean hasNext()
abstract E next()
abstract void remove()
我们从HashMap 入手整理出Map 派系
Map 是一个映射接口即key-value 键值对
entrySet()用于返回键-值集的Set 集合
keySet()用于返回键的Set 集合
values()用户返回值集的Collection 集合
因为Map 中不能包含重复的键;每个键最多只能映射到一个值。所以,键- 值集、键集都是Set , 值在Map 中是可以允许重复的所有值集是Collection。
V get(Object key);
V remove(Object key);
int size();
大家用HashMap 应该都有针对HashMap 的key 和value 进行遍历的操作吧。具体使用如:
hashMapObj.keySet()
hashMapObj.values()
所以整理来讲Map 派系需要依赖Collection 派系.而在我们Collection 的Set 分支中,HashSet 和TreeSet 又是依赖于HashMap 和TreeMap 进行实现(具体分析在后续Set 集合类中)
ArrayList 他的底层数据结构是以数组的方式进行组织数据的,且通过一定的算法逻辑动态扩容数组的长度,或可理解ArrayList 底层是一个动态数组。与Java中的原生的数组相比,它的容量能动态增长.
AbstractCollection
实现了Collection 中大量的函数,除了特定的几个函数iterator()和size()之外的函数
AbstractList
该接口继承于AbstractCollection,并且实现List 接口的抽象类。它实现了List 中除size()、get(intlocation)之外的函数。AbstractList 的主要作用:它实现了List 接口中的大部分函数
和AbstractCollection 相比,AbstractList 抽象类中,实现了iterator()接口
RandomAccess
RandmoAccess 接口,即提供了随机访问功能。RandmoAccess 是java 中用来被List 实现,为List 提供快速访问功能的。在ArrayList 中,我们即可以通过元素的序号快速获取元素对象;这就是快速随机访问。
有序队列接口,提供了一些通过下标访问元素的函数List 是有序的队列,List 中的每一个元素都有一个索引;第一个元素的索引值是0,往后的元素的索引值依次+1
ArrayList中部分代码如下
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;
ArrayList 包含了两个重要的对象:elementData
和size
ArrayList 对象new 的过程并不会实际的创建10 长度的Object[]用于对象的 存储,如果后续并没有使用到Arraylist对象进行操作的话,这部分的内存就是浪 费的.只有当程序第一次使用ArrayList 添加元素时,才会完成通过grow 函数完 成对对象数组的创建.主要的意义是防止内存的浪费.
快速失败机制,是java 集合类应对并发访问在对集合进行迭代过程中,内部对象结构发生变化一种防护措施.这种错误检测的机制为这种有可能发生错误, 通过抛java.util.ConcurrentModificationException.
public class ThreadAdd extends Thread { private List list; public ThreadAdd(List list) { this.list = list; } @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("loop execute :"+i); try { Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } list.add(i); } } }
public class ThreadIterate extends Thread { private List list; public ThreadIterate(List list){ this.list=list; } @Override public void run() { while(true) { for (Iterator iteratorTmp = list.iterator(); iteratorTmp.hasNext(); ) { iteratorTmp.next(); try { Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
public class FailFastTest {
private static final List<String> list = new ArrayList<String>();
public static void main(String[] args) {
new ThreadAdd(list).start();
new ThreadIterate(list).start();
}
}
最终是在这个方法中,判断预期的值和现在的值是否相等来决定是否已经failfast
我们在ArrayList 的Add会改变我们modCount
的值.
以初始长度10已满为例。
继承结构说明
队列接口
boolean offer(E e);
E poll();
E element();
E peek();
双端队列接口
void addFirst(E e);
void addLast(E e);
boolean offerFirst(E e);
boolean offerLast(E e);
E removeFirst();
E removeLast();
LinkedList 是通过双向链表去实现的,他的数据结构具有双向链表结构的优缺点,既然是双向链表,那么它的顺序访问会非常高效,而随机访问效率比较低。它包含一个非常重要的私有的静态内部类:Node。
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
Node 是双向链表节点所对应的数据结构,它包括的属性有:
初此之外还有几个重要的属性.
由于LinkedList 实现了List 的接口,所有必然具备List 的特性.那我们先来看看List 接口中定义的一个方法get(int index)和set(int index,E e);
我们都知道List 接口,ArrayList 和LinkedList 都是实现了该接口,我们也知道基于不同的数据结构通过下标寻找具体的元素.在ArrayList 和LinkedList 中效率是不一样的,那我们来分析一波LinkedList 如何通过下标寻找元素。
get(int index):
set(int index,E e):
通过上面的总结,我们发现确实针对下标位的随机访问,数组的优势很明显。
Vector 的底层与我们的ArrayList 类似.都是以动态数组的方式进行对象的存储,Vector 是线程同步操作安全的
我们去看他的源码发现很多对外的方法都是用Synchronized 关键字进行修饰的,所以通过vector 进行操作性能并不高.所以慢慢被放弃。
要保证List 接口实现的集合对象在同步操作时能够线程安全.我们可以使用下面的方式.
List list = Collections.synchronizedList(new ArrayList());
源码分析:
最终都是返回SynchronizedList 对象
Set
Set 是继承于Collection 的接口。它是一个不允许有重复元素的集合
SortedSet
有序的集合,即SortedSet 中的元素是按指定排序规则排序的排序,定义了一个内部的成员作为排序的比较器(Comparator)
AbstractSet
是一个抽象类,它继承于AbstractCollection,AbstractCollection 实现了Set 中的绝大部分函数,为Set 的实现类提供了便利
NavigableSet
NavigableSet 接口,意味着它支持一系列的导航方法。比如查找与指定目标最匹配项
TreeSet 的实现在我们认识完TreeMap 之后就简单多了.
TreeSet 底层的实现依赖的TreeMap 集合[参见Map>>深入TreeMap]
PRESENT 对象就是每一个TreeMap 元素的Value , key 就是set 中的元素,Value 为固定的
PRESENT 值
TreeMap 的底层数据结构为红黑树(RB TREE) 结构.
TreeMap 的继承结构:
AbstractMap
继承于Map 的抽象类,它实现了Map 中的大部分API。其它Map 的实现类可以通过继承AbstractMap 来减少重复编码
SortedMap
有序的键值对集合接口,即SortedMap 中的内容是排序的键值对,定义了一个
内部的成员作为排序的比较器(Comparator)
NavigableMap
是继承于SortedMap 的接口。相比于SortedMap,NavigableMap 有一系列的导航方法;如"获取大于/等于某对象的键值对"、“获取小于/等于某对象的键值对”等等
红黑树5 大规则:
树形结构保证了TreeMap 的有序.
在二叉树的结构中,我们数据遵循的原则是:
递归比对每一个节点和将插入的数据,若数据比节点的内容小,往该节点的左边进行递归.直到找到元素的存放点.所以我们可以通过红黑树或者说二叉树的特性来完成TreeMap 的有序性.
Entry 对象的结构
采用数据+链表或者数组+红黑树方式进行元素的存储
存储在hashMap 集合中的元素都将是一个Map.Entry 的内部接口的实现当数组的下标位是链表时,此时存储在该下标位置的内容将是Map.Entry 的一个实现Node 内部类对象当数组的下标位是红黑树时,此时存储在该下标位置的内容将是Map.Entry 的一个实现TreeNode 内部类对象
如果我只按照原来的hashcode 值进行计算的话,那么参与计算的位数太少了.比如长度为16.参与计算的值就可以hashcode 的最后4 位.下标分布的情况可能会出现不均衡…所以hashmap 的设计者希望让更多的位数来参与我们的计算.将hashcode 进行高低十六位的异或运算.
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。