目录
上一篇:Collection(LIst,Set)和Iterator(迭代器)
一、Map接口概述
二、特点
三、Map中key-value特点
四、常用方法
五、键值遍历
六、Map的主要实现类:HashMap
1.特点
2.实例:将省份和城市的名称保存在集合中,当用户选择省份以后,二级联动,显示对应省份的地级市供用户选择。
七、Map实现类之二:LinkedHashMap
1.特点
八、Map实现类之三:TreeMap
1.特点
自然排序:TreeMap 的所有的 Key 必须实现 Comparable 接口,而且所有的 Key 应该是同一个类的对象,否则将会抛出 ClasssCastException
定制排序:创建 TreeMap 时,构造器传入一个 Comparator 对象,该对象负责对 TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现 Comparable 接口
九、Map实现类之四:Hashtable
特点
Hashtable和HashMap的区别:
十、Map实现类之五:Properties
特点:
示例:
十一、Collections工具类
排序操作:
例如:编写一个程序,对用户输入的 5 个商品价格进行排序后输出。这里要求使用 Collections 类中 sort() 方法按从低到高的顺序对其进行排序,最后将排序后的成绩输出。
查找操作:
复制、替换、添加操作:
同步操作:
模拟洗牌打牌小游戏:
总结:
Map与Collection并列存在。用于保存具有映射关系
的数据:key-value
Collection
集合称为单列集合,元素是孤立存在的(理解为单身)。
Map
集合称为双列集合,元素是成对存在的(理解为夫妻)。
Map 中的 key 和 value 都可以是任何引用类型的数据。
高效的查找操作:Map提供了根据键快速查找对应值的方法,例如get(key)方法。
动态可变大小:Map的大小是可变的,可以根据需要动态地添加、修改和删除键值对。
顺序性:Map的实现类可以根据插入顺序或其他规则来维护键值对的顺序。例如,LinkedHashMap会根据插入顺序或访问顺序保持键值对的顺序,而HashMap则没有顺序保证。
迭代:Map提供了遍历所有键值对的方法,例如keySet()、entrySet()和values()等,通过这些方法可以获取Map中的键、键值对或值的集合,进行遍历和操作。
多种实现:Java提供了多个Map接口的实现类,包括HashMap、TreeMap、LinkedHashMap等,每个实现类都有自己的特点和适用场景。
键值对映射:Map接口用于存储一组键值对,其中每个键都是唯一的,并且每个键都与一个值相关联。
键的唯一性:Map中的键是唯一的,没有重复的键。如果多次插入相同的键,则后面的键值对会覆盖之前的键值对。
值的重复性:Map中的值可以重复,可以使用不同的键关联相同的值
操作类型 | 方法名 | 描述 |
---|---|---|
添加、修改操作 | Object put(Object key,Object value) | 将指定key-value添加到(或修改)当前map对象中 |
void putAll(Map m) | 将m中的所有key-value对存放到当前map中 | |
删除操作 | Object remove(Object key) | 移除指定key的key-value对,并返回value |
void clear() | 清空当前map中的所有数据 | |
元素查询的操作 | Object get(Object key) | 获取指定key对应的value |
boolean containsKey(Object key) | 是否包含指定的key | |
boolean containsValue(Object value) | 是否包含指定的value | |
int size() | 返回map中key-value对的个数 | |
boolean isEmpty() | 判断当前map是否为空 | |
boolean equals(Object obj) | 判断当前map和参数对象obj是否相等 | |
元视图操作的方法 | Set keySet() | 返回所有key构成的Set集合 |
Collection values() | 返回所有value构成的Collection集合 | |
Set entrySet() | 返回所有key-value对构成的Set集合 |
public class Test {
public static void main(String[] args) {
Map studentMap = new HashMap<>();
studentMap.put("Alice", 18);
studentMap.put("Bob", 20);
studentMap.put("Charlie", 19);
studentMap.put("Alice", 21);
System.out.println("使用keySet()遍历键:");
for (String name : studentMap.keySet()) {
System.out.println("Name: " + name );
//根据建就能得到值
//System.out.println("Name: " + name + ", Age: " + studentMap.get(name));
}
System.out.println("使用values()遍历值:");
for (Integer age : studentMap.values()) {
System.out.println("Age: " + age);
}
System.out.println("使用entrySet()遍历键值对:");
for (Map.Entry entry : studentMap.entrySet()) {
String name = entry.getKey();
Integer age = entry.getValue();
System.out.println("Name: " + name + ", Age: " + age);
}
System.out.println("使用迭代器遍历键值对:");
Iterator> iterator = studentMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry entry = iterator.next();
String name = entry.getKey();
Integer age = entry.getValue();
System.out.println("Name: " + name + ", Age: " + age);
}
System.out.println("使用Lambda表达式遍历:");
studentMap.forEach((name, age) -> {
System.out.println("Name: " + name + ", Age: " + age);
});
}
}
输出:
使用keySet()遍历键:
Name: Bob
Name: Alice
Name: Charlie
使用values()遍历值:
Age: 20
Age: 21
Age: 19
使用entrySet()遍历键值对:
Name: Bob, Age: 20
Name: Alice, Age: 21
Name: Charlie, Age: 19
使用迭代器遍历键值对:
Name: Bob, Age: 20
Name: Alice, Age: 21
Name: Charlie, Age: 19
使用Lambda表达式遍历:
Name: Bob, Age: 20
Name: Alice, Age: 21
Name: Charlie, Age: 19
它有Map的所以特点,另外:
无序性: HashMap中的键值对没有固定的顺序,不保证插入顺序和存储顺序一致。
允许为空: HashMap中的键和值都可以为null。但是只能有一个键为null,因为HashMap的键是唯一的。
线程不安全: HashMap不是线程安全的,多线程环境下需要自行实现同步机制。
高效性能: 在大多数情况下,HashMap的插入、删除和查找操作的时间复杂度为O(1)。但是在极端情况下可能会退化为O(n)。
存储数据采用的哈希表结构,底层使用一维数组
+单向链表
+红黑树
进行key-value数据的存储
public class Test {
public static void main(String[] args) {
for(Object s : CityMap.model.keySet()) {
System.out.print(s + " ");
}
System.out.println();
System.out.println("请选择你所在的省份:");
Scanner scan = new Scanner(System.in);
String province = scan.next();
String[] citys = (String[])CityMap.model.get(province);
for(String city : citys) {
System.out.print(city + " ");
}
System.out.println();
System.out.println("请选择你所在的城市:");
String city = scan.next();
System.out.println("信息登记完毕");
}
}
黑龙江 上海 吉林 天津 河北 重庆 北京
请选择你所在的省份:
黑龙江
哈尔滨 齐齐哈尔 牡丹江 大庆 伊春 双鸭山 绥化
- 请选择你所在的城市:
大庆
信息登记完毕
LinkedHashMap 是 HashMap 的子类(具有HashMap的特点)
顺序一致:存储数据采用的哈希表结构+链表结构,在HashMap存储结构的基础上,使用了一对双向链表
来记录添加元素的先后顺序
,可以保证遍历元素时,与添加的顺序一致。
自然排序
:TreeMap 的所有的 Key 必须实现 Comparable 接口,而且所有的 Key 应该是同一个类的对象,否则将会抛出 ClasssCastExceptionpublic class Test {
public static void main(String[] args) {
TreeMap map = new TreeMap();
map.put("CC", 45);
map.put("MM", 44);
map.put("DD", 56);
map.put("GG", 19);
map.put("JJ", 99);
Set entrySet = map.entrySet();
for (Object entry : entrySet) {
System.out.println(entry);
}
}
}
定制排序
:创建 TreeMap 时,构造器传入一个 Comparator 对象,该对象负责对 TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现 Comparable 接口public class Test {
public static void main(String[] args) {
//按照User的姓名的从小到大的顺序排列
TreeMap map = new TreeMap(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof User && o2 instanceof User){
User u1 = (User)o1;
User u2 = (User)o2;
return u1.name.compareTo(u2.name);
}
throw new RuntimeException("输入的类型不匹配");
}
});
map.put(new User("Tom",12),67);
map.put(new User("Rose",23),"87");
map.put(new User("Jerry",2),88);
map.put(new User("Eric",18),45);
Set entrySet = map.entrySet();
for(Object entry : entrySet){
System.out.println(entry);
}
}
}
高效的查找操作:由于TreeMap基于红黑树,查找操作的时间复杂度为O(log n),因此查找特定键的值非常高效。这使得TreeMap在需要频繁进行查找操作的场景下具有较好的性能。
支持范围查询:TreeMap提供了一些方法来支持范围查询,如subMap()
、headMap()
和tailMap()
。这些方法允许按范围获取子映射或头部/尾部映射。
线程安全性:Hashtable是线程安全的,多个线程可以同时访问Hashtable并进行读写操作,因此在多线程环境下可以直接使用Hashtable而无需担心并发问题。Hashtable通过采用同步机制来实现线程安全。
不允许null键或null值:Hashtable不允许存储null键或null值,如果尝试存储null键或null值,将会抛出 NullPointerException 异常。
效率较低:由于Hashtable是线程安全的,它使用了同步的机制来保证线程安全性。然而,这也导致了Hashtable的性能相对较低,特别是在高并发环境下。
Hashtable是Map接口的古老实现类
,JDK1.0就提供了。
线程安全性:Hashtable是线程安全的,多个线程可以同时访问Hashtable并进行读写操作,而HashMap不是线程安全的。如果需要在多线程环境下使用哈希表,可以选择Hashtable来保证线程安全性。
允许null键或null值:Hashtable不允许存储null键或null值,如果尝试存储null键或null值,将会抛出NullPointerException异常。而HashMap允许存储一个null键和多个null值。
效率:由于Hashtable是线程安全的,它使用了同步机制来保证线程安全性。然而,这也导致了Hashtable的性能相对较低,特别是在高并发环境下。而HashMap不需要进行同步操作,因此具有更高的性能。
迭代器:在Java 8及以后的版本中,HashMap提供了更快速的迭代器(Iterator)和遍历方式,而Hashtable则较旧的Java版本只提供了Enumeration迭代器。
综上所述,Hashtable和HashMap在线程安全性、允许null键值、性能和迭代器方面存在一些差异。如果不需要考虑线程安全和允许存储null键值,更倾向于使用HashMap。如果需要线程安全性,可以考虑使用Hashtable,但要注意性能问题。
Properties是Java中的一个类,它继承自Hashtable类,用于处理属性文件(.properties)的读取和保存。属性文件通常用于存储配置信息,例如数据库连接参数、系统设置等。
键值对都为字符串:Properties类以键值对的形式存储数据,由于属性文件里的 key、value 都是字符串类型,所以键和值都为字符串类型。每个键值对之间使用等号(=)或冒号(:)进行分隔。
读取属性文件:Properties类提供了load()方法,可以从属性文件中读取键值对数据。属性文件的格式为每行一个键值对,以等号或冒号分隔键和值。
存储属性值:Properties类提供了setProperty()和getProperty()方法用于设置和获取属性值。可以通过键获取对应的值,也可以使用默认值来获取属性值。
输出属性文件:Properties类提供了store()方法,可以将属性键值对数据保存到属性文件中。输出的属性文件格式为默认的 ISO 8859-1 字符编码,可以使用其他字符编码进行保存。
public class Test {
public static void main(String[] args) {
Properties properties = new Properties();
try {
FileInputStream input = new FileInputStream("D:\\123.txt");
properties.load(input);
input.close();
} catch (IOException e) {
e.printStackTrace();
}
// 获取属性值
String username = properties.getProperty("username");
String password = properties.getProperty("password");
System.out.println("Username: " + username);
System.out.println("Password: " + password);
// 设置属性值
properties.setProperty("database", "mysql");
properties.setProperty("port", "3306");
// 将配置信息保存到属性文件中
try {
FileOutputStream output = new FileOutputStream("D:\\123.txt");
properties.store(output, "Test");
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
输出:
Username: 2938
Password: 1111
参考操作数组的工具类:Arrays,Collections 是一个操作 Set、List 和 Map 等集合的工具类。
方法名 | 功能描述 |
---|---|
reverse(List list) | 对指定 List 集合元素进行逆向排序。 |
shuffle(List list) | 对 List 集合元素进行随机排序(shuffle 方法模拟了“洗牌”动作)。 |
sort(List list) | 根据元素的自然顺序对指定 List 集合的元素按升序进行排序。 |
sort(List list, Comparator c) | 根据指定 Comparator 产生的顺序对 List 集合元素进行排序。 |
swap(List list, int i, int j) | 将指定 List 集合中的 i 处元素和 j 处元素进行交换。 |
rotate(List list, int distance) | 当 distance 为正数时,将 list 集合的后 distance 个元素“整体”移到前面;当 distance 为负数时,将 list 集合的前 distance 个元素“整体”移到后面。该方法不会改变集合的长度。 |
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
ArrayList prices = new ArrayList<>();
// 通过循环获取用户输入的 5 个商品价格
Scanner scanner = new Scanner(System.in);
for (int i = 0; i < 5; i++) {
System.out.print("请输入第 " + (i + 1) + " 个商品的价格:");
double price = scanner.nextDouble();
prices.add(price);
}
// 使用 Collections 类的 sort() 方法对价格进行排序
Collections.sort(prices);
// 输出排序后的价格
System.out.println("排序后的商品价格为:");
for (double price : prices) {
System.out.println(price);
}
}
}
请输入第 1 个商品的价格:10
请输入第 2 个商品的价格:22
请输入第 3 个商品的价格:66
请输入第 4 个商品的价格:22
请输入第 5 个商品的价格:11
排序后的商品价格为:
10.0
11.0
22.0
22.0
66.0
方法名称 | 描述 |
---|---|
Object max(Collection) | 根据元素的自然顺序,返回给定集合中的最大元素 |
Object max(Collection,Comparator) | 根据 Comparator 指定的顺序,返回给定集合中的最大元素 |
Object min(Collection) | 根据元素的自然顺序,返回给定集合中的最小元素 |
Object min(Collection,Comparator) | 根据 Comparator 指定的顺序,返回给定集合中的最小元素 |
int binarySearch(List list,T key) | 在List集合中查找(二分查找)某个元素的下标,但是List的元素必须是T或T的子类对象,而且必须是可比较大小的,即支持自然排序的,且集合也必须是有序的。否则结果不确定。 |
int binarySearch(List list,T key,Comparator c) | 在List集合中查找某个元素的下标,但是List的元素必须是T或T的子类对象,而且集合也必须是按照c比较器规则进行排序过的,否则结果不确定。 |
int frequency(Collection c,Object o) | 返回指定集合中指定元素的出现次数 |
方法 | 功能 |
---|---|
void copy(List dest,List src) | 将src中的内容复制到dest中 |
boolean replaceAll(List list,Object oldVal,Object newVal) | 使用新值替换 List 对象的所有旧值 |
boolean addAll(Collection c,T... elements) | 将所有指定元素添加到指定 collection 中。 |
示例:在一个集合中保存了 5 个商品名称,现在要使用 Collections 类中的 copy() 方法将其中的 3 个替换掉。具体实现的代码
public class Test {
public static void main(String[] args) {
ArrayList originalNames = new ArrayList<>();
originalNames.add("商品1");
originalNames.add("商品2");
originalNames.add("商品3");
originalNames.add("商品4");
originalNames.add("商品5");
// 创建替换商品名称的集合
ArrayList newNames = new ArrayList<>();
newNames.add("新商品1");
newNames.add("新商品2");
newNames.add("新商品3");
// 使用Collections类的copy()方法将新名称列表的元素替换到原始名称列表中
Collections.copy(originalNames, newNames);
// 输出替换后的商品名称集合
System.out.println("替换后的商品名称集合:");
for (String name : originalNames) {
System.out.println(name);
}
}
}
替换后的商品名称集合:
新商品1
新商品2
新商品3
商品4
商品5
方法名 | 描述 |
---|---|
synchronizedCollection(Collection |
返回由指定集合支持的同步(线程安全)集合。 |
synchronizedList(List |
返回由指定列表支持的同步(线程安全)列表。 |
synchronizedMap(Map |
返回由指定地图支持的同步(线程安全)映射。 |
synchronizedNavigableMap(NavigableMap |
返回由指定的可导航地图支持的同步(线程安全)可导航地图。 |
synchronizedNavigableSet(NavigableSet |
返回由指定的可导航集支持的同步(线程安全)可导航集。 |
synchronizedSet(Set |
返回由指定集合支持的同步(线程安全)集。 |
synchronizedSortedMap(SortedMap |
返回由指定的排序映射支持的同步(线程安全)排序映射。 |
synchronizedSortedSet(SortedSet |
返回由指定的排序集支持的同步(线程安全)排序集。 |
import java.util.ArrayList;
import java.util.Collections;
public class DouDiZhu {
public static void main(String[] args) {
// 创建一副扑克牌
ArrayList pokerDeck = new ArrayList<>();
String[] suits = {"♠️", "♥️", "♣️", "♦️"};
String[] ranks = {"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"};
for (String suit : suits) {
for (String rank : ranks) {
pokerDeck.add(suit + rank);
}
}
pokerDeck.add("小王");
pokerDeck.add("大王");
// 洗牌
Collections.shuffle(pokerDeck);
// 发牌
ArrayList player1 = new ArrayList<>();
ArrayList player2 = new ArrayList<>();
ArrayList player3 = new ArrayList<>();
ArrayList landlord = new ArrayList<>();
for (int i = 0; i < pokerDeck.size() - 3; i++) {
if (i % 3 == 0) {
player1.add(pokerDeck.get(i));
} else if (i % 3 == 1) {
player2.add(pokerDeck.get(i));
} else {
player3.add(pokerDeck.get(i));
}
}
for (int i = pokerDeck.size() - 3; i < pokerDeck.size(); i++) {
landlord.add(pokerDeck.get(i));
}
// 输出玩家手中的牌和地主牌
System.out.println("玩家1的牌:" + player1);
System.out.println("玩家2的牌:" + player2);
System.out.println("玩家3的牌:" + player3);
System.out.println("地主的牌:" + landlord);
}
}
玩家1的牌:[♦️10, ♣️9, ♦️Q, ♠️Q, ♠️4, ♠️6, ♣️8, ♣️3, ♦️8, ♦️9, ♥️K, ♦️A, ♣️7, ♣️Q, ♠️J, ♦️3, ♣️10]
玩家2的牌:[♠️K, ♥️6, ♠️10, ♥️7, ♥️2, ♦️6, ♣️6, ♥️10, ♣️5, ♥️3, ♠️9, ♦️7, 大王, ♣️K, ♠️7, ♠️A, ♦️J]
玩家3的牌:[♠️5, ♣️4, ♦️K, ♠️3, ♠️2, ♥️J, 小王, ♥️5, ♣️A, ♣️2, ♥️8, ♥️4, ♣️J, ♦️5, ♦️2, ♦️4, ♠️8]
地主的牌:[♥️Q, ♥️A, ♥️9]
HashMap
:使用哈希表实现。它是最常用的Map
实现类之一,提供了快速的查找和插入操作。不保证元素的顺序,也不保证对元素的迭代顺序。允许使用null
键和null
值。TreeMap
:使用红黑树实现。按照键的自然顺序或者自定义的比较器对键进行排序。保证元素按照键的升序排列。不允许使用null
键,但允许使用null
值。LinkedHashMap
:使用哈希表和双向链表实现。按照插入顺序或者最近访问顺序保持元素的顺序。保证元素的迭代顺序与插入顺序或访问顺序相同。允许使用null
键和null
值。Hashtable
:使用哈希表实现。它是早期的Map
实现类,线程安全,但性能相对较差。不保证元素的顺序。不允许使用null
键和null
值。