sethtml
Set不按特定方式进行排序,而且没有重复的对象,它的有些实现类能对集合中的对象按照特定的顺序排序。主要有两个实现类:HashSet和TreeSetjava
HashSet按照哈希算法来存取集合中的对象,存取速度比较快。程序员
TreeSet实现了SortSet接口,具备排序功能。算法
Listapi
List主要特征是以线性方式存储,集合中容许存放重复对象主要实现类包括ArrayList和LinkList数组
ArrayList表明长度可变的数组,容许对元素进行快速的随机访问,可是向ArrayList中插入与删除元素较慢。安全
LinkList在实现中采用链表的结构,插入和删除元素较快,随机访问则相对较慢。多线程
LinkList单独具备addFirst(),addLast(),getFirst(),getLast(),removeFirst(),removeLast()等方法这些方法使得LinkList能够做为堆栈,队列和双向队列使用并发
Queue框架
Queue队列中的对象按照先进先出的规则来排列。在队列末尾添加元素,在队列的头部删除元素。能够有重复对象。
双向队列Deque则容许在队列的尾部和头部添加和删除元素。
由于LinkList能够做为双向队列使用,因此Queue和Deque使用比较少
Map
HashMap不保证映射的顺序,特别是它不保证该顺序恒久不变。(也就是无序)
TreeMapTreeMap基于红黑树(Red-Black tree)的 NavigableMap
实现,有序。
HashMap和TreeMap容许使用 null 值和 null 键 。
HashMap是使用hashCode和equals方法去重的。而TreeMap依靠Comparable或Comparator来实现key去重
在java集合框架中,Set List Queue Map的实现类都没有采起同步机制。在单线程环境中,这种实现方式会提升操纵集合的效率,java虚拟机没必要会由于管理用不锁而产生额外的开销。
在多线程环境中,可能会有多个线程同时操纵一个集合,好比一个线程在为集合排序,而另一个线程正不断的向集合中添加新的元素。
为了不并发问题,能够直接采用java.util.concurrent并发包提供线程安全的集合。列如
ConcurrentHashMap 、ConcurrentSkipListMap、ConcurrentSkipListSet和ConcurrentLinkedQueue。
这些集合的底层实现采用了复杂的算法,保证多线程访问集合时,既能保证线程之间的同步,又具备高效的并发性能。
Hashtable:
Hashtable,用做键的对象必须实现 hashCode
方法和 equals
方法。容器使用synchronized来保证线程安全,但在线程竞争激烈的状况下Hashtable的效率很是低下。由于当一个线程访问Hashtable的同步方法时,其余线程访问Hashtable的同步方法,就可能会进入阻塞或轮询状态。
如线程1使用put进行添加元素,线程2不但不能使用put方法添加元素,而且也不能使用get方法来获取元素,因此竞争越激烈效率越低。
历史集合类 :Vector Hashtable Stack Enumeration 在实现中都采用了同步机制,并发性能较差,所以不提倡使用它们。
ConcurrentHashMap:
尽管全部操做都是线程安全的,但获取操做不 必锁定,而且不 支持以某种防止全部访问的方式锁定整个表。
容许经过可选的 concurrencyLevel 构造方法参数(默认值为 16)来引导更新操做之间的并发,该参数用做内部调整大小的一个提示——摘自API
final V putVal(K key, V value, boolean onlyIfAbsent) { if (key == null || value == null) throw new NullPointerException(); //计算hash int hash = spread(key.hashCode()); int binCount = 0; for (Node<K,V>[] tab = table;;) { Node<K,V> f; int n, i, fh; //table是空的,进行初始化 if (tab == null || (n = tab.length) == 0) tab = initTable(); //若是当前索引位置没有值,直接建立 else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { //cas 在 i 位置建立新的元素,当 i 位置是空时,即能建立成功,结束for自循,不然继续自旋 if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null))) break; // no lock when adding to empty bin } //若是当前槽点是转移节点,表示该槽点正在扩容,就会一直等待扩容完成 //转移节点的 hash 值是固定的,都是 MOVED else if ((fh = f.hash) == MOVED) tab = helpTransfer(tab, f); //槽点上有值的 else { V oldVal = null; //锁定当前槽点,其他线程不能操做,保证了安全 synchronized (f) { //这里再次判断 i 索引位置的数据没有被修改 //binCount 被赋值的话,说明走到了修改表的过程里面 if (tabAt(tab, i) == f) { //链表 if (fh >= 0) { binCount = 1; for (Node<K,V> e = f;; ++binCount) { K ek; //值有的话,直接返回 if (e.hash == hash && ((ek = e.key) == key || (ek != null && key.equals(ek)))) { oldVal = e.val; if (!onlyIfAbsent) e.val = value; break; } Node<K,V> pred = e; //把新增的元素赋值到链表的最后,退出自旋 if ((e = e.next) == null) { pred.next = new Node<K,V>(hash, key, value, null); break; } } } //红黑树,这里没有使用 TreeNode,使用的是 TreeBin,TreeNode 只是红黑树的一个节点 //TreeBin 持有红黑树的引用,而且会对其加锁,保证其操做的线程安全 else if (f instanceof TreeBin) { Node<K,V> p; binCount = 2; //知足if的话,把老的值给oldVal //在putTreeVal方法里面,在给红黑树从新着色旋转的时候 //会锁住红黑树的根节点 if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key, value)) != null) { oldVal = p.val; if (!onlyIfAbsent) p.val = value; } } } } //binCount不为空,而且 oldVal 有值的状况,说明已经新增成功了 if (binCount != 0) { // 链表是否须要转化成红黑树 if (binCount >= TREEIFY_THRESHOLD) treeifyBin(tab, i); if (oldVal != null) return oldVal; //这一步几乎走不到。槽点已经上锁,只有在红黑树或者链表新增失败的时候 //才会走到这里,这二者新增都是自旋的,几乎不会失败 break; } } } //check 容器是否须要扩容,若是须要去扩容,调用 transfer 方法去扩容 //若是已经在扩容中了,check有无完成 addCount(1L, binCount); return null; }
equals()方法判断两个对象是否“相等”。若是不重写这个方法,equals两个对象 除非是同一个引用,不然一直不相等(返回false)。
Eclipse默认给咱们重写的equals()方法,是对象的全部成员变量是否都相等,若是该对象和比较对象的成员变量都相等 则两个对象互相equals() 也就是相等。
咱们都知道Set集合存放的对象都是不可重复的,Set集合就是根据对象的equals方法判断对象是否是重复
注意:当此方法被重写时,一般有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具备相等的哈希码。
返回该对象的哈希码值。支持此方法是为了提升哈希表(例如 java.util.Hashtable
提供的哈希表)的性能。
若是根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每一个对象调用 hashCode
方法都必须生成相同的整数结果。
若是根据 equals(java.lang.Object)
方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode 方法不 要求必定生成不一样的整数结果。可是,程序员应该意识到,为不相等的对象生成不一样整数结果能够提升哈希表的性能。
——摘自API,你们也就理解了 为何eclipse中 equals和HasdCode方法给咱们的快捷键中,必须一块儿重写。
Object
类的 toString
方法返回一个字符串,该字符串由类名(对象是该类的一个实例)、at 标记符“@
”和
此对象哈希码的无符号十六进制表示组成。换句话说,该方法返回一个字符串,它的值等于
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
以上是JDK默认toString方法的实现和API解释,可见Object类默认的toString方法和equals方法都和对象的哈希码有关
重写toString方法可让咱们更直观的理解对象的内容属性。
反射能够拿到一个对象的成员变量的值,但方法的参数是出如今方法被调用的时候,
你拿到了class,method,参数类型等这些都没用,这些都是静态的,固定的。
也就是说反射拿不到参数的值
StringBuffer表明可变的字符序列,往StringBuffer字符串加东西,直接相加。
String表明不可变字符序列 当咱们执行两个字符串相加时 须要分配另一块内存 而后 执行两次copy。最后把字符串的指针执行新的那块内存。
StringBuilder和StringBuffer相似 区别是StringBuilder线程非安全。