一. 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的区别
- String: 定长字符串
- StringBuilder: 可变长字符串、线性不安全、效率高
- 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 item;
Node next;
Node prev;
Node(Node prev, E element, Node next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
三. Map
key-value型存储格式
key不可重复
3.1 HashMap
底层是一个哈希表(数组+链表)
key和value可以为null
与HashTable的区别
- HashTable是线性安全的,效率低,而HashMap线性不安全。
- 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 implements Map.Entry {
final int hash;
final K key;
V value;
Node next;
Node(int hash, K key, V value, Node 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如何判断元素是否重复的?
- 先判断hashCode
- 再使用equals判断
如果hashCode的返回值相等 并且 equals返回true,则被认为是相同的元素
源码
...
private transient HashMap 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 m;
TreeSet(NavigableMap m) {
this.m = m;
}
// 构造方法
public TreeSet() {
this(new TreeMap());
}
// 添加方法
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}