一、基本功能
Map.put方法用于将一个键值对存入Map中。语法为:
V put(K key, V value)
其中,key是键,value是值。如果HashMap中没有这个键,则将键值对存入HashMap中,并返回null;否则用value替换原有的值,返回旧值。示例代码如下:
Mapmap = new HashMap<>(); map.put("A", 1); map.put("B", 2); map.put("C", 3); Integer oldValue = map.put("A", 0); System.out.println(oldValue); System.out.println(map);
输出结果为:
1 {A=0, B=2, C=3}
我们可以看到,由于原来有键"A",所以put方法返回旧值1,并用键"A"对应的新值0替换旧值1。
二、多线程并发问题
在多线程并发场景下,通过put方法向HashMap中添加元素会涉及到线程安全问题。当多个线程同时调用put方法时,可能会引起HashMap的不一致性。
在JDK1.8及之前的版本中,HashMap的并发修改可能会引发无限循环和数据丢失等问题。为了解决这一问题,JDK1.8对HashMap做了一些优化,例如增加了红黑树等。另外,在并发场景下,建议使用ConcurrentHashMap或者同步HashMap,它们可以保证线程安全。示例代码如下:
Mapmap = new ConcurrentHashMap<>(); // 或者 Map map = Collections.synchronizedMap(new HashMap ());
三、键值类型限制
在使用Map.put方法时,需要注意键值类型的限制。它们都需要定义equals()和hashCode()方法,以保证不同的键具有不同的hashCode值。否则,可能会导致键的重复插入或者查找时出现问题。
例如,如果使用自定义对象作为键,必须重写equals()和hashCode()方法,否则Map.put方法会把两个不同对象当作同一个键。示例代码如下:
class Person { private String name; public Person(String name) { this.name = name; } public String getName() { return name; } @Override public boolean equals(Object obj) { if (obj == null || !(obj instanceof Person)) { return false; } Person p = (Person) obj; return name.equals(p.getName()); } @Override public int hashCode() { return name.hashCode(); } } Mapmap = new HashMap<>(); map.put(new Person("Tom"), 18); map.put(new Person("Tom"), 20); System.out.println(map);
输出结果为:
{Person@7c53f378=20}
由于自定义对象Person没有重写hashCode()方法,因此put方法将两个不同的Person对象看作同一个键,导致无法存放同名Person的不同值。重写hashCode()方法后,上述代码就可以正常运行。
四、性能问题
在大量数据插入Map时,使用循环多次调用put方法的效率不高,会导致时间复杂度变成O(n^2)级别。此时,我们可以使用Map.putAll方法,一次性把多个键值对添加到Map中。示例代码如下:
Mapmap1 = new HashMap<>(); map1.put("A", 1); map1.put("B", 2); map1.put("C", 3); Map map2 = new HashMap<>(); map2.put("D", 4); map2.put("E", 5); map1.putAll(map2); System.out.println(map1);
输出结果为:
{A=1, B=2, C=3, E=5, D=4}
除此之外,我们还可以使用Stream流来快速填充Map。示例代码如下:
Mapmap = Stream.of(new String[][]{ {"A", "1"}, {"B", "2"}, {"C", "3"} }).collect(Collectors.toMap(e -> e[0], e -> Integer.valueOf(e[1]))); System.out.println(map);
输出结果为:
{A=1, B=2, C=3}
五、容量和负载因子
HashMap内部使用数组存储键值对,当数组不足以存储所有键值对时,HashMap会自动扩容。扩容时,HashMap会创建一个新的数组,并把所有键值对重新分配到新数组里。对于大规模的数据,这样的操作会比较耗时。
因此,我们需要在创建HashMap对象时指定初始容量和负载因子。容量是指HashMap的底层数组初始长度,负载因子是指底层数组在达到多少满时,需要进行扩容操作。可通过Map.put方法的手动扩容能更好避免系统自动扩容耗费大量的时间。代码示例如下:
Mapmap = new HashMap<>(16, 0.75f); map.put("A", 1); map.put("B", 2); System.out.println(map.size());
输出结果为:
2
六、总结
本文从基本功能、多线程并发问题、键值类型限制、性能问题和容量负载因子等方面对Java中Map.put方法进行了详细的分析和解释。希望可以对Java开发工程师有所帮助。