2021.2.8随笔
1.Queue
队列
Queue接口
(1)它是 Collecton 的一个队列子接口(定义了队列的概念)
(2)Queue 实现不允许插入 null 元素(LinkedList 除外)
// 如果使用 queue 定义的 poll 方法在队列中删除元素
// 如果队列中已经没有元素可以删除,那么会返回一个 null 值作为标记
Deque
(1)它是 queue 的一个子接口,它是双端队列子接口
(2)不仅可以作为双端队列使用,还可以充当一个普通队列和栈
(3)不允许存储 null
ArrayDeque
(1)它是双端队列的数组实现,
(2)底层是个数组, 是一个循环存储的数组
(3)默认初始容量(16), 扩容机制: 2倍
(4)有序, 允许重复元素, 不允许 null( poll 无元素可删除的时候, 返回值是null, 为了避免混淆)
(5)线程不安全
BlockingQueue
(1)它是 queue 接口的阻塞队列接口
(2)阻塞队列:限定容量队列,如果队列满了,阻塞添加线程,如果队列空了阻塞删除线程
(3)不允许null
(4)阻塞方法:
Put(): 添加方法, 没有位置可以添加, 线程阻塞, 当有位置可以添加的时候, 唤醒添加阻塞线程
Take(): 删除方法, 阻塞
Offer(e, time, unit)// 添加的元素, 时间, 时间单位
// 如果本身有存储空间, 直接添加, 如果没有添加的空间, 在多少时间内是阻塞的, 超过阻塞时间, 会转化为特殊值情况
Poll(time, unit)//
// 如果有元素可以出队列, 直接出, 如果没有元素可以出队列, 等待, 知道超时, 转化为特殊值情况
2.Map
Map 接口
(1)Map是Map集合体系的顶级接口
(2)Map所存储的数据, key-value结构的数据, 键值对
(3)Map的key不允许’’重复’’:
键值对
Key-Value 结构的数据,一个 Key 对应着一个 Value
Key-value 数据具有自我描述性( 用 key 来描述 value)
api
这条最重要。使用频率最高。
V get(Object key)
返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。
void clear()
从此映射中移除所有映射关系(可选操作)。
boolean equals(Object o)
比较指定的对象与此映射是否相等。
int hashCode()
返回此映射的哈希码值。
boolean isEmpty()
如果此映射未包含键-值映射关系,则返回 true。
int size()
返回此映射中的键-值映射关系数。
V put(K key, V value)
将指定的值与此映射中的指定键关联(可选操作)。
void putAll(Map<? extends K,? extends V> m)
从指定映射中将所有映射关系复制到此映射中(可选操作)。
V remove(Object key)
如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。
boolean containsKey(Object key)
如果此映射包含指定键的映射关系,则返回 true。
boolean containsValue(Object value)
如果此映射将一个或多个键映射到指定值,则返回 true。
Set<Map.Entry<K,V>> entrySet()
返回此映射中包含的映射关系的 Set 视图。
Set<K> keySet()
返回此映射中包含的键的 Set 视图。
Collection<V> values()
返回此映射中包含的值的 Collection 视图。
3.HashMap
最最重要的来了!!!!!!
(1)hashmap的底层结构 : 数组 + 链表 + 红黑树 (jdk 1.8)
(jdk1.8之前: 没有红黑树, 就是数组 + 单链表)
(2)hashmap底层维护了一个数组
(3) 数组默认初始容量16 , 数组扩容机制: 扩为原来的2倍, 默认的加载因子是0.75
(4)无序
(5)不允许重复的key: 详谈
(6)允许存储null键, 0
(7)线程不安全
(8)Hashmap能存储的元素数量 < 底层数组长度 * 加载因子的
(9)hashmap怎么判断重复的问题:
// (hash是否一样 && (key直接相等 || key相equals ))
Hash = (h = key.hashCode()) ^ (h >>> 16)
程序题:
/*
"aababcabcdabcde",获取字符串中每一个字母出现的次数
要求结果:a(5)b(4)c(3)d(2)e(1)
*/
public class work1 {
public static void main(String[] args) {
HashMap<Character, Integer> hashMap = new HashMap<>();
String s = "aababcabcdabcde";
for (char s1 : s.toCharArray()) {
Integer i = hashMap.get(s1);
if (i == null) hashMap.put(s1, 1);
if (i != null) {
i++;
hashMap.put(s1, i);
}
}
System.out.println(hashMap.keySet());
System.out.println(hashMap.values());
}
}
/*
给定一个整数数组和一个目标值,找出数组中和为目标值的两个数, 返回它们的索引。
你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。
比如:nums = [2, 7, 11, 15], target = 9.
因为 nums[0] + nums[1] = 2 + 7 = 9. 所以返回 [0, 1].
*/
public class work2 {
public static void main(String[] args) {
int[] nums = {2, 7, 11, 15};
int target = 9;
System.out.println(findTarget(nums, target));
}
// public static String findTarget(int[] nums, int target) {
// int i, j = 0;
// for (i = 0; i < nums.length; i++) {
// for (j = i + 1; j < nums.length; j++) {
// if (nums[i] + nums[j] == target && i != j) {
// return Arrays.toString(new int[]{i, j});
// }
// }
// }
// return null;
// System.out.println(hashMap.keySet());
// System.out.println(hashMap.values());
//}
public static String findTarget(int[] nums, int target) {
Map<Integer, Integer> hashMap = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
hashMap.put(nums[i], i);
}
for (int i = 0; i < nums.length; i++) {
int a = target - nums[i];
if(hashMap.containsKey(a)){
return "["+hashMap.get(nums[i])+","+hashMap.get(a)+"]";
}
}
return null;
}
}
还有一些源码分析
ArrayList 原码分析
// 1, 它是双端队列的数组实现,
// 2, 底层是个数组, 是一个循环存储的数组
// 3, 初始容量, 扩容机制
// 4, 有序(它是队列 线性表 线性表必定有序),
// 允许重复元素, 不允许null(poll无元素可删除的时候, 返回值是null, 为了避免混淆)
// 5, 线程不安全
ArrayDeque<String> deque = new ArrayDeque<>();
class ArrayDeque{
transient Object[] elements;// ArrayDeque 的底层数组
public ArrayDeque() {
elements = new Object[16];
}
public boolean add(E e) {
addLast(e);
return true;
}
public void addLast(E e) {
if (e == null) throw new NullPointerException();
elements[tail] = e;
if ( (tail = (tail + 1) & (elements.length - 1)) == head)
doubleCapacity();// 扩容方法
}
private void doubleCapacity() {
assert head == tail;
int p = head;
int n = elements.length;
int r = n - p; // number of elements to the right of p
int newCapacity = n << 1;
if (newCapacity < 0)
throw new IllegalStateException("Sorry, deque too big");
Object[] a = new Object[newCapacity];
System.arraycopy(elements, p, a, 0, r);
System.arraycopy(elements, 0, a, r, p);
elements = a;
head = 0;
tail = n;
}
}
HashMap的源码分析
// 2, hashmap底层维护了一个数组
HashMap<String, String> map = new HashMap<>();
map.put("zs", "18");
// 3, 数组初始容量16 , 数组扩容机制
// 4, 无序
// 5, 不允许重复的key:
// 6, 允许存储null键, 0
// 7, 线程不安全
class HashMap{
transient Node<K,V>[] table;// hashmap底层所维护的数组, 是一个Node类型的数组
static final float DEFAULT_LOAD_FACTOR = 0.75f;
final float loadFactor;// 加载因子(饱和度)
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR;
}
// zs 18
public V put(K key, V value) {
return putVal( hash(key) , key, value, false, true);
}
// zs
static final int hash(Object key) {
int h;
// key = null ---> 0
// key != null ---->
// h >>> 16: 为什么要移动它, 又运算
// hash希望尽可能的散列
// 010101010010101
// 0000000 0101010
// 这个操作就是为了让key充分散列
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
// key的hash值 zs 18
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
Node<K,V>[] tab; // 数组
Node<K,V> p; // 结点
int n,i;
// tab = table: table?就是hash的底层数组
// tab = null
if ((tab = table) == null || (n = tab.length) == 0)
// tab = resize(): 扩容方法
// n = 16
n = (tab = resize()).length;
// i = (n - 1) & hash: 取模
// tab[i] : key值经过hash计算, 又经过取模之后的对应数组下标位置
// p = tab[i]
if ((p = tab[i = (n - 1) & hash]) == null)
// 如果这个散列位置, 没有内容, 存进去
tab[i] = newNode(hash, key, value, null);
else {
}
Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
return new Node<>(hash, key, value, next);
}
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;// hash值
final K key;// key
V value;// value
Node<K,V> next;// 下一个结点的指向
}
int threshold;// 标记阈值 = 数组长度 * 加载因子
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
final Node<K,V>[] resize() {
// oldTab = table: 底层数组 , 此时此刻null
Node<K,V>[] oldTab = table;
// oldCap = 0; 旧容量0
int oldCap = (oldTab == null) ? 0 : oldTab.length;
// oldThr = threshold = 0: 阈值是0
int oldThr = threshold;
// 新长度, 新阈值
int newCap, newThr = 0;
//
if (oldCap > 0) {
}
else if (oldThr > 0) // initial capacity was placed in threshold
else {
// newCap = 16
newCap = DEFAULT_INITIAL_CAPACITY;
// newThr = 0.75 * 16 = 12
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {
}
// threshold = 12
threshold = newThr;
// 创建一个数组: 16长度数组
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
// table = 16长度数组
table = newTab;
}
}
HashMap 的扩容:的源码分析
class HashMap{
transient Node<K,V>[] table;// hashmap底层所维护的数组, 是一个Node类型的数组
int size; // 这个hashmap存储的键值对数目
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
// ......
// 上面的逻辑处理添加元素
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
static final int MAXIMUM_CAPACITY = 1 << 30;
final Node<K,V>[] resize() {
// oldTab = table;// 长度为16的数组
Node<K,V>[] oldTab = table;
// oldCap = 16// 旧数组容量
int oldCap = (oldTab == null) ? 0 : oldTab.length;
// oldThr = 12// 旧阈值
int oldThr = threshold;
// 新长度, 新阈值
int newCap, newThr = 0;
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
}
// newCap = 2旧容量 = 32
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
// newThr = 2旧阈值 = 24
newThr = oldThr << 1;
}
else if (oldThr > 0)
else {
}
if (newThr == 0) {
}
// threshold = 24
threshold = newThr;
// 创建了一个长度为32的数组
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
// 把这个数组, 复制给底层数组
table = newTab;
}
}
HashMap源码分析: 重复元素
到底什么是重复元素
class HashMap{
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
// 散列的位置没有元素的逻辑
tab[i] = newNode(hash, key, value, null);
else {
// 散列位置必定有元素
Node<K,V> e;
K k;
// (h = key.hashCode) ^ (h >>> 16): 有可能导致, 两个完全不同的对象hash值一样
// p.hash == hash : 1, 两个key-value数据的, hash值是否一样
// (k = p.key) == key || (key != null && key.equals(k))
// key是否直接相等 或者 相equals
// (hash是否一样 && (key直接相等 || key相equals ))
// 如果判断条件为真: 把新值 替换 旧值
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
}
HashMap的源码分析: toString()
class HashMap extends AbstractMap{
}
class AbstractMap{
public String toString() {
Iterator<Entry<K,V>> i = entrySet().iterator();
if (! i.hasNext())
return "{}";
StringBuilder sb = new StringBuilder();
sb.append('{');
for (;;) {
Entry<K,V> e = i.next();
K key = e.getKey();
V value = e.getValue();
sb.append(key == this ? "(this Map)" : key);
sb.append('=');
sb.append(value == this ? "(this Map)" : value);
if (! i.hasNext())
return sb.append('}').toString();
sb.append(',').append(' ');
}
}
}