java基础整理(二)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

常见集合相关知识整理


一、Collection 和 Collections

Collection 是Java集合框架的根接口,它定义了一组通用的操作和方法,用于操作和处理一组对象。Collection接口派生出了一些具体的子接口,例如List、Set和Queue等,分别表示列表、集合和队列等不同的集合类型。
Collections 是Java中提供的一个工具类,它包含了一系列静态方法,用于对集合进行操作和处理。Collections类提供了诸如排序、搜索、遍历等常用的集合操作方法,它们都是通过静态方法实现的。

二、List集合:ArrayList 和 LinkedList

ArrayList:
底层是数组用来保存数据,没有实现Queue接口、Deque接口,不支持队列操作,
扩容机制:新建一个ArrayList,他的默认长度是10,如果长度不够会以原来长度的1.5倍扩增,有个grow方法,每一次扩原来的1.5倍,空间扩容完了之后,会调用arraycopy来对数组进行拷贝
LinkedList:
底层数据结构是链表便于进行插入删除操作 ,LinkedList 是非同步的,双向链表

三、Set集合:HashSet 和 LinkedHashSet 和 TreeSet

set:
1.不允许重复值
2.没有索引值,不能通过普通for循环进行遍历,没有 xxset.get(index) 这个方法获取值
3.使用通过给存入的数据后计算其hash值来去重
HashSet:
1.底层结构是hash表
2.不保证有序
3.没有索引,数据不可重复
LinkedHashSet:
1.数据有序,使用链表来保证
TreeSet:
1.底层是treeMap
2.存入treeSet集合数据等于存入数据到treeMap 的key值中
3.数据无序,但可排序

四、Map集合:hashMap

map:
1.用来存储键值对的集合
2.不能存储重复的键,一个键只能对应一个值
hashMap:
1.使用了数组 + 链表/红黑树 的结构存储数据,通过hash函数计算出键值映射到数组的索引位置,其中数组的每个位置称为桶,每个桶可以存储一个或者多个键值对
怎么存储键值对:将k,与原来的链表上的k进行equls 比较,如果值不一样,一次向后追加链接,如果k是一样的,覆盖原来的kv数据(jdk1.8后,链表的长度大于8 就会转成红黄树)
2.HashMap集合的默认初始化容量是16,默认加载因子是0.75
3.HashMap集合初始化容量必须是2的倍数,这也是官方推荐的,这是因为达到散列均匀,为了提高HashMap集合的存取效率,所必须的。
4.hash冲突是:不同的键通过hash函数的计算等到相同的hashcode,然后hash值将数据映射到数组的同一个索引位置就会造成hash冲突
4.1.链地址法(Chaining):
在哈希桶(数组)的每个位置上维护一个链表,每个链表节点包含冲突的键值对。当发生冲突时,将新的键值对添加到对应位置的链表上。
4.2.开放地址法:
当发生冲突时,在哈希桶中寻找下一个可用的位置来存储冲突的键值对。
常用的开放地址法有线性探测、二次探测和双重哈希等方法。
再哈希法:当发生冲突时,使用另一个哈希函数对冲突的键值重新计算哈希值,再放入哈希桶中。
hashtable(hash表):
1.线程安全,k-v均不可以为null

内部源码:

//存储k -v 的地方  Node<K,V> 
transient Node<K,V>[] table;

 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;//下个节点的内存地址

        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;
        }
    }


//计算hash 值 
static final int hash(Object key) {
        int h;
      //  (h = key.hashCode()) ^ (h >>> 16)扰动计算:把哈希值右移16位,也就正好是自己长度的一半,之后与原哈希值做异或运算,这样就混合了原哈希值中的高位和低位计算hash值增大了随机性, 减少碰撞
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

五、ConcurrentHashMap(采用了数组+链表/红黑树 + 分段锁)

1.分段锁(Segment Locking):ConcurrentHashMap引入了分段锁的概念,将整个哈希表分成多个小的段(Segment),每个段都有自己的锁。不同的线程可以同时访问不同的段,从而提供了更高的并发性能。每个段类似于一个小的HashMap,它包含一部分桶(Bucket)。
2.数组+链表/红黑树:每个段内部仍然使用数组和链表(或红黑树)的结构来存储键值对。每个桶可以存储一个或多个键值对。当链表或红黑树的长度超过一定阈值时,会进行相应的转换。
3.锁粒度:ConcurrentHashMap的锁粒度更细,每个段都有一个锁,不同的段之间可以并发操作,提高了并发性能。在读取操作时,可以并发地访问不同的段。在写入操作时,只需要锁定对应的段,而不是整个哈希表。
4.CAS操作:ConcurrentHashMap使用CAS(Compare and Swap)操作来实现线程安全的并发更新。CAS操作是一种无锁的原子操作,它允许多个线程同时尝试更新一个变量的值,只有一个线程能够成功。CAS操作可以避免使用锁带来的性能开销。
5.扩容与重新哈希:ConcurrentHashMap在扩容时,只需要对部分段进行扩容,而不是对整个哈希表进行扩容。这样可以减少扩容操作对其他线程的影响。同时,ConcurrentHashMap使用了一种优化的重新哈希算法,减少了重新哈希的时间和开销。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值