目录
一、Map集合
1.1Map集合概述
java中Map用于保存具有映射关系的数据,Map集合里保存着两组值,
一组用于保存Map的key,另一组保存着Map的value,Map是一个将键值映射到值的接口,
一个映射不能包含重复的键值,每个键最多映射到一个值。
1.2Map集合与Collection集合的区别
- Map存储的是两组值,而Collection是一组值
- Map的键是唯一的,Collection的子接口Set键是唯一的
- Map集合的数据结构值针对键有效,和值无关;而Collection集合的数据结构是针对元素有效的
1.3Map功能概述
- 添加功能
- V put(K key,V value)//添加元素
- 如果键为第一次存储,则直接存储元素,返回null
- 如果键已经存在了,那么就用值将以前的旧值替换掉,返回以前的值
- V put(K key,V value)//添加元素
- 删除功能
- void clear()//删除所有的键值对元素
- V remove(Object key)//根据键删除键值对元素,并将值返回
- 判断功能
- boolean containsKey(Object key)//判断集合是否包含指定的键
- boolean containsValue(Object value)//判断集合是否包含指定的值
- boolean isEmpty()//判断集合是否为空
- 获取功能
- Set<Map.Entry<K,V>> entrySet()
- V get(Object key)//根据键获取值
- Set<K> ketSet()//获取集合中所有键的集合
- Collection<V> values()//获取集合中所有值的集合
- 长度功能
- int size()//返回集合中键值对的个数
二、Map集合的遍历
2.1根据键集合迭代
由于Map中没有迭代器的方法,所以我们对Map集合进行遍历的时候,
首先会获取键的集合keySet,然后通过keySet的迭代器遍历所有的键,
最后通过键获取对应的值,达到对Map集合的遍历效果,下面是程序演示,
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<String,Integer> map=new HashMap<>();
map.put("测试1",21);
map.put("测试2",22);
map.put("测试3",23);
Set<String> ketSet=map.keySet();
Iterator<String> it=ketSet.iterator();
while(it.hasNext()){
String key=it.next();
Integer i=map.get(key);
System.out.println(key+"="+i);
}
}
}
2.2根据键值对对象迭代
我们将双列集合的键值对,变为单列集合的键值对对象,
然后遍历这个集合,获取每个键值对对象,根据键值对对象,从而获取键和值的大小,
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<String,Integer> map=new HashMap<>();
map.put("测试1",21);
map.put("测试2",22);
map.put("测试3",23);
//Map.Entry说明Entry是Map的内部接口,将键和值封装成了Entry对象并存储在Set集合中
Set<Map.Entry<String,Integer>> entrySet=map.entrySet();
Iterator<Map.Entry<String,Integer>> it=entrySet.iterator();
while(it.hasNext()){
Map.Entry<String,Integer> entry=it.next();
System.out.println(entry.getKey()+"="+entry.getValue());
}
}
}
2.3增强for循环遍历
我们还可以使用增强for循环对Map集合进行遍历,这种方式会更简单一些,
import java.util.HashMap;
import java.util.Map;
public class MapTest {
public static void main(String[] args) {
Map<String,Integer> map=new HashMap<>();
map.put("测试1",21);
map.put("测试2",22);
map.put("测试3",23);
for(String key:map.keySet()){
System.out.println(key+"="+map.get(key));
}
}
}
三、键为自定义对象
如果我们用自定义的对象当作键存储到HashMap中,集合如果还要保证键的唯一性,
则必须重写自定义对象中的equals方法和hashCode方法,
我们测试一下,键为Student自定义对象,值为籍贯字符串,
import java.util.HashMap;
public class MapTest {
public static void main(String[] args) {
HashMap<Student,String> hashMap=new HashMap<>();
hashMap.put(new Student("测试1",21),"武汉");
hashMap.put(new Student("测试1",21),"长沙");
hashMap.put(new Student("测试1",21),"郑州");
hashMap.put(new Student("测试1",21),"南昌");
System.out.println(hashMap);
}
}
可以看到,前面的Student对象属性相同的只存进来了最后一个值,键相同就会不断覆盖掉前面的值。
四、LinkedHashMap集合
LinkedHashMap就是使用链表实现的HashMap,其特点是存取顺序一致,我们简单测试一下:
public static void main(String[] args) {
LinkedHashMap<String,Integer> linkedHashMap=new LinkedHashMap<>();
linkedHashMap.put("测试1",21);
linkedHashMap.put("测试3",23);
linkedHashMap.put("测试2",22);
System.out.println(linkedHashMap);
}
五、TreeMap集合
TreeMap与TreeSet类似,也是一个有序的集合,存储的为键值对,为两列集合,
同理,如果这个键为自定义对象,则要在自定义对象中继承Comparable,并实现compareTo方法,
public static void main(String[] args) {
TreeMap<Student,String> treeMap=new TreeMap<>();
treeMap.put(new Student("测试1",23),"武汉");
treeMap.put(new Student("测试2",22),"长沙");
treeMap.put(new Student("测试3",21),"郑州");
System.out.println(treeMap);
}
同时我们也可以在TreeMap构造的时候传一个比较器的方法来指定排序的规则,
我们更改一下排序规则,按照姓名来排序,测试一下看看效果,
public static void main(String[] args) {
TreeMap<Student,String> treeMap=new TreeMap<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
int num=o1.getName().compareTo(o2.getName());
return num==0?o1.getAge()-o2.getAge():num;
}
});
treeMap.put(new Student("测试1",23),"武汉");
treeMap.put(new Student("测试2",22),"长沙");
treeMap.put(new Student("测试3",21),"郑州");
System.out.println(treeMap);
}
六、统计字符串中每个字符出现次数
要求给定一个字符串,需要统计里面每个字符出现的次数,
public class MapTest {
public static void main(String[] args) {
String str="aaabbbbbccd";
char arr[]=str.toCharArray();
HashMap<Character,Integer> hashMap=new HashMap<>();
for(char c:arr){
if(!hashMap.containsKey(c)){
hashMap.put(c,1);
}else{
hashMap.put(c,hashMap.get(c)+1);
}
}
System.out.println(hashMap);
}
}
七、HashMap与Hashtable的区别
HashMap和Hashtable底层都是哈希算法支持的,都为双列集合,存储的是键与值,
但是它们主要有以下几点区别,
- Hashtable是线程安全的,效率低,Hashtable不可以存储null键和null值
- HashMap是线程不安全的,效率高,HashMap可以存储null键和null值
八、模拟斗地主洗牌和发牌练习
import java.util.ArrayList;
import java.util.Collections;
public class MapTest {
public static void main(String[] args) {
String num[]={"A","2","3","4","5","6","7","8","9","10","J","Q","K"};
String color[]={"方片","梅花","黑桃","红桃"};
ArrayList<String> poker=new ArrayList<>();
//添加扑克牌到牌库
for(String pokerColor:color){
for(String pokerNum:num){
poker.add(pokerColor.concat(pokerNum));//concat链接俩字符串
}
}
poker.add("小王");
poker.add("大王");
//洗牌
Collections.shuffle(poker);//随机打乱原来的顺序
//发牌
ArrayList<String> player1=new ArrayList<>();
ArrayList<String> player2=new ArrayList<>();
ArrayList<String> player3=new ArrayList<>();
ArrayList<String> cardsOfLandlord=new ArrayList<>();
for (int i = 0; i <poker.size() ; i++) {
if(i>=poker.size()-3){
cardsOfLandlord.add(poker.get(i));
}else if(i%3==0){
player1.add(poker.get(i));
}else if(i%3==1){
player2.add(poker.get(i));
}else if(i%3==2){
player3.add(poker.get(i));
}
}
//看牌
System.out.println("玩家一手牌:"+player1);
System.out.println("玩家二手牌:"+player2);
System.out.println("玩家三手牌:"+player3);
System.out.println("地主奖励底牌:"+cardsOfLandlord);
}
}
我们可以发现这个时候手牌是没有顺序的,现在我们利用HashMap构建键值对,
将54个扑克牌对应好54个数字(注意这里的54个数字对应的大小关系就是我们想要扑克牌排序的大小关系,比如梅花3对应0,梅花4就对应4,梅花5对应8以此类推),
然后在ArrayList里面直接存储着54个数字,随机打乱,
然后使用TreeSet存储这54个数字,存储的时候按照我们想要的排序关系来存储,
最后显示出来就可以看到手牌是有序的,下面我们试一下效果,
import java.util.*;
public class MapTest {
public static void main(String[] args) {
String num[]={"3","4","5","6","7","8","9","10","J","Q","K","A","2"};
String color[]={"♦","♣","♠","♥"};
HashMap<Integer,String> pokerHash=new HashMap<>();
ArrayList<Integer> pokerList=new ArrayList<>();
//构造好扑克牌的HashMap
for(int i=0;i<num.length;i++){
for(int j=0;j<color.length;j++){
pokerHash.put(i*4+j,color[j].concat(num[i]));//concat链接俩字符串
}
}
pokerHash.put(52,"小王");
pokerHash.put(53,"大王");
//添加对应数字到数组中
for (int i = 0; i < 54; i++) {
pokerList.add(i);
}
//洗牌
Collections.shuffle(pokerList);//随机打乱原来的顺序
//发牌
TreeSet<Integer> player1=new TreeSet<>();
TreeSet<Integer> player2=new TreeSet<>();
TreeSet<Integer> player3=new TreeSet<>();
TreeSet<Integer> cardsOfLandlord=new TreeSet<>();
for (int i = 0; i <pokerList.size() ; i++) {
if(i>=pokerList.size()-3){
cardsOfLandlord.add(pokerList.get(i));
}else if(i%3==0){
player1.add(pokerList.get(i));
}else if(i%3==1){
player2.add(pokerList.get(i));
}else if(i%3==2){
player3.add(pokerList.get(i));
}
}
//看牌
System.out.print("玩家1手牌:");
Iterator<Integer> it1=player1.iterator();
while(it1.hasNext()){
Integer it=it1.next();
System.out.print(pokerHash.get(it)+" ");
}
System.out.println();
System.out.print("玩家2手牌:");
Iterator<Integer> it2=player2.iterator();
while(it2.hasNext()){
Integer it=it2.next();
System.out.print(pokerHash.get(it)+" ");
}
System.out.println();
System.out.print("玩家3手牌:");
Iterator<Integer> it3=player3.iterator();
while(it3.hasNext()){
Integer it=it3.next();
System.out.print(pokerHash.get(it)+" ");
}
System.out.println();
System.out.print("地主奖励底牌:");
Iterator<Integer> it4=cardsOfLandlord.iterator();
while(it4.hasNext()){
Integer it=it4.next();
System.out.print(pokerHash.get(it)+" ");
}
}
}
九、泛型固定上下边界(待补充)
9.1泛型固定上边界
? extends E
通俗理解就是只要是E的子类或者同类对象,就可以将父类对象指向子类引用。
9.2泛型固定下边界
? super E
通俗理解就是E中是实现的一些功能,E的子类也可以使用。