经常使用集合和方法

经常使用集合

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  、ConcurrentSkipListMapConcurrentSkipListSetConcurrentLinkedQueue

这些集合的底层实现采用了复杂的算法,保证多线程访问集合时,既能保证线程之间的同步,又具备高效的并发性能。

  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;
}
View Code
  1. 若是数组为空,初始化,初始化完成以后,走 2;
  2. 计算当前槽点有没有值,没有值的话,cas 建立,失败继续自旋(for 死循环),直到成功,槽点有值的话,走 3;
  3. 若是槽点是转移节点(正在扩容),就会一直自旋等待扩容完成以后再新增,不是转移节点走 4;
  4. 槽点有值的,先锁定当前槽点,保证其他线程不能操做,若是是链表,新增值到链表的尾部,若是是红黑树,使用红黑树新增的方法新增;
  5. 新增完成以后 check 需不须要扩容,须要的话去扩容。

经常使用方法(Object类)

equals

  equals()方法判断两个对象是否“相等”。若是不重写这个方法,equals两个对象 除非是同一个引用,不然一直不相等(返回false)。

  Eclipse默认给咱们重写的equals()方法,是对象的全部成员变量是否都相等,若是该对象和比较对象的成员变量都相等 则两个对象互相equals() 也就是相等。

  咱们都知道Set集合存放的对象都是不可重复的,Set集合就是根据对象的equals方法判断对象是否是重复

  注意:当此方法被重写时,一般有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具备相等的哈希码。

hashCode

  返回该对象的哈希码值。支持此方法是为了提升哈希表(例如 java.util.Hashtable 提供的哈希表)的性能。

  若是根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每一个对象调用 hashCode 方法都必须生成相同的整数结果。

  若是根据 equals(java.lang.Object) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode 方法 要求必定生成不一样的整数结果。可是,程序员应该意识到,为不相等的对象生成不一样整数结果能够提升哈希表的性能。

    ——摘自API,你们也就理解了 为何eclipse中 equals和HasdCode方法给咱们的快捷键中,必须一块儿重写。

toString

   Object 类的 toString 方法返回一个字符串,该字符串由类名(对象是该类的一个实例)、at 标记符“@”和

        此对象哈希码的无符号十六进制表示组成。换句话说,该方法返回一个字符串,它的值等于

   public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode()); }

以上是JDK默认toString方法的实现和API解释,可见Object类默认的toString方法和equals方法都和对象的哈希码有关

重写toString方法可让咱们更直观的理解对象的内容属性。

getClass

反射能够拿到一个对象的成员变量的值,但方法的参数是出如今方法被调用的时候,
你拿到了class,method,参数类型等这些都没用,这些都是静态的,固定的。

也就是说反射拿不到参数的值

String StringBuffer和StringBuilder

StringBuffer表明可变的字符序列,往StringBuffer字符串加东西,直接相加。

String表明不可变字符序列  当咱们执行两个字符串相加时 须要分配另一块内存 而后 执行两次copy。最后把字符串的指针执行新的那块内存。

StringBuilder和StringBuffer相似 区别是StringBuilder线程非安全。

相关文章
相关标签/搜索