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

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 值存放到当前 对象集合当中。

voidputAll(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中的所有数据。

voidclear();  // 清空当前map中的所有数据
  • size() : 返回此集合中存储的元素数据(键值对)的数量。

intsize();  // 返回此集合中存储的元素数据(键值对)的数量。

举例:

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

publicclassMapTest {
    publicstaticvoidmain(String[] args) {
        // Map 接口 , HashMap实现类,多态,<String,Integer> 泛型
        Map<String,Integer> map = newHashMap<String,Integer>();

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

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

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

        Integerzhangsan= 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;

publicclassMapTest {
    publicstaticvoidmain(String[] args) {
        // Map 接口 , HashMap实现类,多态,<String,Integer> 泛型
        Map<String,Integer> map = newHashMap<String,Integer>();

        // 添加元素数据:
        map.put("zhangsan",66);
        map.put("lisi",89);
        
        Map<String,Integer> map2 = newHashMap<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值。

booleancontainsKey(Object key);  // 判断该集合当中是否包含指定的 key 值。
  • containsValue(Object key) : 判断该集合当中是否包含指定的 value 值。

booleancontainsValue(Object value); // 判断判断该集合当中是否包含指定的 value 值。
  • isEmpty() : 判断此集合是否为 空,是返回 true,不是返回 false。

booleanisEmpty();  // 判断此集合是否为 空,是返回 true,不是返回 false;
  • equals(Object o) : 判断当前map和参数对象 o 是否相等。

booleanequals(Object o); // 判断当前map和参数对象 o 是否相等

举例:

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

publicclassMapTest {
    publicstaticvoidmain(String[] args) {
        // Map 接口 , HashMap实现类,多态,<String,Integer> 泛型
        Map<String,Integer> map = newHashMap<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;

publicclassMapTest {
    publicstaticvoidmain(String[] args) {
        // Map 接口 , HashMap实现类,多态,<String,Integer> 泛型
        Map<String,Integer> map = newHashMap<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
*
*     Setset= 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;

publicclassMapTest {
    publicstaticvoidmain(String[] args) {
        // Map 接口 , HashMap实现类,多态,<String,Integer> 泛型
        Map<String,Integer> map = newHashMap<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;

publicclassHashMapTest {
    publicstaticvoidmain(String[] args) {
        HashMap<Person5,Integer> hashMap = newHashMap<Person5, Integer>();
        hashMap.put(newPerson5("Tom",19),1);
        hashMap.put(newPerson5("Tom",19),1);
        hashMap.put(newPerson5("Tom",19),1);
        hashMap.put(newPerson5("zhangsan",23),4);
        hashMap.put(newPerson5("lihua",20),5);

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

    }
}


classPerson5 {
    String name;
    int age;

    publicPerson5() {
    }

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

    publicvoidsetName(String name) {
        this.name = name;
    }

    publicvoidsetAge(int age) {
        this.age = age;
    }

    @Overridepublic 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;

publicclassHashMapTest {
    publicstaticvoidmain(String[] args) {
        HashMap<Person5,Integer> hashMap = newHashMap<Person5, Integer>();
        hashMap.put(newPerson5("Tom",19),1);
        hashMap.put(newPerson5("Tom",19),1);
        hashMap.put(newPerson5("Tom",19),1);
        hashMap.put(newPerson5("zhangsan",23),4);
        hashMap.put(newPerson5("lihua",20),5);

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

    }
}


classPerson5 {
    String name;
    int age;

    publicPerson5() {
    }

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

    publicvoidsetName(String name) {
        this.name = name;
    }

    publicvoidsetAge(int age) {
        this.age = age;
    }

    @Overridepublicbooleanequals(Object o) {
        if (this == o) returntrue;
        if (!(o instanceof Person5)) returnfalse;
        Person5person5= (Person5) o;
        returnage== person5.age &&
                Objects.equals(name, person5.name);
    }

    @OverridepublicinthashCode() {
        return Objects.hash(name, age);
    }

    @Overridepublic 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;

publicclassHashMapTest {
    publicstaticvoidmain(String[] args) {
        HashMap<String,Integer> hashMap = newHashMap<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.
*/staticfinalintDEFAULT_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.
*/staticfinalintMAXIMUM_CAPACITY=1 << 30;  // HashMap的最大支持容量,2^30
-----------------------------------------------------------------------------------
/**
* The load factor used when none specified in constructor.
*/staticfinalfloatDEFAULT_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.
*/staticfinalintTREEIFY_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.
*/staticfinalintUNTREEIFY_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.
*/staticfinalintMIN_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.
*/transientint 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).
*/transientint 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
*/finalfloat 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. */staticfinalfloatDEFAULT_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;

publicclassLinkedHashMapTest {
    publicstaticvoidmain(String[] args) {
        // 创建 LinkedHashMap 集合对象
        LinkedHashMap<String,Integer> linkedHashMap = newLinkedHashMap<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 接口

publicTreeMap(Comparator<? super K> comparator)v // 构造一个新的,空的树图,按照给定的比较器排序。
  • 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;

publicclassTreeMapTest {
    publicstaticvoidmain(String[] args) {
        // 创建TreeMap 集合对象,其中 Key 存储的类为自定义 Person6
        TreeMap<Person6,Integer> treeMap = newTreeMap<Person6,Integer>();

        // 添加元素
        treeMap.put(newPerson6("lihua",18),99);
        treeMap.put(newPerson6("zhangsan",20),89);
        treeMap.put(newPerson6("lisi",25),79);
        treeMap.put(newPerson6("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());
        }


    }
}

classPerson6 {
    String name;
    int age;

    publicPerson6() {
    }

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

    public String getName() {
        return name;
    }

    publicvoidsetName(String name) {
        this.name = name;
    }

    publicintgetAge() {
        return age;
    }

    publicvoidsetAge(int age) {
        this.age = age;
    }



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

    @OverridepublicinthashCode() {
        return Objects.hash(getName(), getAge());
    }

    @Overridepublic 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;

publicclassTreeMapTest {
    publicstaticvoidmain(String[] args) {
        // 创建TreeMap 集合对象,其中 Key 存储的类为自定义 Person6
        TreeMap<Person6,Integer> treeMap = newTreeMap<Person6,Integer>();

        // 添加元素
        treeMap.put(newPerson6("lihua",18),99);
        treeMap.put(newPerson6("zhangsan",20),89);
        treeMap.put(newPerson6("lisi",25),79);
        treeMap.put(newPerson6("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());
        }


    }
}

classPerson6implementsComparable<Person6>{
    String name;
    int age;

    publicPerson6() {
    }

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

    public String getName() {
        return name;
    }

    publicvoidsetName(String name) {
        this.name = name;
    }

    publicintgetAge() {
        return age;
    }

    publicvoidsetAge(int age) {
        this.age = age;
    }



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

    @OverridepublicinthashCode() {
        return Objects.hash(getName(), getAge());
    }

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

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

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

publicTreeMap(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;

publicclassTreeMapTest {
    publicstaticvoidmain(String[] args) {
        // 创建一个 TreeMap 集合对象,将匿名实现的比较器(定制排序),作为参数,传递给构造器
        TreeMap<Person6,Integer> treeMap = newTreeMap<Person6,Integer>(newComparator<Person6>() {
            @Overridepublicintcompare(Person6 o1, Person6 o2) {
                // 判断是否是对应比较的实例,其实这里我们可以不用判断的,因为使用的泛型限定if(o1 instanceof Person6 && o2 instanceof  Person6) {
                    Person6p1= (Person6)o1;
                    Person6p2= (Person6)o2;  // 向下转型为对应的实例对象,从而获取比较属性if(p1.age > p2.age) {
                        return -1;
                    } elseif(p1.age < p2.age) {
                        return1;
                    } else {
                        return0;
                    }

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

        // 添加元素
        treeMap.put(newPerson6("lihua",18),99);
        treeMap.put(newPerson6("zhangsan",20),89);
        treeMap.put(newPerson6("lisi",25),79);
        treeMap.put(newPerson6("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());
        }

    }
}

classPerson6implementsComparable<Person6>{
    String name;
    int age;

    publicPerson6() {
    }

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

    public String getName() {
        return name;
    }

    publicvoidsetName(String name) {
        this.name = name;
    }

    publicintgetAge() {
        return age;
    }

    publicvoidsetAge(int age) {
        this.age = age;
    }



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

    @OverridepublicinthashCode() {
        return Objects.hash(getName(), getAge());
    }
    

    @Overridepublic 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;

publicclassHashtableTest {
    publicstaticvoidmain(String[] args) {
        Hashtable<String,Integer> hashtable = newHashtable<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=Tompassword=abc123
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

publicclassPropertiesTest {
    publicstaticvoidmain(String[] args) {
        // Properties 集合常用来处理配置文件:key 和 value 都是String类型Propertiesproperties=newProperties();

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

        } catch (IOException e) {
            thrownewRuntimeException(e);
        }

        Stringname= properties.getProperty("name");
        Stringpassword= 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;

publicclassTraverse {

    // Map 集合遍历方式一:publicstaticvoidmain(String[] args) {
        Map<String, String> map = newHashMap<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;

publicclassTraverse {

    // Map 集合遍历方式二:publicstaticvoidmain(String[] args) {
        Map<String, String> map = newHashMap<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;

publicclassTraverse {

    // Map 集合遍历方式三:publicstaticvoidmain(String[] args) {
        Map<String, String> map = newHashMap<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;

publicclassTraverse {

    // Map集合遍历方式四:publicstaticvoidmain(String[] args) {
        Map<String, String> map = newHashMap<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 中元素的顺序

publicstaticvoidreverse(List<?> list);  // 反转 List 中元素的顺序
  • shuffle(List list): 对 List 集合元素进行随机排序

publicstaticvoidshuffle(List<?> list);  // 对 List 集合元素进行随机排序
  • sort(List list) : 根据元素的自然顺序对指定 List 集合元素按升序排序,注意的是: 排序需要存储的类有比较器调用 自然排序(实现 java.lang.Comparable的接口 / 定制排序 Comparator接口)

publicstatic <T extendsComparable<? super T>> voidsort(List<T> list);  // 根据元素的自然顺序对指定 List 集合元素按升序排序
  • sort(List list, Comparator c) : 根据指定的 Comparator 产生的顺序对 List 集合元素进行排序

publicstatic <T> voidsort(List<T> list,Comparator<? super T> c);  // 根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
  • swap(List list , int i , int j ) : 将指定 list 集合中的 i 处元素和 j 处元素进行交换。注意是左闭右开的。

publicstaticvoidswap(List<?> list,int i,int j);  // 将指定 list 集合中的 i 处元素和 j 处元素进行交换

查找,替换

  • max(Collection c) : 根据元素的自然顺序,返回给定集合中的最大元素。

publicstatic <T extendsObject & Comparable<? super T>> T max(Collection<? extends T> coll);  // 根据元素的自然顺序,返回给定集合中的最大元素
  • max(Collection coll, Comarator comp) : 根据Comparator 指定的顺序,返回给定集合中的最大元素。

publicstatic <T> T max(Collection<? extends T> coll,Comparator<? super T> comp);  // 根据 Comparator 指定的顺序,返回给定集合中的最大元素
  • min(Collection c) : 根据元素的自然顺序,返回给定集合中的最小元素。

publicstatic <T extendsObject & Comparable<? super T>> T min(Collection<? extends T> coll); // 根据元素的自然顺序,返回给定集合中的最小元素。
  • min(Collection coll, Comarator comp) : 根据Comparator 指定的顺序,返回给定集合中的最小元素。

publicstatic <T> T min(Collection<? extends T> coll, Comparator<? super T> comp);  // 根据Comparator 指定的顺序,返回给定集合中的最小元素。
  • replaceAll(List list,T oldVal,T newVal) : 用新值替换List 对象的所有旧值。注意存储的类需要重写 equals()方法,才能比较判断找到对应替换的值。

publicstatic <T> booleanreplaceAll(List<T> list,T oldVal,T newVal); // 用新值替换List 对象的所有旧值.
  • frequency(Collection c , Object o) : 返回指定集合中指定元素的出现次数,注意 :存储的类需要重写 equals()方法才能比较判断查找对应的值的个数。

publicstaticintfrequency(Collection<?> c,Object o);  // 返回指定集合中指定元素的出现次数
  • copy(List dest,List src) : 将 src 集合中的内容复制到 dest 集合当中。

publicstatic <T> voidcopy(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;

publicclassCollectionsTest {
    publicstaticvoidmain(String[] args) {
        List<Integer> src = newArrayList<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 = newArrayList<Integer>();
        desc = Arrays.asList(newInteger[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. 总结:

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

	@Testpublicvoidtest2() {
        Set<String> set = newHashSet<>();
        set.add("king");
        set.add("kingsoft");
        set.add("king2");
        set.add("king1");

        // 将Set集合转换成List集合
        List<String> myList = newArrayList<>(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. 最后:

限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善,谢谢大家,后会有期,江湖再见!!!
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值