Set与Map

本文详细介绍了Java中的Set和Map接口的实现类,如HashSet、TreeSet、HashMap和TreeMap。重点讲解了它们的底层数据结构、特点、新增功能、遍历方式以及常用方法。TreeSet基于红黑树,自动排序,而HashSet利用哈希表实现高效查询和去重。HashMap则是常用的键值对存储结构,使用哈希表并支持快速查找。此外,还对比了HashMap与Hashtable的区别,并提出了处理HashMap线程不安全问题的策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. Set
无序,不可重复|去重。

 无序: 存放的顺序与内部真实存储的顺序不一致。
 去重: 集合不包含元素对e1和e2 ,使得e1.equals(e2)和最多一个null元素。
 新增功能: static <E> Set<E> of(E... elements) 返回包含任意数量元素的不可修改集。
 遍历方式:
      foreach
      iterator迭代器
  • 常用方法:

     E ceiling(E e)       返回此set中大于或等于给定元素的 null元素,如果没有这样的元素,则 null 。
     E floor(E e)         返回此set中小于或等于给定元素的最大元素,如果没有这样的元素,则 null 。
     E first()            返回此集合中当前的第一个(最低)元素。
     E last()             返回此集合中当前的最后一个(最高)元素。
     E higher(E e)        返回此集合中的最小元素严格大于给定元素,如果没有这样的元素,则 null 。
     E lower(E e)         返回此集合中的最大元素严格小于给定元素,如果没有这样的元素,则 null 。
     E pollFirst()        检索并删除第一个(最低)元素,如果此组为空,则返回 null 。
     E pollLast()         检索并删除最后一个(最高)元素,如果此集合为空,则返回 null 。
    
  • TreeSet :

     底层结构 : 红黑树。
     特点 : 查询效率较高,自动把数据做升序排序,底层是由TreeMap维护的。
     新增功能: 新增了一些与比较大小相关的方法。
     遍历方式 :
         foreach
         iterator迭代器
    注意 : TreeSet需要存储相同类型的数据,因为会默认存在比较排序。
    
  • TreeSet存储javabean类型的数据:

     去重与排序: 都是根据比较规则实现的,与equals没有关系
     比较规则:
         1.内部比较器|内部比较规则|自然排序 : 比较规则定义在javabean类型的内部。
           方式:javabean类型实现Comparable接口,重写compareTo(T o)方法,在方法中定义比较规则。
         2.外部比较器|外部比较规则|定制排序 : 比较规则定义在javabean类型的外部。
           方式:定义一个实现类,实现Comparator接口,重写int compare(T o1, T o2),在方法中定义比较规则。
     Arrays.sort(数组) 默认升序排序:
         static <T> void sort(T[] a, Comparator<? super T> c) 
         根据指定比较器引发的顺序对指定的对象数组进行排序。
    
  • 定义自然排序方式

     两个对象 e1,e2
     e1.compareTo(e2)
     大小由返回值决定 :
         0 ---------> e1==e2
         <0 --------> e1<e2
         >0 --------> e1>e2
     TreeSet在做排序的时候: 把两个数据e1,e2调用compareTo看返回值决定大小,默认升序排序,小的放在前面,大的放在后面。
         e1-->this : 101
         e2-->o : 102
         e1.compareTo(e2)-->this.id-o.id; : -1
            升序 : e1,e2   102,102
         e1.compareTo(e2)-->o.id-this.id; : 1
            升序 : e2,e1  102,101
     注意: 
         1.TreeSet就是根据指定的比较规则,对数据做默认的升序排序。
         2.可以根据自定义的比较方式的实现,真实的实现根据指定内容升序|降序。
    
  • HashSet:

     底层结构: 哈希表(数组+链表+红黑树)。
     特点: 查询,增删效率高 ,去重,无序,底层是由HashMap维护的。
     新增方法 : 无。
     遍历: 
        foreach 
        iterator迭代器
     注意:
        此类允许null元素。
        此实现不同步。
    

2. Map
无序的,去重的。

 键值对数据的集合
    键值对->映射关系
    价值对: K-V
    K键 : 无序的,去重的|唯一的 ---> Set
    V值 : 无序的,可重复 ---> Collection
    K-V可以为任意引用数据类型。
特点:
    1.一个key只能对应一个Value。
    2.key相同value覆盖。
遍历方式:
    1.values     获取所有键值对的值。
        Collection<V> values()             返回此映射中包含的值的Collection视图。
    2.keySet     获取所有键值对的key,根据key获取value。
        Set<K> keySet()                    返回此映射中包含的键的Set视图。
    3.entrySet   获取所有的键值对,每一个键值对都是一个Entry类型->表示一个键值对。
        Set<Map.Entry<K,V>> entrySet()     返回此映射中包含的映射的Set视图。
  • 常用方法:

     V put(K key, V value)                 将指定的值与此映射中的指定键相关联(可选操作)。
     V get(Object key)                     返回指定键映射到的值,如果此映射不包含键的映射,则返回 null 。
     V remove(Object key)                  如果存在,则从该映射中移除键的映射(可选操作)。
     void clear()                          从此映射中删除所有映射(可选操作)。
     boolean isEmpty()                     如果此映射不包含键 - 值映射,则返回 true 。
     boolean containsKey(Object key)       如果此映射包含指定键的映射,则返回 true 。
     boolean containsValue(Object value)   如果此映射将一个或多个键映射到指定值,则返回 true 。
     static <K,V> Map<K,V> of(K k1, V v1)  返回包含单个映射的不可修改的映射。
     default V replace(K key, V value)     仅当指定键当前映射到某个值时,才替换该条目的条目。
     int size()                            返回此映射中键 - 值映射的数量。
    
  • TreeMap :

     底层: 红黑树。
        存储键值对类型的数据,自动升序排序,去重的。
     去重,排序: 根据键值对的key实现,与value本身无关。
     TreeSet底层是由TreeMap。
     注意:
        此实现不同步。
    
  • HashMap :

     基于哈希表的Map接口的实现,此实现提供了所有可选的映射操作,并允许null值和null键。
     HashSet底层是由HashMap。
     底层结构 : 哈希表(数组+链表+红黑树)。
    
  • 哈希表:

     数组 : 节点数组Node[] --> 要求数组的长度为2的整数次幂。
     Node : int hash,Object key,Object value,,Node next。
        每个索引位置存储的为一个单向链表的首节点(尾插法)。
        当链表的长度>8,数组的长度>64,会把链表优化成为红黑树。
        当链表的长度>8,但是数组的长度不大于64,这时候会实现扩容(数组的扩容)。
     初始容量: 哈希表中的数组默认的初始长度  16
        static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
     数组的容量最大容量 : 
        static final int MAXIMUM_CAPACITY = 1 << 30;
     加载因子: 0.75 一般不建议改变
     默认加载因子 : 
        static final float DEFAULT_LOAD_FACTOR = 0.75f;
     扩容阀值 threshold  : 扩容的临界值。
        当数据的个数size>数组的长度*加载因子时就会扩容。
     扩容机制: 原容量的2倍。
        int newCap = oldCap << 1。
     新增功能: 无
    
  • HashMap的哈希表存储数据的过程:

     1.根据key计算哈希值
         通过key的hashCode方法的返回值进一步进行hash算法的运算,得到的整数。
           int hash = (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
     2.调用putVal方法实现添加数据(hash,key,value)
         1)判断是否是第一次调用put方法做添加 if ((tab = table) == null || (n = tab.length) == 0):
             如果是第一次添加,直接调用resize()实现扩容。
         2)计算位桶的索引 int index = (n - 1) & hash。
         3)判断哈希表结构的数组table[index]是否存在数据:
             如果不存在数据,证明没有头节点,创建新节点,放入当前数组的对应索引位置作为头节点:
               table[index] = new Node<>(hash, key, value, next);
                 size数据的个数+1,判断是否>扩容的阀值:
                   如果大于需要调用resize方法进行扩容。
                   如果不大于,不需要扩容直接返回null:
                     if (++size > threshold) resize();
                     return null;
             如果存在数据,作为链表的头结点,遍历这个链表,拿到每一个节点的key与hash值判断是否与要添加的key和hash相同,如果相同,value覆盖:
                 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
                   value覆盖之后,返回被覆盖的value:
                     V oldValue = e.value;
                     e.value = value;
                     return oldValue;
    
  • Hashtable 与 HashMap之间的区别:

     共同点 : 都是Map接口的实现类,底层结构都是哈希表。
     异同点 :
         1.继承体系不同。
         2.线程是否安全不同:
             HashMap 线程不安全|不同步。
             Hashtable 线程安全的|同步的。
         3.扩容机制不同:
             HashMap扩容机制 : 每次扩容原容量的2倍。
                 int newCap = oldCap << 1
             Hashtable扩容机制 : 原容量的2倍+1。
                 int newCapacity = (oldCapacity << 1) + 1;
         4.键值对数据null值的要求不同:
             HashMap 可以存储null值的key与value
             Hashtable key与value都不为null
         5.计算hash值与位桶索引index的算法不同:
             HashMap :
                 int hash = (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
                 int index = (n - 1) & hash
            Hashtable :
                 int hash = key.hashCode();
                 int index = (hash & 0x7FFFFFFF) % tab.length;
    
  • 如何处理HashMap线程不安全问题:

      1.使用Hashtable。
      2.使用Collections工具类中static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) 返回由指定映射支持的同步(线程安全)映射。
      3.juc高级并发编程包 ConcurrentHashMap<K,V>-> 线程安全的哈希表。
    

3. Collections :
操作集合的工具类
静态工厂

   void sort(List)                   对List容器内的元素排序,排序的规则是按照升序进行排序。
   void shuffle(List)                对List容器内的元素进行随机排列。
   void reverse(List)                对List容器内的元素进行逆续排列。
   void fill(List, Object)           用一个特定的对象重写整个List容器。
   int binarySearch(List, Object)    对于顺序的List容器,采用折半查找的方法查找特定对象。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值