HashMap与HashTable
HashMap
HashMap 是 Java 集合框架中最常用的类之一,它实现了 Map 接口,基于哈希表(Hash Table)的数据结构。HashMap 提供了高效的键值对存储和检索操作,适合存储大量数据
特点
- HashMap键不能重复,值可以相同,如果插入相同的键则会覆盖(如果onlyIfAbsent为false)
- 基于哈希表实现,HashMap 内部使用哈希表来存储键值对,哈希表是一种高效的数据结构,支持快速的插入、删除和查询操作
- 允许 null 键和 null 值,HashMap 允许键和值为 null,但只能有一个 null 键
- 无序性,HashMap 不保证键值对的顺序,元素的存储顺序可能与插入顺序不同
- HashMap 不是线程安全的,如果需要在多线程环境中使用使用 ConcurrentHashMap
HashMap的遍历
-
通过 keySet() 方法获取所有键,然后遍历键
import java.util.HashMap; import java.util.Map; import java.util.Set; public class HashMapTraversalExample { public static void main(String[] args) { Map<String, Integer> map = new HashMap<>(); map.put("apple", 10); map.put("banana", 20); map.put("cherry", 30); // 遍历键 Set<String> keys = map.keySet(); for (String key : keys) { System.out.println("Key: " + key); } } } -
通过 values() 方法获取所有值,然后遍历值
import java.util.Collection; import java.util.HashMap; import java.util.Map; public class HashMapTraversalExample { public static void main(String[] args) { Map<String, Integer> map = new HashMap<>(); map.put("apple", 10); map.put("banana", 20); map.put("cherry", 30); // 遍历值 Collection<Integer> values = map.values(); for (Integer value : values) { System.out.println("Value: " + value); } } } -
通过 entrySet() 方法获取所有键值对,然后遍历键值对
import java.util.HashMap; import java.util.Map; import java.util.Set; public class HashMapTraversalExample { public static void main(String[] args) { Map<String, Integer> map = new HashMap<>(); map.put("apple", 10); map.put("banana", 20); map.put("cherry", 30); // 遍历键值对 Set<Map.Entry<String, Integer>> entries = map.entrySet(); for (Map.Entry<String, Integer> entry : entries) { System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue()); } } } -
使用 forEach 方法(Java 8+),通过 forEach 方法和 Lambda 表达式遍历 HashMap
import java.util.HashMap; import java.util.Map; public class HashMapTraversalExample { public static void main(String[] args) { Map<String, Integer> map = new HashMap<>(); map.put("apple", 10); map.put("banana", 20); map.put("cherry", 30); // 使用 forEach 方法遍历 map.forEach((key, value) -> { System.out.println("Key: " + key + ", Value: " + value); }); } }
HashMap底层结构
与HashSet相同
- HashMap 的底层是一个哈希表,核心字段包括
- Node<K,V>[] table:哈希表,用于存储键值对。
- int size:HashMap 中键值对的数量。
- int threshold:扩容阈值,当 size 超过 threshold 时,会触发扩容。
- float loadFactor:负载因子,用于计算扩容阈值(默认值为 0.75)
- 哈希表的结构
- 哈希表是一个数组,数组的每个元素是一个链表或红黑树。
- 当哈希冲突较少时,使用链表存储冲突的键值对。
- 当哈希冲突较多时(链表长度超过 8 且哈希表容量大于 64),将链表转换为红黑树
- 扩容机制
- 当 HashMap 中的键值对数量超过 threshold 时,会触发扩容。
- 扩容时,哈希表的容量会加倍,并重新计算所有键值对的哈希值,将其分配到新的桶中
HashTable
- HashTable:HashTable 是 Java 早期提供的哈希表实现,从 JDK 1.0 就已经存在,设计之初就考虑了线程安全问题,不过它的实现方式比较粗糙
- HashTable 不允许键或值为 null。如果尝试将 null 作为键或值插入 HashTable 中,会抛出 NullPointerException
- HashTable 使用 Enumeration 进行迭代,Enumeration 是一个较老的迭代器接口,功能相对有限,没有 remove 方法,只能遍历元素
- HashTable 的初始容量是 11,扩容时会将容量变为原来的 2 倍加 1(oldCapacity * 2 + 1)
Properties
- Properties 类是 Hashtable<Object,Object> 的子类,而 Hashtable 实现了 Map 接口,所以 Properties 本质上也是一个双列集合
- Properties常用于文件中字符串的保存读取
常用方法
-
setProperty(String key, String value):调用 Hashtable 的方法 put,将键值对存储到 Properties 对象中
import java.util.Properties; public class PropertiesExample { public static void main(String[] args) { Properties properties = new Properties(); properties.setProperty("database.url", "jdbc:mysql://localhost:3306/mydb"); properties.setProperty("database.username", "root"); properties.setProperty("database.password", "password123"); } } -
getProperty(String key):用指定的键在此属性列表中搜索属性。如果此属性列表中没有该键,则会搜索默认属性列表及其默认值。如果仍未找到属性,则返回 null
-
getProperty(String key, String defaultValue):与 getProperty(String key) 类似,但如果未找到属性,会返回指定的默认值
import java.util.Properties; public class PropertiesExample { public static void main(String[] args) { Properties properties = new Properties(); properties.setProperty("database.url", "jdbc:mysql://localhost:3306/mydb"); String url = properties.getProperty("database.url"); String port = properties.getProperty("database.port", "3306"); System.out.println("URL: " + url); System.out.println("Port: " + port); } } -
load(InputStream inStream):从输入流中读取属性列表(键和元素对)。输入流可以是文件输入流等
import java.io.FileInputStream; import java.io.IOException; import java.util.Properties; public class PropertiesExample { public static void main(String[] args) { Properties properties = new Properties(); try (FileInputStream fis = new FileInputStream("config.properties")) { properties.load(fis); String username = properties.getProperty("database.username"); System.out.println("Username: " + username); } catch (IOException e) { e.printStackTrace(); } } } -
store(OutputStream out, String comments):将此 Properties 表中的属性列表(键和元素对)写入输出流。comments 是可选的注释信息
import java.io.FileOutputStream; import java.io.IOException; import java.util.Properties; public class PropertiesExample { public static void main(String[] args) { Properties properties = new Properties(); properties.setProperty("app.name", "MyApp"); properties.setProperty("app.version", "1.0"); try (FileOutputStream fos = new FileOutputStream("app.properties")) { properties.store(fos, "Application Configuration"); } catch (IOException e) { e.printStackTrace(); } } }
使用场景和优缺点
- 常常使用 Properties 类来读取和写入配置文件,如数据库连接配置、应用程序的全局设置等
- 可以将不同语言的文本信息存储在属性文件中,通过 Properties 类加载,实现多语言支持
- 简单易用:Properties 类提供了简单的键值对存储和操作方法,易于上手。
- 跨平台:属性文件以文本形式存储,方便在不同平台上使用
- 数据类型单一:只能存储字符串类型的键值对,如果需要存储其他数据类型,需要进行额外的转换。
- 不适合大规模数据:对于大规模的数据存储和管理,Properties 类的性能和功能可能不够,需要使用更专业的数据库或数据存储方案

浙公网安备 33010602011771号