
public final class String
implements java.io.Serializable, Comparable, CharSequence {
private final char value[];
private int hash; // Default to 0
private static final long serialVersionUID = -6849794470754667710L;
private static final ObjectStreamField[] serialPersistentFields =
new ObjectStreamField[0];
public String() {
this.value = new char[0];
}
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
}
(2)String对象的值为什么不可被修改?
可能会觉得是因为char数组用final关键字修改,可是这只是说明数组不能被修改而数组中的值是可以修改的。事实上修改char数组的值有4种方法:第一种如果数组是public修饰的则可以直接通过对象访问并修改,第二种通过String提供的诸如set等方法修改,第三种通过继承String类在子类中修改,第四种通过反射进行修改。事实上前三种方法都被java的api开发者堵死了。
第一种:数组是private修饰的无法直接访问。
第二种:String类并没有提供可以修改char数组的方法。
第三种:String类时final进行修改的无法继承。
(3)String对象的两种创建方式
<1>使用双引号“ ”创建:使用双引号创建时创建的是常量,存放在常量池中。
<2>使用new关键字创建:使用new关键字创建时创建的是对象,存放在堆中。
使用new关键字创建有三种:第一种使用无参数构造函数,此时的字符串为空。第二种有参构造函数,参数为一个String对象,这时会将传入String对象的数组和hash值赋值给创建的对象。第三种有参构造函数,参数为一个char数组,这时会将传入的char数组赋值给被创建的对象。
(4)两个字符串对象是否相等
在此之前我们需要知道一些变量的存放位置:
栈:存放基本类型的变量数据和对象的引用
堆:存放使用new创建的对象
常量池:存放字符串常量和基本类型常量
静态于:存放静态成员(static定义的
注意用==判断是,其实是判断这两个字符串是否是一个对象。
下面的代码中会返回false,这是因为二者并不是同一个对象。在前面我们也介绍了使用new关键字时会重新创建一个对象,这两个当然不是一个对象。需要注意的是hash值相等两个对象可能不是通过一个对象,能够判断两个对象是否是同一个只能看这两个对象地址是否相同。
String a = "hello";
String b = new String("hello");
System.out.println(a == b);
返回true,使用""创建对象只会创建一次。其实这个很好理解,如果创建两次,怎么区分“hello”与“hello”呢。
String a = "hello"; String b = "hello"; System.out.println(a == b);
返回false,很显然二者不是同一个对象。
String a = new String(new char[]{'h','e','l','l','o'});
String b = new String(new char[]{'h','e','l','l','o'});
System.out.println(a == b);
下面比较复杂,其实我们用==判断两个对象是否相等(引用指向的地址)时,如果这两个对象的值相等并且都存放在常量池中那这两个对象一定相等。可以简单点记,如果+两边是常量,并且常量是"*"表示的字符串或者常量指向的是存放在常量池中的字符串,那新创建的字符串对象一定也存放在常量池中,否则不存放在常量池中。
String a = "hello";
String b = "world";
String c = a + b;
String d = "hello" + "world";
System.out.println(c == d);
//返回false
final String a = "hello";
final String b = "world";
String c = a + b;
String d = a + b;
System.out.println(c == d);
//返回true
String c = "hello" + "world";
String d = "hello" + "world";
System.out.println(c == d);
//返回true
final String a = new String("hello");
final String b = new String("world");
String c = a + b;
String d = a + b;
System.out.println(c == d);
//返回false
public StringBuilder() {
super(16);
}
public StringBuilder(int capacity) {
super(capacity);
}
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
(2)扩容:直接扩容为原来容量的两倍,如果不够大则设置容量为需要容量的大小。
java集合分为两部分:Map(以键值对形式存放元素),Collection(集合,以元素形式存放数据)
一、Map
NodenewNode(int hash, K key, V value, Node e) { LinkedHashMap.Entry p = new LinkedHashMap.Entry (hash, key, value, e); linkNodeLast(p); return p; }
(2)删除时维护链表:HashMap使用remove方法移除数据,remove调用了removeNode,removeNode调用了afterNodeRemoval方法。LinkedHashMap重写了afterNodeRemoval方法,该方法维护了删除后节点后链表的操作。
void afterNodeRemoval(Nodee) { // unlink LinkedHashMap.Entry p = (LinkedHashMap.Entry )e, b = p.before, a = p.after; p.before = p.after = null; if (b == null) head = a; else b.after = a; if (a == null) tail = b; else a.before = b; }
(3)维护链表的顺序:LinkedHashMap支持两种链表排序规则,FIFO(默认顺序,插入顺序,最近插入的在链表尾部),LRU(最近访问的在链表尾部)。accessOrder用来控制使用哪种规则排序,默认为false使用FIFO,如果为true则使用LRU。主要使用下面两种方法来维护链表的顺序
void afterNodeAccess(Nodep) { } //访问时调用,插入时键已经存在(如果不存在,FIFO和LRU插入的位置都在链表尾部) void afterNodeInsertion(boolean evict) { } //插入时调用
当访问一个节点时,如果accessOrder = true并且被访问节点不在链表尾部则将被访问节点移到链表尾部。
void afterNodeAccess(Nodee) { // move node to last LinkedHashMap.Entry last; if (accessOrder && (last = tail) != e) { LinkedHashMap.Entry p = (LinkedHashMap.Entry )e, b = p.before, a = p.after; p.after = null; if (b == null) head = a; else b.after = a; if (a != null) a.before = b; else last = b; if (last == null) head = p; else { p.before = last; last.after = p; } tail = p; ++modCount; } }
当插入一个节点后,执行afterNodeInsertion方法,如果evict(accessOrder的值)为true,链表不为空,removeEldestEntry(first)返回true,则删除链表头部节点。removeEldestEntry(first)方法默认返回false。
void afterNodeInsertion(boolean evict) { // possibly remove eldest
LinkedHashMap.Entry first;
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
removeNode(hash(key), key, null, false, true);
}
}
(4)利用LinkedHashMap实现LRU 缓存:我们只需要继承LinkedHashMap,设置accessOrder为true,然后重写removeEldestEntry(first)方法,当容量大于设定的值时返回true。
class LRUCacheextends LinkedHashMap { private static final int MAX_ENTRIES = 3; protected boolean removeEldestEntry(Map.Entry eldest) { return size() > MAX_ENTRIES; } LRUCache() { super(MAX_ENTRIES, 0.75f, true); } }
final V putVal(K key, V value, boolean onlyIfAbsent) {
//键和值不能为空
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
int binCount = 0;
for (Node[] tab = table;;) {
Node f; int n, i, fh;
//数组为空初始化数组
if (tab == null || (n = tab.length) == 0)
tab = initTable();
//对应数组的位置为空,使用CAS方法添加
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null,
new Node(hash, key, value, null)))
break; // no lock when adding to empty bin
}
//如果其他线程在扩容则一起参与扩容
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
//否则对待插入元素对应的链表的表头节点进行加锁
V oldVal = null;
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node 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 pred = e;
if ((e = e.next) == null) {
pred.next = new Node(hash, key,
value, null);
break;
}
}
}
else if (f instanceof TreeBin) {
Node p;
binCount = 2;
if ((p = ((TreeBin)f).putTreeVal(hash, key,
value)) != null) {
oldVal = p.val;
if (!onlyIfAbsent)
p.val = value;
}
}
}
}
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(1L, binCount);
return null;
}
private final ReferenceQueue
WeakHashMap使用expungeStaleEntries来删除被加入到que中的节点,这个方法会在resize(),put(),get()方法中被间接调用。
private void expungeStaleEntries() {
//逐个处理对que中的Entry
for (Object x; (x = queue.poll()) != null; ) {
synchronized (queue) {
@SuppressWarnings("unchecked")
Entry e = (Entry) x;
int i = indexFor(e.hash, table.length);
Entry prev = table[i];
Entry p = prev;
while (p != null) {
//从Hash表中删除对应的Entry
Entry next = p.next;
if (p == e) {
if (prev == e)
table[i] = next;
else
prev.next = next;
// Must not null out e.next;
// stale entries may be in use by a HashIterator
e.value = null; // Help GC
size--;
break;
}
prev = p;
p = next;
}
}
}
}
static final class Entryimplements Map.Entry { K key; V value; Entry left; Entry right; Entry parent; boolean color = BLACK; }
(2)排序规则:默认比较两个键,也可以自己指定排序规则。
public Comparator> getComparator() { // Adapt or create a key-based comparator if (tree.comparator != null) { return Map.Entry.comparingByKey(tree.comparator); } else { return (Comparator > & Serializable) (e1, e2) -> { @SuppressWarnings("unchecked") Comparable super K> k1 = (Comparable super K>) e1.getKey(); return k1.compareTo(e2.getKey()); }; } }
(3)执行put:先找到待插入Entry的位置,然后插入。
public V put(K key, V value) {
Entry t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry parent;
// split comparator and comparable paths
Comparator super K> cpr = comparator;
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
else {
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable super K> k = (Comparable super K>) key;
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
Entry e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
(3)get:类似于二叉查找树的查找过程
final EntrygetEntry(Object key) { // Offload comparator-based version for sake of performance if (comparator != null) return getEntryUsingComparator(key); if (key == null) throw new NullPointerException(); @SuppressWarnings("unchecked") Comparable super K> k = (Comparable super K>) key; Entry p = root; while (p != null) { int cmp = k.compareTo(p.key); if (cmp < 0) p = p.left; else if (cmp > 0) p = p.right; else return p; } return null; } (4)键能否为null:如果用户自定义了比较器那键可以为null,如果使用默认的比较器则键不能为null。
二、Collections
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
public class HashSetextends AbstractSet implements Set , Cloneable, java.io.Serializable { static final long serialVersionUID = -5024744406713321676L; 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; } public boolean remove(Object o) { return map.remove(o)==PRESENT; } public boolean contains(Object o) { return map.containsKey(o); } }
public PriorityQueue(int initialCapacity,
Comparator super E> comparator) {
// Note: This restriction of at least one is not actually needed,
// but continues for 1.5 compatibility
if (initialCapacity < 1)
throw new IllegalArgumentException();
this.queue = new Object[initialCapacity];
this.comparator = comparator;
}
(1)容量:默认为11
(2)扩容:如果容量小于64则容量为原来容量的2倍加1,否则近似于原来容量的1.5倍
private void grow(int minCapacity) {
int oldCapacity = queue.length;
// Double size if small; else grow by 50%
int newCapacity = oldCapacity + ((oldCapacity < 64) ?
(oldCapacity + 2) :
(oldCapacity >> 1));
// overflow-conscious code
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
queue = Arrays.copyOf(queue, newCapacity);
}
(3)offer:将添加的元素放在堆顶,原来堆顶放在堆的末尾,从末尾向上调节堆,如果当前元素比父节点小则交换二者。
(4)poll:返回堆顶,将堆尾元素放在堆顶,然后从上向下调节。