Java集合 Map 集合 与 操作集合的工具类: Collections 的详细说明

news2024/11/15 17:20:37

Java集合 Map 集合 与 操作集合的工具类: Collections 的详细说明

在这里插入图片描述


每博一文案

别把人生,输给心情
师父说:心情不是人生的全部,却能左右人生的全部。
你有没有体会到,当你心情好的时候,生活仿佛阳光灿烂,顺风顺水,
当你心情不好的时候,似乎周围的一切都糟糕透了。
有时候,我们不是输给了别人,而是败给了坏心情的自己。
人活着就像一个陀螺,为了生活不停的转动,永远都有忙不完的事。
有时候又像沙漠中的骆驼,背负着重担努力地前行,却不知道哪里才是终点。
先现在情绪低落,只是因为陷进了自我纠缠的陷阱,等到熬过了这段苦难,
你会发现你所纠结的东西,真的只是无关痛痒的小事。
生活就像天气,不会总是晴天,也不会一直阴雨,喜欢和讨厌是次要的,关键是你要学会调整自己。
心静了,才能听见自己的心声,心清了,才能照见万物的本性。
假如任由坏情绪累积和蔓延,很多事只会变得越来越糟糕,
既然做不到让所有人都满意,为何不努力让自己开心?
生活是你自己的,喜怒悲欢都由你自己决定,记得别被坏情绪束缚住,
不要让你的人生,输给了心情。
                                      ——————   一禅心灵庙语

文章目录

  • Java集合 Map 集合 与 操作集合的工具类: Collections 的详细说明
    • 每博一文案
    • 1. Map接口概述
    • 2. Map接口:常用方法
    • 3. Map实现类之一:HashMap
      • 3.1 HashMap的存储结构
      • 3.2 HashMap源码中的重要常量
      • 3.3 HashMap的存储结构:JDK 1.8之前 / JDK 1.8之后
        • 3.3.1 JDk 1.8 之前
        • 3.3.2 JDk 1.8 及之后
        • 3.3.3 JDK8 HashMap 集合添加元素的过程
        • 3.3.4 JDK8 HashMap 进行 "扩容"和 "树形化"
        • 3.3.5 总结:JDK1.8 相较于之前的变化:
    • 4. Map实现类之二:LinkedHashMap
    • 5. Map实现类之三:TreeMap
    • 6. Map实现类之四:Hashtable
    • 7. Map实现类之五:Properties
    • 8. Map 接口下的集合遍历方式
    • 9. Collections工具类
      • 9.1 Collections常用方法
    • 10. 总结:
    • 11. 最后:

1. Map接口概述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • Map 接口与 Collection 并列存在的,用于保存具有映射关系的数据:key-value 被称为 键值对

  • Java集合可分为 Collection 和 Map 两种体系。

    • Collection 接口:单例数据,定义了存取一组对象的方法的集合。
      • List : 元素有序,可重复的集合。
      • Set :元素无序,不可重复的集合。
    • Map 接口:双列数据,保存具有映射关系**”key-value对“** 的集合。
  • Map 中的 keyvalue 都可以是任何引用类型的数据。

    • keyvalue 都是引用数据类型,都是存储对象的内存地址的。不是基本数据类型。
    • 其中 key 起到主导地位,valuekey 的一个附属品。
  • Map 中的 keySet 集合存储的,不允许重复。即同一个 Map 对象所对应的类,必须重写hashCode() 和 equals() 方法。但是其中的 value 值是可以存储重复的数据的。而 value 值则是被 Collection 接口集合存储的。

  • 常用 String 类作为 Map 的 ”键“。

  • keyvalue 之间存在单向一对一关系,即通过指定的 key 总能找到唯一的,确定的 value

  • Map 接口的常用实现类:

    • HashMap 作为Map的主要实现类,线程不安全的,效率高,可以存储 null 的key 和 value。HashMap是 Map 接口使用频率最高的实现类
    • LinkedHashMap 保证再遍历Map元素时,可以按照添加的顺序实现遍历,原因: 在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。对于频繁的遍历操作,此类执行效率高于HashMap
    • TreeMap 保证按照添加的 key-value键值对进行排序,实现排序遍历.此时考虑key的自然排序或定制排序,底层使用红黑树:
    • Hashtalbe 作为古老的实现类,线程安全的,效率低,不可以存储 null
    • Properties 主要用于配置文件的读取。
  • 键值对的示图:

在这里插入图片描述

2. Map接口:常用方法

添加、删除、修改操作:

  • put(K key, V value) : 将指定的 key 和 value 值添加/修改到该集合当中。
V put(K key,V value);  // 将指定的 key 和 value 值添加/修改到该集合当中。
  • putAll(Map m) : 将 m 中所有的key-value 值存放到当前 对象集合当中。
void putAll(Map<? extends K,? extends V> m);  // 将m中的所有key-value对存放到当前map集合当中
  • remove(Object key) : 移除指定key的key-value对,并返回value。
V remove(Object key);  // 移除指定key的key-value对,并返回value
  • clear() : 清空当前map中的所有数据。
void clear();  // 清空当前map中的所有数据
  • size() : 返回此集合中存储的元素数据(键值对)的数量。
int size();  // 返回此集合中存储的元素数据(键值对)的数量。

举例:

import java.util.HashMap;
import java.util.Map;

public class MapTest {
    public static void main(String[] args) {
        // Map 接口 , HashMap实现类,多态,<String,Integer> 泛型
        Map<String,Integer> map = new HashMap<String,Integer>();

        // 添加元素数据:
        map.put("zhangsan",66);
        map.put("lisi",89);
        map.put("wangwu",97);
        map.put("lihua",99);

        System.out.println(map);
        int size = map.size();  // 返回该集合中存储的键值对的数量。
        System.out.println(size);

        System.out.println("*********************");

        Integer zhangsan = map.remove("zhangsan");  // 移除key = zhangsan的元素数据,并返回该移除的value值。
        System.out.println(zhangsan);
        System.out.println(map);

        map.clear();  // 清空该Map 集合当中的存储的元素数据
        System.out.println(map.size());


    }
}

在这里插入图片描述

举例

import java.util.HashMap;
import java.util.Map;

public class MapTest {
    public static void main(String[] args) {
        // Map 接口 , HashMap实现类,多态,<String,Integer> 泛型
        Map<String,Integer> map = new HashMap<String,Integer>();

        // 添加元素数据:
        map.put("zhangsan",66);
        map.put("lisi",89);
        
        Map<String,Integer> map2 = new HashMap<String,Integer>();
        map2.put("wangwu",97);
        map2.put("lihua",99);
        
        map.putAll(map2);  // 将 map2 集合中所有的key-value键值对添加到此 map集合当中去
                           // 注意:两者集合存储的元素数据类型必须是一致的才可以添加成功。
        System.out.println(map);
    }
}

在这里插入图片描述

元素查询的操作:

  • get(Object key) : 获取指定key对应的value。
V get(Object key);  // 获取指定key对应的value
  • containsKey(Object key) : 判断该集合当中是否包含指定的 key值。
boolean containsKey(Object key);  // 判断该集合当中是否包含指定的 key 值。
  • containsValue(Object key) : 判断该集合当中是否包含指定的 value 值。
boolean containsValue(Object value); // 判断判断该集合当中是否包含指定的 value 值。
  • isEmpty() : 判断此集合是否为 空,是返回 true,不是返回 false。
boolean isEmpty();  // 判断此集合是否为 空,是返回 true,不是返回 false;
  • equals(Object o) : 判断当前map和参数对象 o 是否相等。
boolean equals(Object o); // 判断当前map和参数对象 o 是否相等

举例:

import java.util.HashMap;
import java.util.Map;

public class MapTest {
    public static void main(String[] args) {
        // Map 接口 , HashMap实现类,多态,<String,Integer> 泛型
        Map<String,Integer> map = new HashMap<String,Integer>();

        // 添加元素数据:
        map.put("zhangsan",66);
        map.put("lisi",89);
        map.put("wangwu",97);
        map.put("lihua",99);

        System.out.println(map.get("zhangsan"));  // 获取到对应 key上的 value值
        System.out.println(map.containsKey("zhangsan"));  // 判断该集合当中是否存在 key = zhangsan的键值对
        System.out.println(map.containsValue(99));  // 判断该集合当中是否存在 value = 99的键值对
        System.out.println(map.isEmpty());  // 判断该集合是否为空
        System.out.println(map.equals(map)); //  判断当前map和参数对象 o 是否相等

    }
}

在这里插入图片描述

元视图操作的方法:

  • keySet() : 返回所有key构成的Set集合。从该方法中可以看出 Map 接口下的集合中的 key 值是存储在 Set 接口集合当中的。
Set<K> keySet();  // 返回所有key构成的Set集合
  • values() : 返回所有value构成的Collection集合。从该方法中可以看出 Map 接口下的集合中的 value 值是存储在 Collection 接口集合当中的。
Collection<V> values();  // 返回所有value构成的Collection集合

举例:

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class MapTest {
    public static void main(String[] args) {
        // Map 接口 , HashMap实现类,多态,<String,Integer> 泛型
        Map<String,Integer> map = new HashMap<String,Integer>();

        // 添加元素数据:
        map.put("zhangsan",66);
        map.put("lisi",89);
        map.put("wangwu",97);
        map.put("lihua",99);

        Set<String> keys = map.keySet();  // 返回此集合当中所有的 key 值存储到 Set 集合当中
        for (String s : keys) {
            System.out.println(s);
        }

        System.out.println("****************");
        Collection<Integer> values = map.values();  // 返回此集合当中所有的 value 值存储到 Collection 集合当中
        // Collection 接口集合可以使用迭代器
        Iterator<Integer> iterator = values.iterator();
        while(iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

在这里插入图片描述

  • entrySet() : 返回该集合当中的所有 key-value键值对,并存储到 Set 集合当中后,再返回一个 Set 集合对象(该集合存储了所有的key-value) 值。
Set<Map.Entry<K,V>> entrySet();  // 返回所有key-value对构成的Set集合

其中的 Map.Entry 表示的是一个接口,也可以理解为是一个类。

*   Set<Map.Entry<K,V>> entrySet()Map集合转换成 Set集合
*   假设现在有一个 Map集合 ,如下所示:
*     map1 集合对象
*     key               value
*     1                 zhangsan
*     2                 lisi
*     3                 wangwu
*     4                 zhaoliu
*
*     Set set = mop1.entrySet();
*     set 集合对象
*     1=zhangsan
*     2=lisi
*     3=wangwu
*     4=zhaoliu

Map.Entry<K,V> 的图示:

在这里插入图片描述

在这里插入图片描述

举例:

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class MapTest {
    public static void main(String[] args) {
        // Map 接口 , HashMap实现类,多态,<String,Integer> 泛型
        Map<String,Integer> map = new HashMap<String,Integer>();

        // 添加元素数据:
        map.put("zhangsan",66);
        map.put("lisi",89);
        map.put("wangwu",97);
        map.put("lihua",99);

        Set<Map.Entry<String, Integer>> entries = map.entrySet();
        Iterator<Map.Entry<String, Integer>> iterator = entries.iterator();
        while(iterator.hasNext()) {
            Map.Entry<String, Integer> entry = iterator.next();
            // getKey()获取 key 值,getValue()获取value值
            System.out.println(entry.getKey() + "--->" + entry.getValue());
        }
    }
}

在这里插入图片描述

3. Map实现类之一:HashMap

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • HashMapMap 接口使用频率最高的实现类。

  • HashMap 允许存储 null 值,key 可以为 null ,但仅仅只能有一个,因为不可重复,value 可以为 null 。无序

  • HashMap 中所有的 key 构成的集合是存储在 Set 当中的,无序的,不可重复的,所以:key 所在类和 Set 集合是一样的必须重写 euqlas() 和 hashCode() 方法。其中 Java当中的包装类和String 类都重写了 equals() 和 hashCode()方法。基本上只有我们自定的类需要重写。

  • 一个key-value 构成一个 Map.Entry

  • 所有的 Map.Entry 构成的集合是 Set 无序的,不可重复的。

  • HashMap 判断两个 key 相等的标准是 : 两个key 通过 equals() 方法返回 true , hashCode 值也相等。

  • HashMap 判断两个 value 相等的标准 是: 两个 value 通过 equals() 方法返回 true。

  • HashMap 集合底层是哈希表的数据结构

    • 哈希表是一个数组 + 单向链表 的结合体。
    • 数组:在查询方面效率很高,随机增删方面很低。
    • 链表:在随机增删方面效率较高,在查询方面效率低。
    • 而哈希表:将以上两种数据结构融合在一起,充分发挥它们各自的优点。
  • 对于 HashMap 中的方法基本上都是继承了对应的 Map 接口的方法,上面已经说明了,这里就不多介绍了。

举例:

如下是 Set 中的 Key 存储自定义类 Person5 ,其中并没有重写Object 中的 equals() 方法和 hashCode()方法。会出现 Key 存储到重复的数据。

package blogs.blogs7;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class HashMapTest {
    public static void main(String[] args) {
        HashMap<Person5,Integer> hashMap = new HashMap<Person5, Integer>();
        hashMap.put(new Person5("Tom",19),1);
        hashMap.put(new Person5("Tom",19),1);
        hashMap.put(new Person5("Tom",19),1);
        hashMap.put(new Person5("zhangsan",23),4);
        hashMap.put(new Person5("lihua",20),5);

       // 遍历HashMap 集合
        Set<Map.Entry<Person5,Integer>> entries = hashMap.entrySet();
        for (Map.Entry<Person5,Integer> entry : entries) {
            System.out.println(entry);
        }

    }
}


class Person5 {
    String name;
    int age;

    public Person5() {
    }

    public Person5(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person5{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

在这里插入图片描述


修改: 重写其中的 Key 值的 Set 集合中存储的 类中的 equals() 和 hashCode() 方法。

package blogs.blogs7;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

public class HashMapTest {
    public static void main(String[] args) {
        HashMap<Person5,Integer> hashMap = new HashMap<Person5, Integer>();
        hashMap.put(new Person5("Tom",19),1);
        hashMap.put(new Person5("Tom",19),1);
        hashMap.put(new Person5("Tom",19),1);
        hashMap.put(new Person5("zhangsan",23),4);
        hashMap.put(new Person5("lihua",20),5);

       // 遍历HashMap 集合
        Set<Map.Entry<Person5,Integer>> entries = hashMap.entrySet();
        for (Map.Entry<Person5,Integer> entry : entries) {
            System.out.println(entry);
        }

    }
}


class Person5 {
    String name;
    int age;

    public Person5() {
    }

    public Person5(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person5)) return false;
        Person5 person5 = (Person5) o;
        return age == person5.age &&
                Objects.equals(name, person5.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    @Override
    public String toString() {
        return "Person5{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

在这里插入图片描述

HashMap中的 Key值可以存储添加 null 值,但是仅仅只能添加一个 null ,因为 Key 中的数据存储在 Set集合当中的,不可重复,而 Value 值也可以存储 null值,而且可以存储多个 null 值,因为 Value 值数据底层是存储在Collection集合当中的

举例:

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

public class HashMapTest {
    public static void main(String[] args) {
        HashMap<String,Integer> hashMap = new HashMap<String, Integer>();
        hashMap.put(null,null);
        hashMap.put(null,null);
        hashMap.put(null,null);
        hashMap.put("1",null);
        hashMap.put("2",null);
        hashMap.put("3",null);

        // 遍历HashMap 集合
        Set<Map.Entry<String,Integer>> entries = hashMap.entrySet();
        for (Map.Entry<String,Integer> entry : entries) {
            System.out.println(entry);
        }
    }
}

在这里插入图片描述

常用方法总结:

  • 添加: put(Object key,Object value)
  • 删除: remove(object key)
  • **修改: **put(Object key,Object value)
  • 查询: get(Object key)
  • 长度: size();
  • 遍历: keySet()/values()/entrySet()

3.1 HashMap的存储结构

JDK 7及以前版本:HashMap是数组+链表结构(即为链地址法)

JDK 8版本发布以后:HashMap是数组+链表+红黑树实现

在这里插入图片描述

如下是 JDK8 的HashMap 结构图

在这里插入图片描述

3.2 HashMap源码中的重要常量

/**
 * The default initial capacity - MUST be a power of two.
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16  HashMap的默认容量是 16
-----------------------------------------------------------------------------------
 /**
* The maximum capacity, used if a higher value is implicitly specified
* by either of the constructors with arguments.
* MUST be a power of two <= 1<<30.
*/
static final int MAXIMUM_CAPACITY = 1 << 30;  // HashMap的最大支持容量,2^30
-----------------------------------------------------------------------------------
/**
* The load factor used when none specified in constructor.
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;  // HashMap的默认加载因子
-----------------------------------------------------------------------------------
 /**
* The bin count threshold for using a tree rather than list for a
* bin.  Bins are converted to trees when adding an element to a
* bin with at least this many nodes. The value must be greater
* than 2 and should be at least 8 to mesh with assumptions in
* tree removal about conversion back to plain bins upon
* shrinkage.
*/
static final int TREEIFY_THRESHOLD = 8;  // Bucket中链表长度大于该默认值,转化为红黑树
-----------------------------------------------------------------------------------
/**
* The bin count threshold for untreeifying a (split) bin during a
* resize operation. Should be less than TREEIFY_THRESHOLD, and at
* most 6 to mesh with shrinkage detection under removal.
*/
static final int UNTREEIFY_THRESHOLD = 6; // Bucket中红黑树存储的Node小于该默认值,转化为链表
-----------------------------------------------------------------------------------
/**
* The smallest table capacity for which bins may be treeified.
* (Otherwise the table is resized if too many nodes in a bin.)
* Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts
* between resizing and treeification thresholds.
*/
static final int MIN_TREEIFY_CAPACITY = 64; // 桶中的Node被树化时最小的hash表容量。(当桶中Node的数量大到需要变红黑树时,若hash表容量小于MIN_TREEIFY_CAPACITY时,此时应执行resize扩容操作这个MIN_TREEIFY_CAPACITY的值至少是TREEIFY_THRESHOLD的4倍。)
-----------------------------------------------------------------------------------
/**
* The table, initialized on first use, and resized as
* necessary. When allocated, length is always a power of two.
* (We also tolerate length zero in some operations to allow
* bootstrapping mechanics that are currently not needed.)
*/
transient Node<K,V>[] table;  // 存储元素的数组,总是2的n次幂
-----------------------------------------------------------------------------------
/**
* Holds cached entrySet(). Note that AbstractMap fields are used
* for keySet() and values().
*/
transient Set<Map.Entry<K,V>> entrySet;  // 存储具体元素的集合
-----------------------------------------------------------------------------------
/**
* The number of key-value mappings contained in this map.
*/
transient int size;  // HashMap中实际存储的键值对的数量
-----------------------------------------------------------------------------------
/**
* The number of times this HashMap has been structurally modified
* Structural modifications are those that change the number of mappings in
* the HashMap or otherwise modify its internal structure (e.g.,
* rehash).  This field is used to make iterators on Collection-views of
* the HashMap fail-fast.  (See ConcurrentModificationException).
*/
transient int modCount;  // HashMap扩容和结构改变的次数。
-----------------------------------------------------------------------------------
/**
* The next size value at which to resize (capacity * load factor).
*
* @serial
*/
// (The javadoc description is true upon serialization.
// Additionally, if the table array has not been allocated, this
// field holds the initial array capacity, or zero signifying
// DEFAULT_INITIAL_CAPACITY.)
int threshold;  // 扩容的临界值,=容量 * 填充因子
-----------------------------------------------------------------------------------
/**
* The load factor for the hash table.
*
* @serial
*/
final float loadFactor;  // 填充因子

3.3 HashMap的存储结构:JDK 1.8之前 / JDK 1.8之后

3.3.1 JDk 1.8 之前

  • HashMap 内部存储结构其实是 数组 + 链表的结合。当实例化一个 new HashMap() 时,实际上会创建一个长度为 CapacityEntry 数组。这个长度在 哈希表中称为 容量(Capacity) ,在这个数组中可以存放元素的位置,我们称之为 ”桶“ (bucket) ,每个 bucket 都有自己的索引,系统可以根据索引快速的查找 bucket 中的元素。

  • 每个bucket 中存储一个元素,即 一个 Entry 对象内部类 ,但每一个 Entry 对象可以带 一个引用变量,用于指向下一个元素,因此,在一个桶 (bucket) 中,就有可能生成一个 Entry 链。而且新添加的元素作为链表的 head

  • JDK7 源码分析如下:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3.3.2 JDk 1.8 及之后

JDK8: HashMap 的内部存储结构其实是:数组+链表+树 的结合。当实例化一个 new HashMap 时,会初始化 initilCapacityloadFactor ,在 put() 第一对映射关系(键值对)添加时,系统会创建一个长度为 initilCapacityNode 数组 ,这个长度在哈希表中被称为 ”容量" (Capacity),在这个数组中可以存放元素的位置,我们称之为 “桶”(bucket) ,每个 bucket 都有自己的索引,系统可以根据索引快速的查找 bucket 中的元素。

每个bucket 中存储一个元素数据,既 一个 Node 对象,但每一个 Node 对象可以带一个引用变量 next ,用于指向下一个元素,因此,在一个桶中,就有可能生成一个 Node 链表。也可能是一个一个TreeNode 对象,每一个TreeNode 对象可以有两个叶子节点 leftright ,因此,在一个桶中,就有可能生成一个 TreeNode 树。而新添加的元素作为链表的 last ,或树的叶子节点。

JDK1.8 源码分析:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3.3.3 JDK8 HashMap 集合添加元素的过程

HashMap 集合中添加 put(key1,value1) 键值对, 首先调用 元素 key1 所在类的 hashCode() 方法,来得到该 key1对象的 hashCode(哈希) 值。

然后再根据得到的 hashCode (哈希)值,通过某种散列函数 计算除该对象在 HashSet 集合中底层Node[] 数组的存储位置(即为:索引下标位置),(这个散列函数会与底层数组的长度相计算得到在数组中的下标,并且这种散列函数计算还尽可能保证能均匀存储元素,越是散列分布,该散列函数设计的越好)。

  • 判断此计算处理得到的数组下标位置上是否已经有元素存储了 :

    • 如果没有其他元素数据存储,则 元素 key1-value1添加到该位置上。 —— 情况1
    • 如果其它元素数据存储(或以链表形式存储的多个元素) : 则比较key1和已经存在的一个或多个数据的哈希值):
      • 如果 key1的hashCode() 哈希值与已经存在的数据的哈希值都 不相等, 则元素 key1-value1添加到该数组链表上。—— 情况2

      • 如果 key1 的hashCode() 哈希值 与 已经存在的数据的哈希值都 相等, 则调用 key1 元素所在类的 equals() 方法,, 判断比较所存储的内容是否和集合中存储的相等。

        • 如果 不相等 也就是 equals() 方法,返回 false ,则此时 key1-value1添加成功。—— 情况3
        • 如果 相等 也就是 **equals()**方法,返回 true,不添加,替换掉其中存放的 value 值为 value1 ,因为 key1 是唯一的不可重复的,但是其 对应的 value 值是可以重复的。
  • 对应上述 添加成功的 情况2情况3 而言,关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储

  • 如下是 添加键值对的过程的图示:

在这里插入图片描述

如下是查找图示:

在这里插入图片描述

假设将所有的hashCode()方法返回设定为不一样的值,可以吗?,有什么问题:

不行,因为这样的话,就导致 HashMap 集合底层的哈希表就成为了一维数组了,没有链表的概念了,更没有哈希表的概念了。

假设将所有的hashCode()方法返回设返回值固定为某个值,可以吗?,有什么问题:

答:不可以,设将所有的hashCode()方法,返回值固定为某个值,那么会导致底层哈希表变成了纯单向链表。这种情况下我们称为:散列分别不均匀。

什么时散列分布不均匀

假设我们有 100 个元素,10个单向链表,那么每个单向链表上有10个节点,这是最好的,是散列分布均匀的

3.3.4 JDK8 HashMap 进行 "扩容"和 “树形化”

扩容

put(Key1,value1) 添加键值对个数超过 数组大小(数组总大小 length ,不是数组中实际存放的键值对个数 size),时,就会进行数组扩容。loadFactor 的默认值:DEFAULT_LOAD_FACTOR)为0.75,这是一个折中的取值,也就是说,默认情况下,数组大小**(DEFAULT_INITIAL_CAPACITY)为16** ,那么当 HashMap 中元素个数超过 16 * 0.75 = 12 (这个值就是代码中的 threshold值,也叫临界值)的时候,就把数组的大小扩展为 2 * 16 = 32 ,即扩大 1倍 ,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作。所以在开发中如果我们可以预估计其存储的数据量,也就是 HashMap中存储元素的个数,那么就调用其HashMap(int num) 设定存储容量的大小,减少扩容次数,提高 HashMap的性能

在这里插入图片描述

树形化

HashMap中的其中一个链的对象个数如果达到了8个,此时如果capacity没有达到64,那么HashMap会先扩容解决,如果已经达到了64,那么这个链会变成,结点类型由Node变成TreeNode类型。当然,如果当映射关系被移除后,下次resize方法时判断树的结点个数低于6个,也会把树再转为链表

在这里插入图片描述

补充:

关于映射关系的key是否可以修改 ???

answer:不要修改,映射关系存储到 HashMap 中会存储 key哈希值 ,这样就不用每次查找时,重新计算每一个 EntryNode (TreeNode)的 哈希值了,因此如果已经 putMap 中的映射关系,再修改 key 的属性,而这个属性有参与 hashCode值的计算,那么会导致匹配不上。

为什么HashMap扩容时,不是数组满了的时候扩容而是达到一个的 0.75 的额度才扩容 ???

因为HashMap 集合的底层时由 链表 + 数组 + 树 构成的。由于链表的存在,HashMap 当中的数组不一定会存储满了。

以及涉及到 HashMap 集合性能最优的效果,散列均匀分布,所以是到达一定额度 0.75 是最好的情况了.

在这里插入图片描述

负载因子值的大小,对HashMap有什么影响 ???

/**
* The load factor used when none specified in constructor.
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;  // HashMap的默认加载因子

负载因子的大小决定了HashMap的数据密度。

负载因子越大密度越大,发生碰撞的几率越高,数组中的链表越容易长,

造成查询或插入时的比较次数增多,性能会下降。

负载因子越小,就越容易触发扩容,数据密度也越小,意味着发生碰撞的

几率越小,数组中的链表也就越短,查询和插入时比较的次数也越小,性能会更高。但是会浪费一定的内容空间。而且经常扩容也会影响性能,建议初始化预设大一点的空间。

按照其他语言的参考及研究经验,会考虑将负载因子设置为0.7~0.75,此时平均检索长度接近于常数。

3.3.5 总结:JDK1.8 相较于之前的变化:

  • JDK8 :HashMap map = new HashMap() ,默认情况下,是不会先创建长度为 16 的 数组的,而是首先调用 map.put() 添加键值对的时候创建 长度为 16的数组(类比:单例模式中的饿汉式)。 JDK7 则是默认先创建了一个长度为 16的数组(类比:单例模式中的懒汉式)。

  • JDK8 底层的数组是 Node[ ]JDK7 底层的数组是 Entry[ ]

  • put(Key1,Value1) 添加键值对时,JDK7 是添加到链表上的头部(数组上),JDK8 是添加到链表的尾部(不在数组上),简称:七上八下。

  • jdk7 底层结构只有:数组 + 链表jdk8 中底层结构: 数组 + 链表 + 红黑树

    当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度 > 64时,此时此索引位置上的所有数据改为使用“红黑树”存储。当小于 8 时,有会变成链表的形式存储。

4. Map实现类之二:LinkedHashMap

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • LinkedHashMapHashMap 的子类,所以 LinkedHashMap 继承了 HashMap 的特点:其中的 key 是无序,不可重复的,其 Key 存储的元素类必须重写 eqauls() 和 hashCode() 方法。同样的 Key 值是存储在 Set 集合当中的,而Value 则是存储在 Collection 集合当中的。
  • LinkedHashMap 是在 HashMap 存储结构的基础上,使用了一对双向链表来记录添加元素的顺序的,所以你添加元素数据的顺序是怎样的,取元素数据的顺序就是怎样的。
  • LinkedHashSet 类似, LinkedHashMap 可以维护 Map 的迭代顺序:迭代顺序与 Key-Value 键值对的插入顺序一致,简单的说就是:存取顺序一样。
  • LinkedHashMap 是继承了 HashMap 其常用的方法是一样的,这里就不多说明了。不同的是HashMap 底层的内部类是 Node ,而LinkedHashMap 底层的内部类是Entry ,该内部类继承了 HashMap.Node<K,V>

HashMap中的内部类:Node

在这里插入图片描述

LinkedHashMap中的内部类:Entry

在这里插入图片描述

举例:

import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

public class LinkedHashMapTest {
    public static void main(String[] args) {
        // 创建 LinkedHashMap 集合对象
        LinkedHashMap<String,Integer> linkedHashMap = new LinkedHashMap<String, Integer>();

        // 添加元素数据(键值对)
        linkedHashMap.put("lihua",99);
        linkedHashMap.put("zhangsan",89);
        linkedHashMap.put("lisi",79);
        linkedHashMap.put("wangwu",69);

        // 遍历 LinkedHashMap 集合
        // 获取到key-value 存储的 Set Entry 内部类集合对象
        Set<Map.Entry<String, Integer>> entries = linkedHashMap.entrySet();
        // 获取到该 Set Entry 内部类集合的迭代器
        Iterator<Map.Entry<String, Integer>> iterator = entries.iterator();
        while(iterator.hasNext()) {
            Map.Entry<String, Integer> entry = iterator.next();
            System.out.println(entry.getKey() + "--->" + entry.getValue());
        }

    }
}

在这里插入图片描述

5. Map实现类之三:TreeMap

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • TreeMap 存储 Key-Value 键值对时,需要根据 key-value 键值对进行排序,TreeMap 可以保证所有的 key-value 键值对处于有序状态。

  • TreeSet 底层 就是由 TreeMap 构成的,new TreeSet 底层实际上说就是 new TreeMap 集合存储数据,向 TreeSet 中添加数据就是向 TreeMap 集合中添加数据。

  • TreeMap 中的 key 存储的数据类型必须是一致的,不然无法比较判断,从而排序。

  • TreeMap 的 排序是对 Key 的内容进行排序的,其中的 Key 值内部是由 Set 集合存储的,无序,不可重复性,所存储类必须重写 equals() 和 hashCode() 方法。因为会自动排序,所以还需要实现排序:两种方式一种是:

    • 自然排序TreeMap 的所有的 Key 必须实现(实现 java.lang.Comparable的接口,而且所有 的 Key 应该是同一个类的对象(因为不是同一类型无法比较判断),否则将会抛出 ClasssCastException自然排序,重写其中的 compareTo()抽象方法) 。在Java当中所有的包装类和String都实现了该 java.lang.Comparable接口。所以一般要实现该接口的都是自定的类。
    • 定制排序: 创建 TreeMap 时,传入一个 Comparator 对象,该对象负责对 TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现 Comparable 接口
    public TreeMap(Comparator<? super K> comparator)v // 构造一个新的,空的树图,按照给定的比较器排序。
    
    • 关于自然排序与 定制排序 的详解内容大家可以移步至:🔜🔜🔜 比较器: Comparable 与 Comparator 区别_ChinaRainbowSea的博客-CSDN博客
  • TreeMap 判断两个 Key 相等的标准:两个 key 通过 重写的 compareTo()方法或 compare()方法,返回0 表示相等。

举例:

TreeMap 集合中存储自定义类 Person6 对象,其中的 Key 存储的类为自定义 Person6 对象,该对象重写了 equals() 和 hashCode()方法,但是没有重写比较器的情况,报异常:java.lang.ClassCastException 类型转换异常。

将其中的Person6 中 age 年龄,升序排列

在这里插入图片描述


import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;

public class TreeMapTest {
    public static void main(String[] args) {
        // 创建TreeMap 集合对象,其中 Key 存储的类为自定义 Person6
        TreeMap<Person6,Integer> treeMap = new TreeMap<Person6,Integer>();

        // 添加元素
        treeMap.put(new Person6("lihua",18),99);
        treeMap.put(new Person6("zhangsan",20),89);
        treeMap.put(new Person6("lisi",25),79);
        treeMap.put(new Person6("wangwu",19),69);

        // 遍历集合
        // 遍历 TreeMap 集合
        // 获取到key-value 存储的 Set Entry 内部类集合对象
        Set<Map.Entry<Person6, Integer>> entries = treeMap.entrySet();
        // 获取到该 Set Entry 内部类集合的迭代器
        Iterator<Map.Entry<Person6, Integer>> iterator = entries.iterator();
        while(iterator.hasNext()) {
            Map.Entry<Person6, Integer> entry = iterator.next();
            System.out.println(entry.getKey() + "--->" + entry.getValue());
        }


    }
}

class Person6 {
    String name;
    int age;

    public Person6() {
    }

    public Person6(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }



    // 当对象中的 name 和 age 属性值相同返回 true,否则返回 fasle
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person6)) return false;
        Person6 person6 = (Person6) o;
        return getAge() == person6.getAge() &&
                Objects.equals(getName(), person6.getName());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getName(), getAge());
    }

    @Override
    public String toString() {
        return "Person6{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

修正: 对 Key 中所存储的类,提供比较器,方式一:自然排序,该存储类实现 java.lang.Comparable接口,并重写其中的 CompareTo()重写方法。


import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;

public class TreeMapTest {
    public static void main(String[] args) {
        // 创建TreeMap 集合对象,其中 Key 存储的类为自定义 Person6
        TreeMap<Person6,Integer> treeMap = new TreeMap<Person6,Integer>();

        // 添加元素
        treeMap.put(new Person6("lihua",18),99);
        treeMap.put(new Person6("zhangsan",20),89);
        treeMap.put(new Person6("lisi",25),79);
        treeMap.put(new Person6("wangwu",19),69);

        // 遍历集合
        // 遍历 TreeMap 集合
        // 获取到key-value 存储的 Set Entry 内部类集合对象
        Set<Map.Entry<Person6, Integer>> entries = treeMap.entrySet();
        // 获取到该 Set Entry 内部类集合的迭代器
        Iterator<Map.Entry<Person6, Integer>> iterator = entries.iterator();
        while(iterator.hasNext()) {
            Map.Entry<Person6, Integer> entry = iterator.next();
            System.out.println(entry.getKey() + "--->" + entry.getValue());
        }


    }
}

class Person6 implements Comparable<Person6>{
    String name;
    int age;

    public Person6() {
    }

    public Person6(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }



    // 当对象中的 name 和 age 属性值相同返回 true,否则返回 fasle
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person6)) return false;
        Person6 person6 = (Person6) o;
        return getAge() == person6.getAge() &&
                Objects.equals(getName(), person6.getName());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getName(), getAge());
    }

    /**
     * 升序的比较规则:
     * this > 参数 ,返回 > 0
     * this < 参数,返回 < 0
     * this == 参数,返回 == 0;
     * 降序反过来:
     * this > 参数 ,返回 < 0
     * this < 参数,返回 > 0
     * this == 参数,返回 == 0;
     */
    @Override
    public int compareTo(Person6 o) {
        // 首先判断该需要比较的参数是否是同一个实例,同一个实例的对象才能比较
        if(o instanceof Person6) {  // 其实这里我们使用了<Person3 o> 泛型限定了,就不需要判断了
            Person6 person6 = (Person6) o;  // 是对应的实例向下转型。
            if(this.age > person6.age) {
                return 1;
            } else if( this.age < person6.age) {
                return -1;
            } else {
                return 0;
            }
        } else {
            // throw 可以替代 return
            throw new RuntimeException("类型不一致");  // 抛出运行时异常
        }
    }

    @Override
    public String toString() {
        return "Person6{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

在这里插入图片描述

修改方式二:定制排序 创建 TreeMap 集合对象时,将一个匿名实现Com 的类,作为参数,传递给构造器。该匿名实现类定制排序按照你 Perso6 中的 age 年龄降序排列

public TreeMap(Comparator<? super K> comparator); // 构造一个新的,空的树图,按照给定的比较器排序。
package blogs.blogs7;

import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;

public class TreeMapTest {
    public static void main(String[] args) {
        // 创建一个 TreeMap 集合对象,将匿名实现的比较器(定制排序),作为参数,传递给构造器
        TreeMap<Person6,Integer> treeMap = new TreeMap<Person6,Integer>(new Comparator<Person6>() {
            @Override
            public int compare(Person6 o1, Person6 o2) {
                // 判断是否是对应比较的实例,其实这里我们可以不用判断的,因为使用的泛型限定
                if(o1 instanceof Person6 && o2 instanceof  Person6) {
                    Person6 p1 = (Person6)o1;
                    Person6 p2 = (Person6)o2;  // 向下转型为对应的实例对象,从而获取比较属性

                    if(p1.age > p2.age) {
                        return -1;
                    } else if(p1.age < p2.age) {
                        return 1;
                    } else {
                        return 0;
                    }

                }
                // throw 可以代替 return
                throw new RuntimeException("类型不一致");  // 抛出运行时异常
            }
        });

        // 添加元素
        treeMap.put(new Person6("lihua",18),99);
        treeMap.put(new Person6("zhangsan",20),89);
        treeMap.put(new Person6("lisi",25),79);
        treeMap.put(new Person6("wangwu",19),69);

        // 遍历集合
        // 遍历 TreeMap 集合
        // 获取到key-value 存储的 Set Entry 内部类集合对象
        Set<Map.Entry<Person6, Integer>> entries = treeMap.entrySet();
        // 获取到该 Set Entry 内部类集合的迭代器
        Iterator<Map.Entry<Person6, Integer>> iterator = entries.iterator();
        while(iterator.hasNext()) {
            Map.Entry<Person6, Integer> entry = iterator.next();
            System.out.println(entry.getKey() + "--->" + entry.getValue());
        }

    }
}

class Person6 implements Comparable<Person6>{
    String name;
    int age;

    public Person6() {
    }

    public Person6(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }



    // 当对象中的 name 和 age 属性值相同返回 true,否则返回 fasle
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person6)) return false;
        Person6 person6 = (Person6) o;
        return getAge() == person6.getAge() &&
                Objects.equals(getName(), person6.getName());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getName(), getAge());
    }
    

    @Override
    public String toString() {
        return "Person6{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

在这里插入图片描述

6. Map实现类之四:Hashtable

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • Hashtable 是个古老的 Map 实现类,JDK1.0 就提供了。不同于 HashMapHashtable 是线程安全的,其中的方法基本上都不被 synchronized
  • Hashtable 实现原理和 HashMap 相同,功能相同。底层都使用哈希结构,速度快,很多情况下可以互用。
  • HashtebleHashMap 不同,Hashtable 不允许使用 null 作为 KeyValue 的值。不然报,java.lang.NullPointerException 空指针异常。
  • HashtableHashMap 一样,Hashtable 也不能保证其中 Key-Value 键值对的顺序。
  • 同样的 其中的 Key 值内部是由 Set 集合存储的,无序,不可重复性,所存储类必须重写 equals() 和 hashCode() 方法。
  • Hashtable 判断两个 key 相等,两个 value 相等的标准,与 HashMap 是一样的。Hashtable和HashMap 一样,底层都是哈希表的数据结构,Hashtable 的初始容量为 11,默认加载因子是 : 0.75,Hashtable 的扩容: 原容量 * 2 + 1;

HashMap和HashTable的比较:

这里写图片描述

举例

在这里插入图片描述

在这里插入图片描述

import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class HashtableTest {
    public static void main(String[] args) {
        Hashtable<String,Integer> hashtable = new Hashtable<String,Integer>();

        hashtable.put("lihua",1);
        hashtable.put("zhangsan",2);
        hashtable.put("lisi",3);
        hashtable.put("wangwu",4);

        Set<Map.Entry<String, Integer>> entries = hashtable.entrySet();
        Iterator<Map.Entry<String, Integer>> iterator = entries.iterator();
        while(iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

在这里插入图片描述

7. Map实现类之五:Properties

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • Properties 类是 Hashtable 的子类,该对象用于处理读取属性文件。
  • 由于属性文件里的 key,value都是字符串类型的,所以 Properties 里的 key 和 value 都是字符串类型
  • 存取数据时,建议使用 setProperty(String key,String value) 方法 和 getProperty(String key) 方法。
public Object setProperty(String key,String value); // 致电Hashtable方法put 。 提供与getProperty方法的并行性 。 强制使用字符串的属性键和值。 返回的值是Hashtable调用put的结果。简单的说:就是向Property 集合中添加键值对元素。
public String getProperty(String key); // 通过 key 找到对应的 value值,如果没有找到返回 null

举例:

首先我们先在项目中(注意添加到顶级项目中也就是如下的Test 项目下,不是Test2,或者 day模块下 ,不然无法读取到)添加一个属性文件(以.properties后缀的配置文件)用于Properties 集合读取,内容如下:

在这里插入图片描述

name=Tom
password=abc123
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

public class PropertiesTest {
    public static void main(String[] args) {
        // Properties 集合常用来处理配置文件:key 和 value 都是String类型
        Properties properties = new Properties();

        try {
            // IO流读取文件信息,需要异常处理
            FileInputStream fileInputStream = new FileInputStream("jdbc.properties"); // 文件名
            properties.load(fileInputStream);  // 加载流对应的文件,同样需要异常处理

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        String name = properties.getProperty("name");
        String password = properties.getProperty("password");  // 根据对应文件中的 key 值获取到对应的value值

        System.out.println(name);
        System.out.println(password);


    }
}

在这里插入图片描述

8. Map 接口下的集合遍历方式

Map 接口下的集合的遍历方式:注意:Map 集合中没有下标可以访问的。也没有迭代器可以使用的。

方式一: 普遍使用,二次取值。通过获取 keySet() 方法获取到 Map 集合中所有的 key 值,返回一个 Set 集合。再通过遍历 Set 集合中存储的所有的 key ,使用 get(key) 方法获取到对应的 value值。

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Traverse {

    // Map 集合遍历方式一:
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<String, String>();
        map.put("1", "value1");
        map.put("2", "value2");
        map.put("3", "value3");

        // 1.获取到该 Map 集合当中的所有 Key 值
        Set<String> keys = map.keySet();

        // 2.遍历所有的 key 值
        for (String key : keys) {
            // 3. 通过 key 获取到对应的 value 值
            System.out.println(key + "--->" + map.get(key));
        }
    }
}

在这里插入图片描述

方式二: 通过使用 entrySet() 方法,返回一个:Set< Map.Entry<K, V> > 集合对象, 再通过获取到该 Set<Map.Entry> 集合 的迭代器,通过迭代器遍历,获取到Map.Entry中存储的 key(getKey()), value(getVale()) 方法

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class Traverse {

    // Map 集合遍历方式二:
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<String, String>();
        map.put("1", "value1");
        map.put("2", "value2");
        map.put("3", "value3");

        // 1. 获取到 Set<Map.Entry> 集合对象
        Set<Map.Entry<String, String>> entries = map.entrySet();

        // 2. 获取到该 Set<Map.Entry> 集合 的迭代器
        Iterator<Map.Entry<String, String>> iterator = entries.iterator();

        // 3. 通过迭代器遍历,获取到Map.Entry中存储的 key,value值
        while(iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();
            System.out.println(entry.getKey() + "--->" + entry.getValue());
        }

    }
}

在这里插入图片描述

方式三: 推荐,尤其是容量大时。因为这是一次性获取到 Map 中所有的key-value 值后,再取出的,效率高

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class Traverse {

    // Map 集合遍历方式三:
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<String, String>();
        map.put("1", "value1");
        map.put("2", "value2");
        map.put("3", "value3");

        for (Map.Entry<String, String> entry : map.entrySet()) {
            System.out.println(entry.getKey() + "--->" + entry.getValue());
        }
    }
}

在这里插入图片描述

方式四: 通过Map.values() 返回一个Collection 集合遍历所有的value,但不能遍历key

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class Traverse {

    // Map集合遍历方式四:
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<String, String>();
        map.put("1", "value1");
        map.put("2", "value2");
        map.put("3", "value3");

        // 获取到集合当中所有的 value值
        Collection<String> values = map.values();
        for (String value : values) {
            System.out.println(value);
        }
    }
}

在这里插入图片描述

9. Collections工具类

一个独立的集合 工具类

在这里插入图片描述

在这里插入图片描述

  • Collections 是一个操作 Set,List 和 Map 等集合的工具类。
  • 注意区别:Collection 是一个接口集合,而 Collection s 多了个 s 的是 集合工具类,都是在 java.util. 包下的。
  • Collections 中提供了一系列静态的方法(工具方法麻,一般都是静态方法)对集合元素进行排序,查询和修改等操作,还提供了对集合对象设置不可变,对集合对象实现同步控制(解决线程安全问题)等方法。

9.1 Collections常用方法

排序:

  • reverse(List lsit) : 反转 List 中元素的顺序
public static void reverse(List<?> list);  // 反转 List 中元素的顺序
  • shuffle(List list): 对 List 集合元素进行随机排序
public static void shuffle(List<?> list);  // 对 List 集合元素进行随机排序
  • sort(List list) : 根据元素的自然顺序对指定 List 集合元素按升序排序,注意的是: 排序需要存储的类有比较器调用 自然排序(实现 java.lang.Comparable的接口 / 定制排序 Comparator接口)
public static <T extends Comparable<? super T>> void sort(List<T> list);  // 根据元素的自然顺序对指定 List 集合元素按升序排序
  • sort(List list, Comparator c) : 根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
public static <T> void sort(List<T> list,Comparator<? super T> c);  // 根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
  • swap(List list , int i , int j ) : 将指定 list 集合中的 i 处元素和 j 处元素进行交换。注意是左闭右开的。
public static void swap(List<?> list,int i,int j);  // 将指定 list 集合中的 i 处元素和 j 处元素进行交换

查找,替换

  • max(Collection c) : 根据元素的自然顺序,返回给定集合中的最大元素。
public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll);  // 根据元素的自然顺序,返回给定集合中的最大元素
  • max(Collection coll, Comarator comp) : 根据Comparator 指定的顺序,返回给定集合中的最大元素。
public static <T> T max(Collection<? extends T> coll,Comparator<? super T> comp);  // 根据 Comparator 指定的顺序,返回给定集合中的最大元素
  • min(Collection c) : 根据元素的自然顺序,返回给定集合中的最小元素。
public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll); // 根据元素的自然顺序,返回给定集合中的最小元素。
  • min(Collection coll, Comarator comp) : 根据Comparator 指定的顺序,返回给定集合中的最小元素。
public static <T> T min(Collection<? extends T> coll, Comparator<? super T> comp);  // 根据Comparator 指定的顺序,返回给定集合中的最小元素。
  • replaceAll(List list,T oldVal,T newVal) : 用新值替换List 对象的所有旧值。注意存储的类需要重写 equals()方法,才能比较判断找到对应替换的值。
public static <T> boolean replaceAll(List<T> list,T oldVal,T newVal); // 用新值替换List 对象的所有旧值.
  • frequency(Collection c , Object o) : 返回指定集合中指定元素的出现次数,注意 :存储的类需要重写 equals()方法才能比较判断查找对应的值的个数。
public static int frequency(Collection<?> c,Object o);  // 返回指定集合中指定元素的出现次数
  • copy(List dest,List src) : 将 src 集合中的内容复制到 dest 集合当中。
public static <T> void copy(List<? super T> dest,List<? extends T> src); // 将 src 集合中的内容复制到 dest 集合当中

注意该 copy(List dest,List src) 方法,两个集合对象存储的数据类型是必须是一样的,不然无法拷贝添加到 dest 集合当中的。

还有拷贝存储到的对象 dest 的 size()长度 < 被拷贝的 src 的 size()长度 就会拷贝失败,报异常: IndexOutOfBoundsException

所以拷贝存储到的对象 dest 的 size()长度 必须 >= 被拷贝的 src 的 size()长度注意是 size()实际存储元素数据的长度,不是length()集合的长度

如下源码:

在这里插入图片描述

举例 : 解决思路如下:

package blogs.blogs7;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class CollectionsTest {
    public static void main(String[] args) {
        List<Integer> src = new ArrayList<Integer>();
        src.add(1);
        src.add(99);
        src.add(-1);
        src.add(66);

        // 创建一个 和 src 集合存储类型一样的  Arrays.asList(new Integer[src.size()])数组,并设置该数组的大小长度为 src.size()
        // 再使用这个数组创建拷贝存储的 desc 集合对象,默认数组没有添加数据(这里的是 null)
        // 这样就 desc 就拥有了一个和 sec集合一样大小的 size()长度了。
        List<Integer> desc = new ArrayList<Integer>();
        desc = Arrays.asList(new Integer[src.size()]);
        for (Integer integer : desc) {
            System.out.println(integer);
        }

        // copy()拷贝
        Collections.copy(desc, src);

        System.out.println("***********  拷贝  ************");
        for (Integer num : desc) {
            System.out.println(num);
        }
    }
}

在这里插入图片描述

10. 总结:

补充 :各个集合的转换,可以使用对应的方法,或构造器

	@Test
    public void test2() {
        Set<String> set = new HashSet<>();
        set.add("king");
        set.add("kingsoft");
        set.add("king2");
        set.add("king1");

        // 将Set集合转换成List集合
        List<String> myList = new ArrayList<>(set);
        for(String s : myList) {
            System.out.println(s);
        }
    }

在这里插入图片描述

在这里插入图片描述

  • Map 接口下的集合的特点:集合的key 就是一个 Set 集合存储的。而 value 值则是被 Collectio接口集合存储的。
    在Set 集合中放数据,实际上放到了Map集合的key 部分中去了。
  • 注意:Map集合中的 Key 都是存储在 Set 集合当中的(该集合无序,不可重复),所以Map集合当中的 key 存储的类必须重写 equals() 和 hashCode() 方法。不然无法处理 Key 的不可重复特点 。。但是其中的 value 值是可以存储重复的数据的。而 value 值则是被 Collection 接口集合存储的。
  • Map 接口与 Collection 并列存在的,用于保存具有映射关系的数据:key-value 被称为 键值对 。一个key-value 构成一个 Map.Entry。所有的 Map.Entry 构成的集合是 Set 无序的,不可重复的。
  • 理解 HashMap 中的 put() 添加键值对元素数据的原理,以及扩容,和树化的机制。区别 JDK7 / JDK8 的机制不同点
  • TreeMap 的 排序是对 Key 的内容进行排序的,其中的 Key 值内部是由 Set 集合存储的,无序,不可重复性,所存储类必须重写 equals() 和 hashCode() 方法。因为会自动排序,所以还需要实现排序:两种方式一种是:
    • 自然排序TreeMap 的所有的 Key 必须实现(实现 java.lang.Comparable的接口,而且所有 的 Key 应该是同一个类的对象(因为不是同一类型无法比较判断),否则将会抛出 ClasssCastException自然排序,重写其中的 compareTo()抽象方法) 。在Java当中所有的包装类和String都实现了该 java.lang.Comparable接口。所以一般要实现该接口的都是自定的类。
    • 定制排序: 创建 TreeMap 时,传入一个 Comparator 对象,该对象负责对 TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现 Comparable 接口

11. 最后:

限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善,谢谢大家,后会有期,江湖再见!!!
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/196112.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第一章:ElasticSearch简介

Elaticsearch&#xff0c;简称为es&#xff0c; es是一个开源的高扩展的分布式全文检索引擎&#xff0c;它可以近乎实时的存储、检索数据&#xff1b; 本身扩展性很好&#xff0c;可以扩展到上百台服务器&#xff0c;处理PB级别的数据。 es也使用Java开发并使用Lucene作为其核…

MFC|各控件的使用

参考&#xff1a; MFC学习笔记-4-选项控件Combox的使用&#xff08;https://dandelioncloud.cn/article/details/1517727978783109122&#xff09; 文章目录控件与变量关联各控件的使用Combo Box添加设置默认选项插入 index从0开始删除获取1号索引的具体内容添加事件获取当前in…

前端学习--async

文章目录async函数await使用await等待Promise异步函数await等待普通函数什么时候使用async/awaitasync也是用于异步任务的&#xff0c;可以说是异步任务处理的另一种方式async函数 async修饰函数&#xff0c;表示这个函数中可以处理异步函数 async修饰的函数&#xff0c;会返…

每天10个前端小知识 【Day 3】

前端面试基础知识题 1. 使用js生成1-10000的数组 实现的方法很多&#xff0c;除了使用循环&#xff08;for,while,forEach等&#xff09;外&#xff0c;最简单的是使用Array.from //方法一&#xff1a; Array.from(new Array(10001).keys()).slice(1) //方法二&#xff1a;…

基于paddlex图像分类模型训练(二):训练自己的分类模型、熟悉官方demo

0. 前言 相关系列博文&#xff1a;基于paddlex图像分类模型训练&#xff08;一&#xff09;&#xff1a;图像分类数据集切分&#xff1a;文件夹转化为imagenet训练格式 代码在线运行&#xff1a; https://aistudio.baidu.com/aistudio/projectdetail/5440569 1. 官方demo&a…

spring框架之注解开发

Spring是轻代码而重配置的框架&#xff0c;配置比较繁重&#xff0c;影响开发效率&#xff0c;所以注解开发是一种趋势。 让我们来看看注解开发之前是如何定义bean的&#xff1f; ① BrandDemo.java ② applicationContext.xml ③Test.java 一、注解开发定义bean 组件扫描 二…

信用卡APP评测系列——工银e生活5.0打造个人生活服务平台,引领用户美好生活

易观&#xff1a;中国信用卡市场规模增速趋稳&#xff0c;线上成为存量用户经营主阵地&#xff0c; APP用户高质量经营成为新发力点&#xff0c;也是业务良性增长保障&#xff0c;对此&#xff0c;银行机构着力用户体验竞相升级信用卡APP。工商银行顺势升级工银e生活APP5.0版&a…

第二章.神经网络—3层神经网络的实现,输出层设计

第二章.神经网络 2.3 三层神经网络的实现 1.各层间信号传递的实现 1).示意图&#xff1a; 2).公式&#xff1a; ①.用数学式表示a1(1)&#xff1a; ②.用矩阵表示第一层的加权和&#xff1a; 3).实现&#xff1a; import numpy as np# 3层神经网络的实现# 参数初始化 def i…

华数杯B题——校任务尝试

一、背景说明 根据影响社会稳定的因素&#xff0c;以及颜色革命&#xff0c;来衡量社会稳定性&#xff0c;判断社会风险 社会预警指标体系是由一系列经过理论遴选的敏感指标组成的一种测量社会危机现象及其运行过程的指标系统&#xff0c;它作为一种特定的测量工具和手段&…

Java设计模式--原型模式

概念&#xff1a;用原型实例&#xff08;最初的&#xff09;指定创建对象的种类&#xff0c;并且通过拷贝这些原型&#xff0c;创建新的对象。&#xff08;自我复制能力&#xff09;1.类图原理类图分析Prototype&#xff1a;原型类&#xff0c;声明一个克隆自己的接口ConcreteP…

Go编程规范和性能调优(三)——规范编码和性能优化

文章目录一、本次学习重点内容&#xff1a;二、详细知识点介绍&#xff1a;1、高质量编程简介什么是高质量&#xff1f;编程原则&#xff1a;2、编码规范注释&#xff1a;代码格式&#xff1a;命名规范变量&#xff1a;函数&#xff1a;package&#xff1a;错误和异常处理&…

关于yolov8的训练的一些改动

1、YOLOv8创新改进点&#xff1a; 1.1.Backbone 使用的依旧是CSP的思想&#xff0c;不过YOLOv5中的C3模块被替换成了C2f模块&#xff0c;实现了进一步的轻量化&#xff0c;同时YOLOv8依旧使用了YOLOv5等架构中使用的SPPF模块&#xff1b; 1.2.PAN-FPN 毫无疑问YOLOv8依旧使…

大文件传输软件的优势有哪些?-镭速传输

互联网时代&#xff0c;大数据传输是企业面临的必不可免的问题&#xff0c;可以选择传统的FTP、网盘等方式来传输&#xff0c;对于小型文件或许是有优势的&#xff1b;但是对于大型文件数据的话&#xff0c;也许会出现传输速度慢&#xff0c;数据不可靠的情况&#xff0c;极大的…

python3+requests+unittest:接口自动化测试(一)

简单介绍框架的实现逻辑&#xff0c;参考代码的git地址&#xff1a; GitHub - zhangying123456/python_unittest_interface: pythonunittest接口自动化测试脚本 1.环境准备 python3 pycharm编辑器 2.框架目录展示 &#xff08;该套代码只是简单入门&#xff0c;有兴趣的可…

Nginx——Keepalived的原理与配置

摘要 Keepalived的作用是检测服务器的状态&#xff0c;如果有一台web服务器宕机&#xff0c;或工作出现故障&#xff0c;Keepalived将检测到&#xff0c;并将有故障的服务器从系统中剔除&#xff0c; 同时使用其他服务器代替该服务器的工作&#xff0c;当服务器工作正常后Keep…

python求解带约束的优化问题

带约束的优化问题可被定义为&#xff1a; 在python中&#xff0c;可以使用scipy的optimize包进行求解&#xff0c;具体求解函数为linprog&#xff0c;下面举例说明求解方法&#xff1a; 假设问题被定义为&#xff1a; 首先&#xff0c;求解最大值问题&#xff0c;我们可以通…

Spring Security 源码解读 :认证总览

Spring Security 提供如下几种认证机制&#xff1a; Username & PasswordOAuth2.0 LoginSAML 2.0 LoginRemember MeJAAS AuthenticationPre-authentication ScenariosX509 Authentication 这里使用Spring Boot 2.7.4版本&#xff0c;对应Spring Security 5.7.3版本 Serv…

LeetCode题目笔记——1588. 所有奇数长度子数组的和

文章目录题目描述题目难度——简单方法一&#xff1a;暴力代码/C代码/Python方法二&#xff1a;前缀和代码/C代码/Python总结题目描述 给你一个正整数数组 arr &#xff0c;请你计算所有可能的奇数长度子数组的和。 子数组 定义为原数组中的一个连续子序列。 请你返回 arr 中…

MySql性能优化(六)索引监控

文章目录索引监控Handler_read_firstHandler_read_keyHandler_read_lastHandler_read_nextHandler_read_prevHandler_read_rndHandler_read_rnd_next索引监控 SHOW STATUS LIKE Handler_read%解释一下各个参数的含义 Handler_read_first 通过index获取数据的次数 Handler_r…

在cmd中遍历局域网内的IP命令解析

简单的方法 1&#xff0c;直接通过浏览器访问路由器&#xff0c;通过路由器的页面查看。2&#xff0c;网络中很多扫描网络的软件&#xff0c;3&#xff0c;自己使用cmd命令查看 有时候自己也觉得&#xff0c;有简单的方式还用这麻烦的干嘛。但遇到不知道路由的登录密码呢&…