常见包装类、List、Set、HashMap见解

一. Integer与String

1.1 Integer

包装类,就是对基本数据类型的一个包装,赋予一些常用的方法

比如类型装换等。

int的包装类,使用final修饰类,不可继承。

会自动装箱与自动拆箱。

特别注意:在-128到127之间,维护了一个数组(static初始化,服务器启动就存在。)

比较值时,如果是这个区间,则默认从数组中拿出值来进行判断。

Integer a = 1;
Integer b = 1;
Integer c = 128;
Integer d = 128;
sout(a == b); // true
sout(c == d); // false

源码部分

 private static class IntegerCache {
     static final int low = -128;
     static final int high;
     static final Integer cache[];
    ​
     static {
         // high value may be configured by property
         int h = 127;
         String integerCacheHighPropValue =
         sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
         if (integerCacheHighPropValue != null) {
            try {
                 int i = parseInt(integerCacheHighPropValue);
                 i = Math.max(i, 127);
                 // Maximum array size is Integer.MAX_VALUE
                 h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            } catch( NumberFormatException nfe) {
             // If the property cannot be parsed into an int, ignore it.
             }
         }
         high = h;
        ​
         cache = new Integer[(high - low) + 1];
         int j = low;
         for(int k = 0; k < cache.length; k++)
         cache[k] = new Integer(j++);
        ​
         // range [-128, 127] must be interned (JLS7 5.1.7)
         assert IntegerCache.high >= 127;
 }
​
 private IntegerCache() {}
 } 
​
 public static Integer valueOf(int i) {
     if (i >= IntegerCache.low && i <= IntegerCache.high)
     return IntegerCache.cache[i + (-IntegerCache.low)];
     return new Integer(i);
 }

1.2 String

不可变长字符串。

使用String字符串拼接时,默认调用StringBulider的append方法

String str1 = "hello,";
String str2 = "dbc";
String str3 = str1 + str2;
// 实际: String str3 = (new StringBuilder()).append(str1).append(str2).toString();

String,StringBuilder,StringBuffer的区别

  1. String: 定长字符串
  2. StringBuilder: 可变长字符串、线性不安全、效率高
  3. StringBuffer: 可变长字符串、线性安全、效率低

二. List

有序,可重复

2.1 ArrayList

底层维护了一个数组

会根据存入数据个数和容量,来自动扩容

特点:查询快、添加和删除效率低(和LinkedList相比)。

源码

 transient Object[] elementData; // non-private to simplify nested class access
 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
​
 // 带参的一个构造方法
 public ArrayList(int initialCapacity) {
     if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
     } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
     } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
     initialCapacity);
     }
 }
​
 //添加的一个方法
 public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
 }
​
 // 扩容相关
 private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
 }
​
 private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
​
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
    }
 private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1); // 扩容1.5倍
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
     // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
 }

2.2 LinkedList

底层维护了一个链表

特点:查询慢、添加和删除快(和ArrayList相比)。

源码

 // 链表的节点
 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;
     }
 }

三. Map

key-value型存储格式

key不可重复

3.1 HashMap

底层是一个哈希表(数组+链表)

key和value可以为null

与HashTable的区别

  1. HashTable是线性安全的,效率低,而HashMap线性不安全。
  2. HashTable不允许值(key和value)为null,HashMap的值可以为null

源码

 /**
 * 默认容量(数组长度)
 */
 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
​
 /**
 * 最大容量
 */
 static final int MAXIMUM_CAPACITY = 1 << 30;
​
 /**
 *  Jdk1.8后,数组中某个位置的链表长度为8,并且总元素个数>=64,则将该链表编程红黑树
 *  优化查询效率
 */
 static final int TREEIFY_THRESHOLD = 8;
​
 /**
 * 最小树化容量 
 * 数组中某个位置的链表长度为8,并且总元素个数>=64,则将该链表编程红黑树
 * 优化查询效率
 */
 static final int MIN_TREEIFY_CAPACITY = 64;
​
 /**
 *  当数据个数低于这个数,将其转换为链表
 */
 static final int UNTREEIFY_THRESHOLD = 6;
​
 /**
 * 负载因子,添加元素时,当哈希表中的元素个数为:数组长度*负载因子时,会扩容
 */
 static final float DEFAULT_LOAD_FACTOR = 0.75f;
​
​
 /**
 * 基本的哈希bin节点
 */
 static class Node<K,V> implements Map.Entry<K,V> {
     final int hash;
     final K key;
     V value;
     Node<K,V> next;
    ​
     Node(int hash, K key, V value, Node<K,V> next) {
         this.hash = hash;
         this.key = key;
         this.value = value;
         this.next = next;
    }
​
     public final K getKey()        { return key; }
     public final V getValue()      { return value; }
     public final String toString() { return key + "=" + value; }
​
    public final int hashCode() {
        return Objects.hashCode(key) ^ Objects.hashCode(value);
    }
​
     public final V setValue(V newValue) {
        V oldValue = value;
         value = newValue;
        return oldValue;
     }
​
    public final boolean equals(Object o) {
        if (o == this)
         return true;
     if (o instanceof Map.Entry) {
         Map.Entry<?,?> e = (Map.Entry<?,?>)o;
         if (Objects.equals(key, e.getKey()) &&
            Objects.equals(value, e.getValue()))
             return true;
         }
        return false;
     }
 }
​

四. Set

无序、不可重复

底层维护了一个Map

存值时,将添加的值存为Map的key, 而value就是一个Object

故要获取值,只能获取迭代器来遍历

4.1 HashSet

底层维护了一个HashMap

存值时,存的为Map的key

可以添加null(HashMap的key可以为null)

HashSet如何判断元素是否重复的?

  1. 先判断hashCode
  2. 再使用equals判断

如果hashCode的返回值相等 并且 equals返回true,则被认为是相同的元素

源码

 ...
 private transient HashMap<E,Object> map;
 // Dummy value to associate with an Object in the backing Map
 // 要与支持映射中的对象关联的伪值
 private static final Object PRESENT = new Object();
 ...
 
 // 构造方法 
 public HashSet() {
    map = new HashMap<>();
 }
 ...
 
 // 添加方法 
 public boolean add(E e) {
    return map.put(e, PRESENT)==null;
 }
​

4.2 LinkedHashSet

HashSet的子类

双向链表

4.3 TreeSet

底层维护了一个TreeMap

存值时,存的为Map的key

不能添加null

源码

 private transient NavigableMap<E,Object> m;
​
 TreeSet(NavigableMap<E,Object> m) {
    this.m = m;
 }
​
 // 构造方法
 public TreeSet() {
    this(new TreeMap<E,Object>());
 }
​
 // 添加方法
 public boolean add(E e) {
    return m.put(e, PRESENT)==null;
 }
阅读 101

推荐阅读