Java中的集合类
1、HashMap
1.1、HashMap的简介
HashMap集合和之前介绍的ArrayList集合、LinkedList集合和HashSet集合本质上是有区别的,区别在于:HashMap集合进行是双值存储,存储的元素为键值对,而后面列举的集合都是进行单值存储的集合,因此,HashMap集合的顶级父接口就不再是进行单值存储的接口:Collection接口了,而是进行双值存储的接口:Map接口,此外该类还继承了 AbstractMap 类和实现了这些接口:Serializable 接口, Cloneable 接口。使用该类时需要进行导包操作,导入的包名为:java.util.HashMap。
HashMap集合的特点和HashSet集合大致是相同的,相同的地方在于:(1)不允许存储相同的元素,(2)元素的存取顺序是不同的,即默认情况下元素的存储是无序的。这两个集合的特点会有相似,我在介绍HashSet集合的博文中简单提到过:HashSet集合是基于HashMap支持的。
当然大致相同的说法可以看出可是有不同之处的,不同之处在于,HashMap集合中不允许重复存储的是元素中键值对中的键,而键值对中的值是可以重复存储的,所以HashSet集合的实现是依赖于HashMap集合中的键的存储原理。HashMap集合会有这样的特点与该集合的底层实现原理有关,下面就来介绍HashMap集合的底层实现原理。
1.2、HashMap的底层实现原理
HashMap的底层实现依赖于哈希表这一结构,我在关于HashSet集合的博文中有介绍过,在这再一次介绍一遍:
哈希表的底层结构相当于一个可变数组结构里的每一个下标空间都存放着一个链表结构,之所以会有这样的结构取决于:哈希表在存储元素时会计算每个新元素相应的哈希值,该哈希值代表着这个新元素存放在数组中的哪一个下标位置,如果该下标有旧元素则会先进行equals方法的比较,而HashMap集合中的一个键值对就是一个元素,进行equals方法比较的是:键值对中的键,当键相同时,新的键值对中的值会覆盖旧的键值对中的值,而键值对中的键是不变的。;不相等则将新元素放在旧元素的下一个节点处,如果该节点也存放着旧元素,依次执行上述的操作,直到新元素覆盖旧元素或存入集合为止,这就形成了链表结构。除此在外,当某一下标位置中的链表的节点数大于8时,该链表会自动转化成红黑树,相反如果某一下标位置中的红黑树的节点数小于8时,红黑树又会自动转化成链表,
此外可变数组这一特点取决于加载因子值(构造方法中会提及),当数组中存放元素的下标数占数组的总容量的百分数等于加载因子的百分数时,数组就会自动进行扩容,并且会重新计算里面元素的哈希值,让里面的元素进行新的排列存储。
由上述的哈希表的结构原理就能得出HashMap集合的特点:不能存储相同键(equals方法造成)、元素的存取顺序是不同的(链表结构、哈希值的计算和哈希表的散列共同造成)。介绍完HashMap集合的底层原理,下面来介绍HashMap集合可以操作的方法和这些方法有什么作用。
1.3、HashMap的操作方法
1.3.1、构造方法
首先需要先了解如何创建HashMap集合,创建的方式为:使用构造方法实现创建HashMap集合,而该集合的构造方法有以下几种
- HashMap()
- HashMap(int initialCapacity)
- HashMap(int initialCapacity, float loadFactor)
- HashMap(Map<? extends K,? extends V> m)
第一种方法的作用是:创建默认初始容量(16)和默认加载因子(0.75)的一个空 HashMap 。
第二种方法需传入一个int类型的参数,该参数的值代表你指定的HashMap集合的初始容量,方法的作用是:创建你所指定的初始容量和默认加载因子(0.75)的一个空 HashMap 。
第三种方法需传入两个参数,第一个参数是int类型,该参数的值代表你指定的HashMap集合的初始容量,第二个参数是float类型,该参数的值代表你所指定的加载因子,方法的作用是:创建你所指定的初始容量和加载因子的一个空 HashMap
第四种方法传入一个参数,该参数是一个你所指定的集合,并且该集合必须实现了Map接口的,方法的作用是:创建一个包含你所指定的集合的空HashMap集合。
注意:加载因子的值不宜过大或过小,过大会造成数组散列困难,从而使数组中存放了大量的元素,不宜数据的操作,而过小会造成数组散列频繁,从而造成内存空间的浪费。因此加载因子设为默认值(0.75)是效率最高的。
以下是Java代码格式:
//第一种方法 注意:尖括号里可以是任何一种类型,不过必须是包装类,
//第一个为键值对中键的类型,第二个为键值对中值的类型
HashMap<Integer,Integer> hashMap = new HashMap<>();
//第二种方法: 指定容量为20
HashMap<Integer,Integer> hashMap1 = new HashMap<>(20);
//第三种方法:指定容量为20,加载因子为0.8
HashMap<Integer,Integer> hashMap2 = new HashMap<>(20,0.8f);
//第四种方法:指定的集合为新创建HashMap集合,该方法使用的比较少
HashMap<Integer,Integer> hashMap3 = new HashMap<>(new HashMap<Integer,Integer>());
了解完HashMap集合的构造方法,可以创建HashMap集合的对象了,而接下来我将介绍该集合对象可以调用的方法和这些方法的作用。
1.3.2、containsKey方法和containsValue方法
(1)containsKey方法使用后会有一个boolean类型的返回值,使用方法时传入一个参数,该参数代表你指定的键值对中的键,参数的类型与你创建集合时传入的类型要一致,方法的作用是:判断集合中是否存在你指定的键值对中的键,如果存在返回true,不存在返回false。格式如下:
- boolean containsKey(Object key)
(2)containsValue方法使用后会有一个boolean类型的返回值,使用方法时传入一个参数,该参数代表你指定的键值对中的值,参数的类型与你创建集合时传入的类型要一致,方法的作用是:判断集合中是否存在你指定的键值对中的值,如果存在返回true,不存在返回false。格式如下:
- boolean containsValue(Object value)
以下是具体的Java代码:
public class Test2 {
public static void main(String[] args) {
HashMap<String,Integer> map = new HashMap<>();
map.put("1",1);
boolean b = map.containsKey("1");
boolean b1 = map.containsKey("2");
boolean b2 = map.containsValue(1);
boolean b3 = map.containsValue(2);
System.out.println(b?"该键存在":"该键不存在");
System.out.println(b1?"该键存在":"该键不存在");
System.out.println(b2?"该值存在":"该值不存在");
System.out.println(b3?"该值存在":"该值不存在");
}
}
以下是效果图:
可能有些读者不了解三目元算符(判断条件 ? 语句1 : 语句2),我来简单介绍一下,问号前面的是一个判断条件,当判断条件为true时会执行问号和冒号之间的语句1,当判断条件为false时会执行冒号后面的语句2。由此便可以清楚的理解代码和效果图。
1.3.3、remove方法
使用该方法后会有一个泛型的返回值,该返回值代表是:你指定的键值对中的键对应的值,返回值的类型与键值对中值的类型一致,使用方法时传入一个参数,该参数代表你指定的键值对中的键,参数的类型与你创建集合时传入的类型要一致,方法的作用是:删除集合中你指定的键值对中的键和其对应的值,如果存在该键则删除成功返回该键对应的值,不存在该键则删除失败返回null。格式如下:
- V remove(Object key)
以下是具体的Java代码:
public class Test3 {
public static void main(String[] args) {
HashMap<String,Integer> map = new HashMap<>();
map.put("1",1);
Integer remove = map.remove("1");
Integer remove1 = map.remove("1");
System.out.println("第一次删除的键值对的值为:"+remove);
System.out.println("第二次删除的键值对的值为:"+remove1);
}
}
以下是效果图:
由代码和效果图可以看出,当我第一次删除时,我指定的键存在,所以返回的是该键对应的值:1,而第二次删除时,该键已被删除了,即集合中不存在该键,所以返回null。
当然该方法还有一种传值方式,格式如下:
- boolean remove(K key,V value)
该方法使用后是有一个boolean的返回值,使用方法时传入两个参数,第一个参数代表是:键值对中的键,第二个参数代表的是:键值对中的值,这两个参数的类型与你创建集合时传入的类型要一致,该方法的作用就是:删除你指定的相对应的键值对,如果存在该键值对则删除成功,返回true,不存在该键值对则删除失败,返回false。
注意:你指定的键和值一定要存在映射的(即成对存在的)
具体的Java代码如下:
public class Test4 {
public static void main(String[] args) {
HashMap<String,Integer> map = new HashMap<>();
map.put("1",2);
map.put("2",1);
boolean remove = map.remove("2", 2);
boolean remove2 = map.remove("2", 1);
System.out.println("第一次"+(remove?"删除成功":"删除失败"));
System.out.println("第二次"+(remove2?"删除成功":"删除失败"));
}
}
以下是效果图:
由代码和效果图可以看出,就算集合中存在你指定的键和值,但两者不存在映射关系的话,也是不能删除的,就如第一次一样,第二次指定的键和值存在映射关系,则删除成功。
1.3.4、put方法
使用该方法后会有一个泛型的返回值,该返回值代表是:HashMap集合中旧的键值对中的值,返回值的类型与键值对中值的类型一致,使用方法时传入两个参数,第一个参数代表是:键值对中的键,第二个参数代表的是:键值对中的值,这两个参数的类型与你创建集合时传入的类型要一致,作用是:将你指定的键值对添加到集合中,如果键值对中的键在集合中是唯一的,将返回null,如果不唯一将返回旧键值对中的值。格式如下:
- V put(K key, V value)
以下是具体的Java代码:
public class Test {
public static void main(String[] args) {
HashMap<String,Integer> hashMap = new HashMap<>();
Integer put = hashMap.put("1", 1);
Integer put1 = hashMap.put("1", 2);
Integer put2 = hashMap.put("1", 3);
System.out.println("第一次添加返回的值:"+put);
System.out.println("第二次添加返回的值:"+put1);
System.out.println("第三次添加返回的值:"+put2);
}
}
以下是效果图:
由效果图可以看出第一次返回null代表添加的键值对中的键在集合中是唯一的,而第二次和第三次则返回集合中存在的旧键值对中的值。
1.3.5、get方法
使用该方法后会有一个泛型的返回值,该返回值代表是:你指定的键值对中的键对应的值,返回值的类型与键值对中值的类型一致,使用方法时传入一个参数,该参数代表你指定的键值对中的键,参数的类型与你创建集合时传入的类型要一致,方法的作用是:获得你指定的键值对中的键所对应的键值对中的值,如果该键在集合中存在则返回键所对应的值,如果不存在则返回null。格式为:
- V get(Object key)
以下是具体的Java代码:
public class Test1 {
public static void main(String[] args) {
HashMap<String,Integer> map = new HashMap<>();
map.put("1",1);
map.put("1",20);
Integer integer = map.get("1");
Integer integer1 = map.get("2");
System.out.println("第一次获取的键值对的值为:"+integer);
System.out.println("第二次获取的键值对的值为:"+integer1);
}
}
以下是效果图:
由代码和效果图可以看出,在两次添加中我添加的键值对的键是相同的,而第一次得到的键值对的值是20而不是1,就可以知道当新旧键值对中的键相同时,新的键值对中的值会覆盖旧键值对中的值,除此之外,第二次我传入的键在集合中是不存在的,所以返回null。
1.3.6、keySet方法和values方法
(1)keySet方法使用后会返回一个Set集合,该Set集合中存放着HashMap集合中的所有键值对的键,使用时无需传入任何参数,方法的作用是:获取HashMap集合中的所有键值对的键,格式如下:
- Set< K > keySet()
注意Set集合的中的元素类型与HashMap集合中键值对中键的类型一致。
(2)values方法使用后会有返回一个Collection集合,该集合存放着HashMap集合中的所有键值对的值,使用时无需传入任何参数,方法的作用是:获取HashMap集合中的所有键值对的值,格式如下:
- Collection< V > values()
注意Collection集合的中的元素类型与HashMap集合中键值对中的值的类型一致。
以下是具体的Java代码:
public class Test5 {
public static void main(String[] args) {
HashMap<String,Integer> map = new HashMap<>();
map.put("1",10);
map.put("2",20);
map.put("3",30);
map.put("4",40);
Set<String> set = map.keySet();
Collection<Integer> values = map.values();
System.out.print("HashMap集合中的键有:");
for (String a:set ) {
System.out.print(a+" ");
}
System.out.println();
System.out.print("HashMap集合中的值有:");
for (int a:values) {
System.out.print(a+" ");
}
System.out.println();
System.out.print("HashMap集合中的键值对有:");
for (String a:set) {
System.out.print(a+"——>"+map.get(a)+" | ");
}
}
}
以下是效果图:
由效果图可以清楚的知道,这两种方法可以进行HashMap集合键的遍历、值的遍历、键值对的遍历。
1.3.7、size方法和isEmpty方法
(1)size方法使用后会有一个int类型的返回值,代表HashMap集合中存在的键值对个数,使用时无需传入任何参数,方法作用是:获取HashMap集合中存在的键值对个数,格式如下:
- int size()
(2)isEmpty方法使用后会有一个boolean类型的返回值,使用时无需传入任何参数,方法的作用是:判断HashMap集合是否为空,为空返回true,不为空返回false。格式如下:
- boolean isEmpty()
以下是具体的Java代码:
public class Test6 {
public static void main(String[] args) {
HashMap<String,Integer> map = new HashMap<>();
boolean empty = map.isEmpty();
System.out.println("添加键值对之前集合"+(empty?"为空":"不为空"));
map.put("1",10);
map.put("2",20);
map.put("3",30);
map.put("4",40);
boolean empty1 = map.isEmpty();
System.out.println("添加键值对之前集合"+(empty1?"为空":"不为空"));
int size = map.size();
System.out.println("集合中键值对个数为"+size);
}
}
以下是效果图:
关于HashMap集合常用的方法就介绍到这,还有一些不常用的方法感兴趣的读者可以自行百度。
与HashMap集合的相关知识就介绍到这,希望文章可以帮到你们,谢谢阅读,文章结尾还附送了其他集合的相关博文介绍,感兴趣的读者可以点击阅读。
相关博文,点击即可阅读:
“Java中的集合类:ArrayList”
“Java中的集合类:LinkedList”
“Java中的集合类:HashSet”