反馈和复习
a.比较器口诀: 升序 前-后(源码)
b.为什么保证元素的唯一性.重写hashCode和equals
因为Hash表结构,底层就是根据hashCode和equals来判断两个元素是否重复
1.Collection: 7+1
2.List: 7+1+4
ArrayList: 7+1+4
LinkedList: 7+1+4+8
3.Set:7+1
HashSet:7+1 无序
LinkedHashSet:7+1 有序
TreeSet:7+1 无序(有自然顺序的)
4.Collections
shuffle(List list); 打乱集合顺序
sort(List list); 对集合元素进行排序(默认升序)
sort(List list,Comparator 比较器); 自定义排序规则(升序 前-后)
今日内容
- Map集合(和Collection没有直接联系)
- 集合的嵌套(集合的元素还是集合)
- 斗地主发牌案例[重要]
- 冒泡排序算法(a,算法过程 b,算法的代码实现)
Map集合(接口)[重点]
概述
所有实现类都没有特有方法 ,是个双列集合
特点:
- Collection<E>泛型只有一个, Map<K,V>泛型有两个
- Collection 中的集合,元素是孤立存在的(理解为单身),向集合中存储元素采用一个个元素的方式存储。
- Map 中的集合,元素是成对存在的(理解为夫妻)。每个元素由键与值两部分组成,通过键可以找对所对应的
值。 - Collection 中的集合称为单列集合, Map 中的集合称为双列集合。
注意 Map 中的集合不能包含重复的键,值可以重复;每个键只能对应一个值。
Map的实现类及其特点
Map接口三个常见的实现类:
- HashMap 底层采用哈希表结构 无序
- LinkedHashMap 底层采用链表+哈希表结构 有序 (单列链表结构)
- TreeMap 底层采用红黑树结构 无序,但是键有自然顺序.可以对元素的键进行排序,排序方式有两种:自然排序和比较器排序
重点:
Map中为了保证键的唯一性, 如果键是自定义类型,必须重写键的hashCode和equals方法
Map的通用方法
- 增: public V put(K key, V value); 添加一个键值对,返回值类型为V, 返回的就是null
- 删:public V remove(Object key);根据键删除键值对,返回值类型为 V
- 改: 用的就是增的方法, put(K key, Value);因为键是唯一的, 添加重复的键,就相当于是修改原键所对应的值了.
- 查:public V get(Object key) ;根据键获取对应的值,返回值类型为 V
- 其他:
public Set<K> keySet() : 获取Map集合中所有的键,存储到Set集合中。
public Set<Map.Entry<K,V>> entrySet() : 获取到Map集合中所有的键值对对象的集合(Set集合)。
public boolean containKey(Object key) :判断该集合中是否有此键
tips:
使用put方法时,若指定的键(key)在集合中没有,则没有这个键对应的值,返回null,并把指定的键值添加到集合中;
若指定的键(key)在集合中存在,则返回值为集合中键对应的值(该值为替换前的值),并把指定键所对应的值,替换成指定的新值。
Map的遍历[非常重要]
迭代器,增强for,for循环都不可用. 因为 Map和Collection没有关系, 没有索引
-
遍历方式一以键找值
1.获取Map中所有的键,由于键是唯一的,所以返回一个Set集合存储所有的键。方法提示: keyset()
2.遍历键的Set集合,得到每一个键。
3.根据键,获取键所对应的值。方法提示: get(K key)
第一种方式称为:以键找值
public class TestMap01 {
public static void main(String[] args) {
//第一种遍历方式:以键找值
//1.创建一个Map的实现类对象
HashMap<String, Integer> map = new HashMap<String, Integer>();
//2.添加几个
map.put("张三", 18);
map.put("李四", 28);
map.put("王五", 38);
map.put("赵六", 48);
map.put("前妻", 8);
map.put("王八", 88);
//3.获取所有的键
Set<String> keys = map.keySet();
//4.遍历这个keys集合
for (String key : keys) {
//5.以键找值
Integer value = map.get(key);
System.out.println(key + "..." + value);
}
}
}
- 遍历方式二键值对方式
Entry键值对对象:
Map 中存放的是两种对象,一种称为key(键),一种称为value(值),它们在在 Map 中是一一对应关系,这一对对象又称做 Map 中的一个 Entry(项) 。 Entry 将键值对的对应关系封装成了对象。即键值对对象,这样我们在遍历 Map 集合时,就可以从每一个键值对( Entry )对象中获取对应的键与对应的值。
在Map集合中也提供了获取所有Entry对象的方法:
public Set<Map.Entry<K,V>> entrySet() : 获取到Map集合中所有的键值对对象的集合(Set集合)。
获取了Entry对象 , 表示获取了一对键和值,那么同样Entry中 , 分别提供了获取键和获取值的方法:
public K getKey() :获取Entry对象中的键。
public V getValue() :获取Entry对象中的值。
第二种方式称为:键值对方式
public class TestMap02 {
public static void main(String[] args) {
//第一种遍历方式:以键找值
//1.创建一个Map的实现类对象
HashMap<String, Integer> map = new HashMap<String, Integer>();
//2.添加几个
map.put("张三", 18);
map.put("李四", 28);
map.put("王五", 38);
map.put("赵六", 48);
//Map集合遍历的第二种方式:键值对方式
//3.获取Map中所有的键值对
Set<Map.Entry<String, Integer>> entries = map.entrySet();
//4.遍历这个entries集合
for (Map.Entry<String, Integer> entry : entries) {
//5.从entry中取出键和值
String key = entry.getKey();
Integer value = entry.getValue();
//6.打印
System.out.println(key+"..."+value);
}
}
}
HashMap存储自定义类型
HashMap存储键位自定义类型时,必须在自定义类型中重写hash和equals方法
需求:
创建一个Map,学生作为键, 家庭住址作为值。
HashMap<Student,String>
public class TestDemo {
public static void main(String[] args) {
//创建一个Map,学生作为键, 家庭住址作为值。
//1.创建集合
HashMap<Student,String> map = new HashMap<Student, String>();
//2.添加数据
map.put(new Student("jack",12),"北京中关村");
map.put(new Student("rose",16),"南京中关村");
map.put(new Student("marry",20),"天津中关村");
map.put(new Student("tom",12),"东京中关村");
//3.打印
//{Student{name=jack,age=12}="北京中关村",键=值,键=值,键=值}
System.out.println(map);
//4.我要修改rose的地址
map.put(new Student("rose",16),"广东东莞");
System.out.println(map);
}
}
结论:
如果键是自定义类型,为了保证键的唯一性,必须重写hashCode和equals方法
LinkedHashMap介绍
HashMap底层采用哈希表结构,是无序的.
LinkedHashMap底层采用链表+哈希表结构,是有序的
public class LinkedHashMapDemo {
public static void main(String[] args) {
LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
map.put("邓超", "孙俪");
map.put("李晨", "范冰冰");
map.put("刘德华", "朱丽倩");
Set<Entry<String, String>> entrySet = map.entrySet();
for (Entry<String, String> entry : entrySet) {
System.out.println(entry.getKey() + " " + entry.getValue());
}
}
}
TreeMap集合
TreeMap底层采用红黑树结构
TreeMap也是无序的, 会按照键的自然顺序默认升序
如果存储的键为自定义类型,则必须使用比较器进行排序,否则TreeMap的自然顺序会不知道按照对象的什么来排序.
public static void main(String[] args) {
TreeMap<Integer, String> map = new TreeMap<Integer, String>();
map.put(1,"张三");
map.put(4,"赵六");
map.put(3,"王五");
map.put(6,"酒八");
map.put(5,"老七");
map.put(2,"李四");
System.out.println(map);
}
控制台的输出结果为:
{1=张三, 2=李四, 3=王五, 4=赵六, 5=老七, 6=酒八}
自然排序
扩展:
Arrays.sort | Collections.sort
| TreeSet | TreeMap 按照如下进行
如果键是数值类型,按照键值大小升序排序,如果是字符串类型, 先按照首字母码值,再按次字母码值
如果想要采用从大到小等自定义规则排序, 则使用比较器Comparator 排序就可以
在TreeMap中可以使用如下代码,使用其构造方法
public class Student {
private int age;
private String name;
//省略get/set..
public Student() {}
public Student(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(age, name);
}
}
public static void main(String[] args) {
TreeMap<Student, String> map = new TreeMap<Student, String>(new Comparator<Student>
() {
@Override
public int compare(Student o1, Student o2) {
//先按照年龄升序
int result = o1.getAge() - o2.getAge();
if (result == 0) {
//年龄相同,则按照名字的首字母升序
return o1.getName().charAt(0) - o2.getName().charAt(0);
} else {
//年龄不同,直接返回结果
return result;
}
}
});
map.put(new Student(30, "jack"), "深圳");
map.put(new Student(10, "rose"), "北京");
map.put(new Student(20, "tom"), "上海");
map.put(new Student(10, "marry"), "南京");
map.put(new Student(30, "lucy"), "广州");
System.out.println(map);
}
控制台的输出结果为:
{
Student{age=10, name='marry'}=南京,
Student{age=10, name='rose'}=北京,
Student{age=20, name='tom'}=上海,
Student{age=30, name='jack'}=深圳,
Student{age=30, name='lucy'}=广州
}
Map集合练习[重点]
需求: 输入一个字符串中每个字符出现次数。
public class MapTest {
public static void main(String[] args) {
//友情提示
System.out.println("请录入一个字符串:");
String line = new Scanner(System.in).nextLine();
// 定义 每个字符出现次数的方法
findChar(line);
}
private static void findChar(String line) {
//1:创建一个集合 存储 字符 以及其出现的次数
HashMap<Character, Integer> map = new HashMap<Character, Integer>();
//2:遍历字符串
for (int i = 0; i < line.length(); i++) {
char c = line.charAt(i);
//判断 该字符 是否在键集中
if (!map.containsKey(c)) {//说明这个字符没有出现过
//那就是第一次
map.put(c, 1);
} else {
//先获取之前的次数
Integer count = map.get(c);
//count++;
//再次存入 更新
map.put(c, ++count);
}
}
System.out.println(map);
}
}
集合嵌套[非常重要]
总述:任何集合内部都可以存储其它任何集合
List嵌套List
public class Test{
public static void main(String[] args){
/*
假如有两个班的学生姓名,它们分别存储在两个集合中:
*/
//第一个班
ArrayList<String> list1 = new ArrayList<>();
list1.add("迪丽热巴");
list1.add("古力娜扎");
list1.add("柳岩");
list1.add("杨幂");
//第二个班
ArrayList<String> list2 = new ArrayList<>();
list2.add("蔡徐坤");
list2.add("杨坤");
list2.add("陈伟霆");
list2.add("李易峰");
//将两个集合存储到一个集合中
ArrayList<ArrayList<String>> allList = new ArrayList<>();
allList.add(list1);
allList.add(list2);
System.out.println(allList);
//遍历allList,取出每个ArrayList
for(ArrayList<String> list : allList){
//遍历每个班的ArrayList
for(String s : list){
System.out.println(s);
}
}
}
}
List嵌套Map
public class Test{
public static void main(String[] args){
/*
有两个班的学员,分别存储在两个Map中
*/
//第一个班:
Map<String,String> map1 = new HashMap<>();
map1.put("it001","迪丽热巴");
map1.put("it002","古力娜扎");
//第二个班:
Map<String,String> map2 = new HashMap<>();
map2.put("heima001","蔡徐坤");
map2.put("heima002","李易峰");
//将两个班的map存储到一个ArrayList中
ArrayList<Map<String,String>> allList = new ArrayList<>();
allList.add(map1);
allList.add(map2);
//遍历allList,取出每个Map
for(Map<String,String> map : allList){
//遍历map
Set<String> keys = map.keySet();
for(String key : keys){
System.out.println(key + " - " + map.get(key));
}
}
}
Map嵌套Map
public class Test{
public static void main(String[] args){
/*
有两个班,班号分别为:"黑马188期"和"黑马189期",两个班学员的姓名分别存储在两个Map中
*/
//"黑马188期":
Map<String,String> map1 = new HashMap<>();
map1.put("it001","迪丽热巴");
map1.put("it002","古力娜扎");
//"黑马189期":
Map<String,String> map2 = new HashMap<>();
map2.put("heima001","蔡徐坤");
map2.put("heima002","李易峰");
//将两个班的Map连同对应的"班号"一同存储在一个Map中
Map<String,Map<String,String>> allMap = new HashMap<>();
allMap.put("黑马188期",map1);
allMap.put("黑马189期",map2);
//遍历allMap
Set<String> keys = allMap.keySet();
for(String k : keys){
System.out.println(k + ":");
//取出对应的map
Map<String,String> map = allMap.get(k);
//遍历map
Set<String> keys2 = map.keySet();
for(String k2 : keys2){
System.out.println(k2 + " = " + map.get(k2));
}
}
}
}
模拟斗地主洗牌发牌[重点]
案例介绍
需求: 模拟斗地主发牌,看牌(但是不打牌)
图解斗地主洗牌
分析
步骤分析:
1.准备 编号和牌 组成的Map集合
2.准备一副牌(54个编号)
3.洗牌(打乱集合)
4.发牌(遍历集合)
5.排序(sort方法)
6.转牌(以键找值)
7.打印给用户看
斗地主洗牌代码实现
public class TestDouDiZhu {
public static void main(String[] args) {
//1.准备 编号和牌 组成的Map集合
LinkedHashMap<Integer, String> map = new LinkedHashMap<Integer, String>();
//a.花色 4种
String[] colors = {"♠", "♥", "♣", "♦"};
//b.数值 13种
String[] nums = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"};
//c.编号变量
int id = 1;
//d.组合牌
for (String num : nums) {
for (String color : colors) {
String card = color + num;
//保存到map集合中
map.put(id, card);
id++;
}
}
//e.单独添加大小王
map.put(53, "小S");
map.put(54, "大S");
//2.准备一副牌(54个编号)
ArrayList<Integer> cards = new ArrayList<Integer>();
for (int i = 1; i < 55; i++) {
cards.add(i);
}
//3.洗牌(打乱集合)
Collections.shuffle(cards);
//4.发牌(遍历集合)
ArrayList<Integer> player1 = new ArrayList<Integer>();
ArrayList<Integer> player2 = new ArrayList<Integer>();
ArrayList<Integer> player3 = new ArrayList<Integer>();
ArrayList<Integer> diPai = new ArrayList<Integer>();
//遍历集合
//此处不能使用增强for
for (int i = 0; i < cards.size() - 3; i++) {
//取出每一张牌
Integer card = cards.get(i);
//给谁呢?????????????????????????????????
//i = 0 3 6 p1
//i = 1 4 7 p2
//i = 2 5 8 p3
if (i % 3 == 0) {
player1.add(card);
} else if (i % 3 == 1) {
player2.add(card);
}else{
player3.add(card);
}
}
//最后张三留给底牌
diPai.add(cards.get(53));
diPai.add(cards.get(52));
diPai.add(cards.get(51));
//5.排序(sort方法)
Collections.sort(player1);
Collections.sort(player2);
Collections.sort(player3);
Collections.sort(diPai);
//6.看牌
lookCards(player1,map);
lookCards(player2,map);
lookCards(player3,map);
lookCards(diPai,map);
}
//看牌方法
public static void lookCards(ArrayList<Integer> idCards, LinkedHashMap<Integer, String> map) {
//遍历
for (Integer idCard : idCards) {
String card = map.get(idCard);
System.out.print(card+" ");
}
System.out.println();
}
}
冒泡排序算法(工具类已经写好了)
介绍
- 依次比较相连的两个元素,大的放后边
- 如果有n个数据进行排序,总共需要比较n-1轮(每一轮是指, 如 数组中5个数, 0和1,1和2,2和3,3和4 才算一轮)
- 每一轮比较完毕,下一轮的比较就会少一个数据参与
n个数据一共执行 n-1轮, 每一轮, 需要执行 n-1次
过程图解
冒泡排序动态图.gif
代码实现
/*
冒泡排序:
一种排序的方式,对要进行排序的数据中相邻的数据进行两两比较,将较大的数据放在后面,
依次对所有的数据进行操作,直至所有数据按要求完成排序
*/
public class ArrayDemo {
public static void main(String[] args) {
//定义一个数组
int[] arr = {7, 6, 5, 4, 3};
System.out.println("排序前:" + Arrays.toString(arr));
// 这里减1,是控制每轮比较的次数
for (int x = 0; x < arr.length - 1; x++) {
// -1是为了避免索引越界,-x是为了调高比较效率
for (int i = 0; i < arr.length - 1 - x; i++) {
//如果前>后
if (arr[i] > arr[i + 1]) {
int temp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = temp;
}
}
}
System.out.println("排序后:" + Arrays.toString(arr));
}
}
数结构特点小结
数组特点:查询快,增删慢
单向链表特点:查询慢,增删快
栈特点:先进后出
队列特点:先进先出
哈希表特点:查询速度快
今日小结
1.Map接口定义的共性方法【必须掌握】
put(键,值);
remove(键);
put(重复的键,值);
get(键)
containsKey(键);
containsValue(值);
2.Map各种实现类的遍历方式(a.以键找值 b.键值对方式)【必须掌握】
a.以键找值
Set<K> keys = map.keySet(); //获取所有键的集合
for(K key : keys){ //遍历所有的键
V value = map.get(key)//以键找值
//打印
sout(key,value);
}
b.键值对方式
Set<Map.Entry<K,V>> entrys = map.entrySet();//获取所有的键值对的集合
for(Map.Entry<K,V> entry : entrys){//遍历这个键值对集合
K key = entry.getKey();//获取键值对中的键和值
V value = entry.getValue();//获取键值对中的键和值
//打印
sout(key,value);
}
3.集合嵌套【难点,开发中见的不多】
a。List套List:
ArrayList<ArrayList<String>> arr;
b。List套Map
ArrayList<HashMap<String,Integer>> map
c。Map套Map
HashMap<String,HashMap<String,Integer>> map
4.斗地主牌【必须掌握】
至少3遍!!!
5.冒泡排序【理解】
a。理解冒泡过程
b。算法背下来
for(int i = 0;i<arr.length-1;i++){
for(int j = 0;j<arr.length-1-i;j++){
if(arr[j]>arr[j+1]){
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
```
网友评论