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 值存放到当前 对象集合当中。
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 // 构造一个新的,空的树图,按照给定的比较器排序。
  • 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. 最后:

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

Java集合 Map 集合 与 操作集合的工具类: Collections 的详细说明的更多相关文章

  1. 集合中的工具类Collections和Arrays

    集合框架的工具类: Collections: 方法sort(): List<String> list = new ArrayList<String>();        lis ...

  2. 操作集合的工具类Collections

    1       操作集合的工具类Collections Java提供了一个操作Set.List和Map等集合的工具类:Collections,该工具类里提供了大量方法对集合元素进行排序.查询和修改等操 ...

  3. Java从零开始学二十四(集合工具类Collections)

    一.Collections简介 在集合的应用开发中,集合的若干接口和若干个子类是最最常使用的,但是在JDK中提供了一种集合操作的工具类 —— Collections,可以直接通过此类方便的操作集合 二 ...

  4. Java笔记(二十四)……集合工具类Collections&Arrays

    Collections 集合框架的工具类,方法全部为静态 Collections与Collection的区别 Collection是集合框架的一个顶层接口,里面定义了单列集合的共性方法 Collect ...

  5. Java:集合工具类-Collections

    Java.util.Collections 集合框架工具类Collections,其方法都是静态的,本身没有构造函数. 常见方法: static <T extends Comparable< ...

  6. 通过CollectionUtils工具类判断集合是否为空,通过StringUtils工具类判断字符串是否为空

    通过CollectionUtils工具类判断集合是否为空 先引入CollectionUtils工具类: import org.apache.commons.collections4.Collectio ...

  7. 最全的Java操作Redis的工具类,使用StringRedisTemplate实现,封装了对Redis五种基本类型的各种操作!

    转载自:https://github.com/whvcse/RedisUtil 代码 ProtoStuffSerializerUtil.java import java.io.ByteArrayInp ...

  8. java里poi操作excel的工具类(兼容各版本)

    转: java里poi操作excel的工具类(兼容各版本) 下面是文件内具体内容,文件下载: import java.io.FileNotFoundException; import java.io. ...

  9. Java操作字符串的工具类

    操作字符串的工具类 import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintStre ...

  10. Java操作图片的工具类

    操作图片的工具类: import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Font; import java.a ...

随机推荐

  1. mysql是如何实现mvcc的

    mvcc的概念 mvcc即多版本并发控制,是一种并发控制的策略,能让数据库在高并发下做到安全高效的读写,提升数据库的并发性能; 是一种用来解决并发下读写冲突的无锁解决方案,为事务分配单向增长时间戳,为 ...

  2. C#多线程之线程高级(下)

    四.Monitor信号构造 信号构造本质:一个线程阻塞直到收到另一个线程发来的通知. 当多线程Wait同一对象时,就形成了一个"等待队列(waiting queue)",和用于等待 ...

  3. 利用KubeEdge在A500部署边缘推理任务

      利用KubeEdge在A500部署边缘推理任务 目  录 1 环境介绍... 1 2 云端环境部署... 2 2.1 在master节点安装Docker和k8S (ubuntu) 2 2.1.1 ...

  4. i春秋Fuzz

    点开只有三个单词plz fuzz parameter 大概意思就是让我们疯狂尝试参数... 我们通过url尝试传入参数 ?user=123 ?name=123 ?username=123 ?id=12 ...

  5. i春秋phone number

    点开题目是一个普普通通的登录注册界面,随便注册一个点进去有两个功能,一个是查看电话和你相同的用户,一个是登出. 点击查询就可以看到用户数 这里有访问数据库的操作应该,所以就应该用数据库注入来解题. 又 ...

  6. 痞子衡嵌入式:MCUXpresso IDE下高度灵活的FreeMarker链接文件模板机制

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是MCUXpresso IDE下高度灵活的FreeMarker链接文件模板机制. 痞子衡之前写过一篇文章 <MCUXpresso I ...

  7. js day04 实参与形参个数不一致

    // function fn(x, y) {         //     // x = 1         //     // y = undefined         //     // 1 + ...

  8. 【微服务架构设计实施】第一部分:架构篇-1:微服务架构与Spring Cloud介绍

    〇.概述 一.微服务架构与Spring Cloud (一)概念 不同说法:细粒度的.清凉组件化的小型SOA(面向服务架构) 统一说法:小型应用程序(服务组件),使用轻量级设计方法和HTTP协议通信 理 ...

  9. 【Shell案例】【awk map计数&sort按指定列排序】9、统计每个单词出现的个数

    描述写一个 bash脚本以统计一个文本文件 nowcoder.txt 中每个单词出现的个数. 为了简单起见,你可以假设:nowcoder.txt只包括小写字母和空格.每个单词只由小写字母组成.单词间由 ...

  10. 单一JVM同步锁实现

    同步锁实现 一.背景 在并发场景下,需要单一线程或限定并发数操作某些逻辑,这时候就需要用到一个锁来保证线程安全. 二.思路 使用ConcurrentHashMap实现,但只支持同一个jvm下的线程(暂 ...