1.1. Map接口(重点)
1.1.1. 认识Map(理解)
Map,翻译为映射,在数学中的解释为:
设A、B是两个非空集合,如果存在一个法则f,使得A中的每个元素a,按法则f,在B中有唯一确定的元素b与之对应,则称f为从A到B的映射,记作f:A→B。
也就是说映射表示两个集合之间各自元素的一种“对应”的关系,在面向对象中我们使用Map来封装和表示这种关系。
从定义和结构图上,可以看出Map并不是集合,而表示两个集合之间的一种关系,故Map没有实现Collection接口。
在Map中,要求A集合中的每一个元素都可以在B集合中找到唯一的一个值与之对应。这句话可以解读为一个A集合元素只能对应一个B集合元素,也就说A集合中的元素是不允许重复的,B集合中的元素可以重复,也可不重复。那么不难推断出A集合应该是一个Set集合,B集合应该是List集合。 [图片上传失败...(image-2b76fc-1536844564929)]
我们把A集合中的元素称之为key,把B集合中的元素称之为value。
其实能看出一个Map其实就有多个key-value(键值对)组成的,每一个键值对我们使用Entry表示。
不难发现,一个Map结构也可以理解为是Entry的集合,即Set<Entry>。
一般的,我们依然习惯把Map称之为集合,不过要区分下,Set和List是单元素集合,Map是双元素集合。
单元素集合:每次只能存储一个元素,比如Set和List。
双元素集合:每次需要存储两个元素(一个key和一个value),比如Map。
注意:
Map接口并没有继承于Collection接口也没有继承于Iterable接口,所以不能直接对Map使用for-each操作。
如果不能理解Map的结构,就直接记住Map每次需要存储两个值,一个是key,一个是value,其中value表示存储的数据,而key就是这一个value的名字。
1.1.2. Map常用的API方法(记住)
添加操作
boolean put(Object key,Object value):存储一个键值对到Map中
boolean putAll(Map m):把m中的所有键值对添加到当前Map中
删除操作
- Object remove(Object key):从Map中删除指定key的键值对,并返回被删除key对应的value
修改操作
- 无专门的方法,可以调用put方法,存储相同key,不同value的键值对,可以覆盖原来的。
查询操作
int size():返回当前Map中键值对个数
boolean isEmpty():判断当前Map中键值对个数是否为0.
Object get(Object key):返回Map中指定key对应的value值,如果不存在该key,返回null
boolean containsKey(Object key):判断Map中是否包含指定key
boolean containsValue(Object value):判断Map中是否包含指定value
Set keySet():返回Map中所有key所组成的Set集合
Collection values():返回Map中所有value所组成的Collection集合
Set<Entry> entrySet():返回Map中所有键值对所组成的Set集合
注意,标红的是重度使用的方法。
1.1.3. HashMap(重点)
HashMap底层基于哈希表算法,Map中存储的key对象的hashCode值决定了在哈希表中的存储位置,因为Map中的key是Set,所以不能保证添加的先后顺序,也不允许重复。
需求1:操作Map接口常用方法
public class HashMapDemo1{
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("girl1", "西施");
map.put("girl2", "王昭君");
map.put("girl3", "貂蝉");
map.put("girl4", "杨玉环");
System.out.println("map中有多少键值对:"+map.size());
System.out.println(map);
System.out.println("是否包含key为girl1:"+map.containsKey("girl1"));
System.out.println("是否包含value为貂蝉:"+map.containsValue("貂蝉"));
//替换key为girl3的value值
map.put("girl3", "小乔");
System.out.println(map);
//删除key为girl3的键值对
map.remove("girl3");
System.out.println(map);
}
}
Map的迭代遍历:
//获取Map中所有的key
Set<String> keys = map.keySet();
System.out.println("Map中所有key:"+keys);
//获取Map中所有的value
Collection<String> values = map.values();
System.out.println("Map中所有value:"+values);
//获取Map中所有的key-value(键值对)
Set<Entry<String, String>> entrys = map.entrySet();
for (Entry<String, String> entry : entrys) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key+"->"+value);
}
需求2:统计一个字符串中每隔字符出现次数
public class HashMapDemo2{
public static void main(String[] args) {
String str = "ABCDEFABCDEABCDABCABA";
//把字符串转换为char数组
char[] charArray = str.toCharArray();
//Map的key存储字符,value存储出现的次数
Map<Character, Integer> map = new HashMap<>();
//迭代每一个字符
for (char ch : charArray) {
//判断Map中是否已经存储该字符
if (map.containsKey(ch)) {
Integer count = map.get(ch);
//如果已经存储该字符,则把出现次数加上1
map.put(ch, count+1);
}else {
//如果没有存储该字符,则把设置次数为1
map.put(ch, 1);
}
}
System.out.println(map);
}
}
1.1.4. TreeMap(了解)
TreeMap底层基于红黑树算法,因为Map中的key是Set,所以不能保证添加的先后顺序,也不允许重复,但是Map中存储的key会默认使用自然排序(从小到大),和TreeSet一样,除了可以使用自然排序也可以自定义排序。
需求:测试HashMap和TreeMap中key的顺序
public class App {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("girl4", "杨玉环");
map.put("girl2", "王昭君");
map.put("key1", "西施");
map.put("key3", "貂蝉");
System.out.println(map);
//-------------------------------------------
map = new TreeMap<>(map);
System.out.println(map);
}
}
输出结果:
{key1=西施, girl4=杨玉环, key3=貂蝉, girl2=王昭君}
{girl2=王昭君, girl4=杨玉环, key1=西施, key3=貂蝉}
2. 集合框架工具类和方法
2.1. Arrays(掌握)
Arrays类是数据的工具类,其中有一个方法比较常用。
- public static <T> List<T> asList(T... a):该方法可以把一个Object数组转换为List集合。
public class ArraysDemo{
public static void main(String[] args) {
//把Integer[]转换为List<Integer>
List<Integer> list1 = Arrays.asList(1, 2, 3);
System.out.println(list1);
//把String[]转换为List<String>
List<String> list2 = Arrays.asList("A", "B", "C");
System.out.println(list2);
}
}
注意通过Arrays.asList方法得到的List对象的长度是固定的,不能增,也不能减。
2.2. Collections(了解)
Collections是集合的工具类,封装了Set、List、Map操作的工具方法,比如拷贝、排序、搜索、比较大小等。
2.3. 斗地主发牌案例(了解)
按照斗地主游戏的规则,模拟对54张扑克牌的洗牌和发牌以及对手中牌排序操作。
具体规则:
将54张不同花色的扑克牌打乱(♠♣♥♦☻)
三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张留作底牌
查看三人各自手中的牌(按照牌的大小排序)、底牌
手中扑克牌从大到小的摆放顺序:大王、小王、2、A、K、Q、J、10、9、8、7、6、5、4、3
首先我们定义好基本的代码测试框架:
public class App {
public static void main(String[] args) {
}
}
接下来,使用一个列表来记录四中花色:
//花色♠♣♥♦
List<String> colors = new ArrayList<>();
colors.add("♠");
colors.add("♣");
colors.add("♥");
colors.add("♦");
然后,想办法添加牌面。牌面有2~A;一共四种花色,所以我们可以使用一个列表,来添加4份2-A的牌面:
//数字345678910JQKA2
List<String> numbers = new ArrayList<>();
Collections.addAll(numbers,
"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2");
numbers.addAll(numbers);//把13张复制成26张
numbers.addAll(numbers);//把26张复制成52张
牌面最终要和花色对应,为了后面的发牌操作方便,我们定义一个Map<Integer,String>,key是我们定义的排序,可以简单的理解为我们为每一张牌定义了一个编号,value就是具体的排面+花色,相当于每一张牌和一个具体的序号一一对应:
//大小王: 大☻ 小☻
//定义Map,用数字来记录每一张牌
Map<Integer, String> map = new HashMap<>();
Integer squence = 0;//牌的顺序
//加入其它牌
for (String number : numbers) {
for (String color : colors) {
//一副牌54张,序号范围在[0,51]之间
if (squence <= 51) {
map.put(squence, number + "(" + color + ")");
squence++;
}
}
}
map.put(squence, "小☻");
squence++;
map.put(squence, "大☻");
squence++;
洗牌:
//使用0~53来记录每一张牌
List<Integer> cards = new ArrayList<>(map.keySet());
Collections.shuffle(cards);
System.out.println(cards);
接下来定义三个玩家的牌列表,然后为最后剩下的底牌定义一个列表在存放:
//三个玩家和底牌
List<Integer> player1 = new ArrayList<Integer>();
List<Integer> player2 = new ArrayList<Integer>();
List<Integer> player3 = new ArrayList<Integer>();
List<Integer> end = new ArrayList<Integer>();
发牌:
//发牌
for (int index = 0; index < cards.size(); index++) {
if (index < 51) {
int mod = index % 3;//index除以3的余数
int card = cards.get(index);
if (mod == 1) {
player1.add(card);
} else if (mod == 2) {
player2.add(card);
} else {
player3.add(card);
}
} else {
end.add(cards.get(index));
}
}
每个玩家手牌排序:
System.out.println(player1);
System.out.println(player2);
System.out.println(player3);
System.out.println(end);
//排序
Collections.sort(player1);
Collections.sort(player2);
Collections.sort(player3);
Collections.sort(end);
注意,到这里为止,每个玩家手上的牌,仍然不是具体的牌,只是每张牌对应的序号,接下来我们就要通过序号找到对应的牌:
//各自手上的牌
List<String> player1Cards = new ArrayList<>();
List<String> player2Cards = new ArrayList<>();
List<String> player3Cards = new ArrayList<>();
List<String> endCards = new ArrayList<>();
for (Integer key : player1) {
player1Cards.add(map.get(key));
}
for (Integer key : player2) {
player2Cards.add(map.get(key));
}
for (Integer key : player3) {
player3Cards.add(map.get(key));
}
for (Integer key : end) {
endCards.add(map.get(key));
}
看牌:
//看牌
System.out.println("玩家1:" + player1Cards);
System.out.println("玩家2:" + player2Cards);
System.out.println("玩家3:" + player3Cards);
System.out.println("底牌 :" + endCards);
到此,发牌操作结束。
集合框架小结
List、Set、Map选用
一般的在存储元素时候,是否需要给元素起一个名字:
- 需要:此时使用Map。
- 不需:存储的元素使用需要保证先后添加的顺序
- 需要:此时使用List
- 不需:此时使用Set(如果需要保证集合元素不重复,也选用Set)
若要获得最好的学习效果,需要配合对应教学视频一起学习。需要完整教学视频,请参看https://ke.qq.com/course/272077。