今日内容
- Collections工具类-------------->必须掌握 
  - 常用方法
 
- Set集合-------------->必须掌握 
  - 保证元素唯一的原理
- 使用set集合
 
- Map集合-------------->必须掌握 
  - 常用方法
- 使用Map集合
 
- 集合的嵌套
- 斗地主案例
第一章 Collections类
1.1 Collections常用功能
- 概述:java.util.Collections是集合工具类,用来对集合进行操作。
- 常用方法: 
  -  public static void shuffle(List<?> list) :打乱集合顺序。/** * Created by PengZhiLin on 2021/8/5 8:38 */ public class Test1_shuffle { public static void main(String[] args) { //- `public static void shuffle(List<?> list) :打乱集合顺序。` // 创建List集合,限制集合元素的类型为Integer类型 List<Integer> list = new ArrayList<>(); // 往集合中添加元素 list.add(500); list.add(100); list.add(300); list.add(200); list.add(400); System.out.println("打乱顺序前的集合:"+list); // 打乱集合元素顺序 Collections.shuffle(list); // 打印集合 System.out.println("打乱顺序后的集合:"+list); } }
-  public static <T> void sort(List<T> list):将集合中元素按照默认规则排序。-  什么是默认规则------->事先写好的规则 
-  在哪事先写好排序规则? - 在集合元素所属的类中事先写好排序规则
 
-  如何在集合元素所属的类中写好排序规则呢? - 集合元素所属的类实现Comparable接口,重写compareTo方法,在compareTo方法中写好排序规则
- 注意: 集合元素所属的类必须实现Comparable接口,否则会编译报错
 
-  案例1: /** * Created by PengZhiLin on 2021/8/5 8:39 */ public class Test2_sort { public static void main(String[] args) { //- `public static <T> void sort(List<T> list):将集合中元素按照默认规则排序。` // 创建List集合,限制集合元素的类型为Integer类型 List<Integer> list = new ArrayList<>(); // 往集合中添加元素 list.add(500); list.add(100); list.add(300); list.add(200); list.add(400); System.out.println("排序前的集合:"+list); // 按照默认规则排序 Collections.sort(list); System.out.println("排序后的集合:"+list); } }
-  案例2: /** * Created by PengZhiLin on 2021/8/5 8:50 */ public class Person implements Comparable<Person>{ String name; int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } @Override public int compareTo(Person o) { // 对Person对象进行排序 // 升序: 前减后 // 降序: 后减前 // 前: this 后: 参数o // 按照年龄升序 //return this.age - o.age; // 按照年龄降序 return o.age - this.age; } }/** * Created by PengZhiLin on 2021/8/5 8:39 */ public class Test2_sort { public static void main(String[] args) { //- `public static <T> void sort(List<T> list):将集合中元素按照默认规则排序。` // 创建List集合,限制集合元素的类型为Integer类型 List<Integer> list = new ArrayList<>(); // 往集合中添加元素 list.add(500); list.add(100); list.add(300); list.add(200); list.add(400); System.out.println("排序前的集合:"+list); // 按照默认规则排序 Collections.sort(list); System.out.println("排序后的集合:"+list); System.out.println("------------------------"); // 创建List集合,限制集合元素的类型为Person类型 List<Person> list1 = new ArrayList<>(); // 往集合中添加元素 list1.add(new Person("张三1",48)); list1.add(new Person("张三2",28)); list1.add(new Person("张三3",38)); list1.add(new Person("张三4",18)); list1.add(new Person("张三5",58)); System.out.println("排序前的集合:"+list1); // 对list1集合元素按照默认规则进行排序 Collections.sort(list1); System.out.println("排序后的集合:"+list1); } }
 
-  
-  public static <T> void sort(List<T> list,Comparator<? super T> comp):将集合中元素按照指定规则排序- 在哪指定排序规则---------->通过Comparator接口进行指定规则排序
- 传入Comparator接口的实现类对象,实现类中就会重写compare方法,在compare方法中指定排序规则
 /** * Created by PengZhiLin on 2021/8/5 8:39 */ public class Test3_sort { public static void main(String[] args) { //- `public static <T> void sort(List<T> list,Comparator<? super T> comp):将集合中元素按照指定规则排序` // 创建List集合,限制集合元素的类型为Integer类型 List<Integer> list = new ArrayList<>(); // 往集合中添加元素 list.add(500); list.add(100); list.add(300); list.add(200); list.add(400); System.out.println("排序前的集合:"+list); // 按照指定规则排序 Collections.sort(list, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { // 指定排序规则 // 升序: 前减后 // 降序: 后减前 // 前: 参数1 后: 参数2 return o1 - o2;// 升序 } }); System.out.println("排序后的集合:"+list); Collections.sort(list, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { // 指定排序规则 // 升序: 前减后 // 降序: 后减前 // 前: 参数1 后: 参数2 return o2 - o1;// 降序 } }); System.out.println("排序后的集合:"+list); System.out.println("------------------------"); // 创建List集合,限制集合元素的类型为Person类型 List<Person> list1 = new ArrayList<>(); // 往集合中添加元素 list1.add(new Person("张三1",48)); list1.add(new Person("张三2",28)); list1.add(new Person("张三3",38)); list1.add(new Person("张三4",18)); list1.add(new Person("张三5",58)); System.out.println("排序前的集合:"+list1); // 对list1集合元素按照指定规则进行排序 Collections.sort(list1, new Comparator<Person>() { @Override public int compare(Person o1, Person o2) { return o2.age - o1.age;// 按照年龄降序 } }); System.out.println("排序后的集合:"+list1); // 对list1集合元素按照指定规则进行排序 Collections.sort(list1, new Comparator<Person>() { @Override public int compare(Person o1, Person o2) { return o1.age - o2.age;// 按照年龄升序 } }); System.out.println("排序后的集合:"+list1); } }
 
-  
1.2 可变参数
-  概述: 在JDK1.5之后,定义了可变参数,用来表示一个方法需要接受的多个同类型参数。 
-  格式: 修饰符 返回值类型 方法名(数据类型... 变量名){ }
-  案例: /** * Created by PengZhiLin on 2021/8/5 9:09 */ public class Test1 { public static void main(String[] args) { // 调用method1方法 method1(); method1("itheima"); method1("itheima","itcast"); method1("itheima","itcast","java"); String[] arr = {"itheima", "itcast", "java","php"}; method1(arr); } // 定义一个方法,可以接收多个字符串,可以接收字符串类型的数组 public static void method1(String... strs){ System.out.println("长度:"+strs.length); // 在方法中,可变参数就当做数组来使用 for (int i = 0; i < strs.length; i++) { System.out.println(strs[i]); } } }
-  注意事项: -  可变参数一定是定义在方法的形参位置 
-  一个方法只能有一个可变参数 
-  如果方法中有多个参数,可变参数要放到最后。 /** * Created by PengZhiLin on 2021/8/5 9:24 */ public class Test2_注意事项 { public static void main(String[] args) { method(10,"itheima","itcast"); } //- 可变参数一定是定义在方法的形参位置 //- 一个方法只能有一个可变参数 /* public static void method(int... nums,String... strs){// 编译报错 }*/ //- 如果方法中有多个参数,可变参数要放到最后。 /*public static void method(String... strs,int num){// 编译报错 }*/ public static void method(int num,String... strs){ } }
 
-  
-  应用场景: -  Collections工具类中的批量添加元素的静态方法: 
-  static <T> boolean addAll(Collection<T> c, T... elements) :往集合中添加一些元素。/** * Created by PengZhiLin on 2021/8/5 9:28 */ public class Test3_addAll { public static void main(String[] args) { // 创建List集合,限制集合元素的类型为String类型 List<String> list = new ArrayList<>(); // 往list集合中添加多个元素 Collections.addAll(list,"2","A","K","Q","J","10","9","8","7","6","5","4","3"); System.out.println(list); } }
 
-  
第二章 Set接口
2.1 Set接口介绍
- 概述: java.util.Set接口继承自Collection接口,是单列集合的一个重要分支。
- 特点: 元素没有索引,元素唯一(不重复)
- 注意事项: 
  - Set集合元素没有索引,只能使用迭代器或者增强for循环进行遍历元素
- Set集合没有特殊的方法,都是使用Collection的方法
- Set接口就是Set集合,但凡实现了Set接口的类也叫做Set集合
 
- 常用实现类: 
  - HashSet类: 元素没有索引,元素唯一,元素存取顺序不一致 
    - 存储结构采用的是哈希表结构,由哈希表保证元素唯一
 
- LinkedHashSet类:元素没有索引,元素唯一,元素存取顺序一致 
    - 存储结构采用的是哈希表+链表结构,由哈希表保证元素唯一,由链表保证元素存取顺序一致
 
- TreeSet类: 元素没有索引,元素唯一,可以对元素进行排序 
    - 存储结构采用的是红黑树结构,由红黑树保证元素唯一,由比较器来对元素进行排序
 
 
- HashSet类: 元素没有索引,元素唯一,元素存取顺序不一致 
    
2.2 HashSet集合
-  概述:java.util.HashSet是Set接口的一个实现类, 底层的实现其实是一个java.util.HashMap支持 
-  特点: 元素没有索引,元素唯一,元素存取顺序不一致 
-  案例演示: /** * Created by PengZhiLin on 2021/8/5 9:47 */ public class Test { public static void main(String[] args) { // 创建HashSet集合,限制集合元素的类型为String HashSet<String> set = new HashSet<>(); // 往集合中存储元素 set.add("nba"); set.add("abc"); set.add("bac"); set.add("cba"); set.add("abc"); // 特点: 元素没有索引,元素唯一,元素存取顺序不一致 System.out.println(set); } }
2.3 HashSet保证元素唯一原理(哈希表)
哈希表底层结构
在JDK1.8之前,哈希表底层采用数组+链表实现,即使用数组处理冲突,同一hash值的链表都存储在一个数组里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,哈希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。
简单的来说,哈希表是由数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的,如下图所示。

HashSet保证元素唯一原理
HashSet的底层是哈希表结构,由哈希表保证元素唯一
哈希表如何保证元素唯一:  依靠hashCode和equals方法
哈希表:
   jdk1.8之前: 数组+链表
  jdk1.8之后:  数组+链表 + 红黑树
原理:
1.存储元素的时候,会调用该元素的hashCode方法计算哈希值
2.判断该哈希值对应的位置上是否有元素:
3.如果没有元素,就直接存储该元素
4.如果有元素,就产生了哈希冲突
5.产生了哈希冲突,就得调用该元素的equals方法,与该位置上的所有元素进行一一比较:
  比较完后,如果没有一个元素与该元素相等,就直接存储;
  比较完后,如果有任意一个元素与该元素相等,就不存储;

扩展 HashSet的源码分析—了解
3.4.1 HashSet的成员属性及构造方法
public class HashSet<E> extends AbstractSet<E>
    					implements Set<E>, Cloneable, java.io.Serializable{
    
	//内部一个HashMap——HashSet内部实际上是用HashMap实现的
    private transient HashMap<E,Object> map;
    // 用于做map的值
    private static final Object PRESENT = new Object();
    /**
     * 构造一个新的HashSet,
     * 内部实际上是构造了一个HashMap
     */
    public HashSet() {
        map = new HashMap<>();
    }
    
}
- 通过构造方法可以看出,HashSet构造时,实际上是构造一个HashMap
3.4.2 HashSet的add方法源码解析
public class HashSet{
    //......
    public boolean add(E e) {
       return map.put(e, PRESENT)==null;//内部实际上添加到map中,键:要添加的对象,值:Object对象
    }
    //......
}
3.4.3 HashMap的put方法源码解析
public class HashMap{
    //......
    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
    //......
    static final int hash(Object key) {//根据参数,产生一个哈希值
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
    //......
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; //临时变量,存储"哈希表"——由此可见,哈希表是一个Node[]数组
        Node<K,V> p;//临时变量,用于存储从"哈希表"中获取的Node
        int n, i;//n存储哈希表长度;i存储哈希表索引
        
        if ((tab = table) == null || (n = tab.length) == 0)//判断当前是否还没有生成哈希表
            n = (tab = resize()).length;//resize()方法用于生成一个哈希表,默认长度:16,赋给n
        if ((p = tab[i = (n - 1) & hash]) == null)//(n-1)&hash等效于hash % n,转换为数组索引
            tab[i] = newNode(hash, key, value, null);//此位置没有元素,直接存储
        else {//否则此位置已经有元素了
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))//判断哈希值和equals
                e = p;//将哈希表中的元素存储为e
            else if (p instanceof TreeNode)//判断是否为"树"结构
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {//排除以上两种情况,将其存为新的Node节点
                for (int binCount = 0; ; ++binCount) {//遍历链表
                    if ((e = p.next) == null) {//找到最后一个节点
                        p.next = newNode(hash, key, value, null);//产生一个新节点,赋值到链表
                        if (binCount >= TREEIFY_THRESHOLD - 1) //判断链表长度是否大于了8
                            treeifyBin(tab, hash);//树形化
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))//跟当前变量的元素比较,如果hashCode相同,equals也相同
                        break;//结束循环
                    p = e;//将p设为当前遍历的Node节点
                }
            }
            if (e != null) { // 如果存在此键
                V oldValue = e.value;//取出value
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;//设置为新value
                afterNodeAccess(e);//空方法,什么都不做
                return oldValue;//返回旧值
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
}
2.4 HashSet存储自定义类型元素
-  需求: 使用HashSet集合存储学生对象 
-  结论: HashSet集合要存储自定义类型的元素,保证元素唯一,那么就得必须重写hashCode和equals方法 
-  实现: /** * Created by PengZhiLin on 2021/8/5 10:34 */ public class Student { String name; int age; public Student(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } // 重写hashCode方法: 根据所有属性值计算哈希值 // 重写equals方法: 比较所有的属性值 @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(name, age); } }public class Test { public static void main(String[] args) { /* 场景:在开发中,2个同类型的对象所有属性相同,我们就认为是2个相同的对象 结论: HashSet集合要存储自定义类型的元素,保证元素唯一,那么就得必须重写hashCode和equals方法 */ // 需求: 使用HashSet集合存储学生对象 // 创建HashSet集合,限制集合元素的类型为Student类型 HashSet<Student> set = new HashSet<>(); // 往集合中存储学生对象 set.add(new Student("张三",18)); set.add(new Student("李四",28)); set.add(new Student("王五",38)); set.add(new Student("赵六",48)); set.add(new Student("张三",18)); // 循环遍历集合 for (Student student : set) { System.out.println(student); } } }
2.5 LinkedHashSet
-  概述: java.util.LinkedHashSet 是HashSet的一个子类,底层采用链表+哈希表 
-  特点: - 元素没有索引,元素唯一,元素存取顺序一致
- 存储结构采用的是哈希表+链表结构,由哈希表保证元素唯一,由链表保证元素存取顺序一致
- 如果集合中存储的是自定义类型的元素,那么就要求该元素所属的类要重写hashCode和equals方法
 
-  案例: /** * Created by PengZhiLin on 2021/8/5 10:43 */ public class Test { public static void main(String[] args) { // 创建LinkedHashSet集合,限制集合元素的类型为String LinkedHashSet<String> set = new LinkedHashSet<>(); // 往集合中存储元素 set.add("nba"); set.add("abc"); set.add("bac"); set.add("cba"); set.add("abc"); // 特点: 元素没有索引,元素唯一,元素存取顺序一致 System.out.println(set); System.out.println("--------------------"); // 创建HashSet集合,限制集合元素的类型为Student类型 LinkedHashSet<Student> set1 = new LinkedHashSet<>(); // 往集合中存储学生对象 set1.add(new Student("张三",18)); set1.add(new Student("李四",28)); set1.add(new Student("王五",38)); set1.add(new Student("赵六",48)); set1.add(new Student("张三",18)); // 循环遍历集合 for (Student student : set1) { System.out.println(student); } } }
2.6 TreeSet集合
-  概述: TreeSet集合是Set接口的一个实现类,底层依赖于TreeMap,是一种基于红黑树的实现 
-  特点: - 元素没有索引,元素唯一,可以对元素进行排序
- 存储结构采用的是红黑树结构,由红黑树保证元素唯一,由比较器来对元素进行排序
 
-  排序: -  默认规则排序: public TreeSet(); 创建TreeSet集合对象,该集合对象使用默认规则对元素进行排序-  默认规则是在集合元素所属的类中指定的 
-  要求集合元素所属的类必须实现Comparable接口,重写compareTo方法,在compareTo方法中指定排序规则 
-  案例: /** * Created by PengZhiLin on 2021/8/5 10:50 */ public class Student implements Comparable<Student>{ String name; int age; public Student(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } @Override public int compareTo(Student o) { //return this.age - o.age;// 升序 return o.age - this.age;// 降序 } }/** * Created by PengZhiLin on 2021/8/5 10:49 */ public class Test1_默认规则排序 { public static void main(String[] args) { // 创建TreeSet集合,限制集合元素的类型为Integer TreeSet<Integer> set1 = new TreeSet<>(); // 添加元素 set1.add(500); set1.add(100); set1.add(400); set1.add(200); set1.add(300); // set1:[100, 200, 300, 400, 500]---->升序 System.out.println("set1:"+set1); System.out.println("---------------------"); // 创建TreeSet集合,限制集合元素的类型为Student TreeSet<Student> set2 = new TreeSet<>(); // 添加元素 set2.add(new Student("张三1",48)); set2.add(new Student("张三2",28)); set2.add(new Student("张三3",58)); set2.add(new Student("张三4",18)); set2.add(new Student("张三5",38)); // 循环遍历集合 for (Student student : set2) { System.out.println(student); } } }
 
-  
-  指定规则排序: public TreeSet(Comparator<? super E> comparator); 创建TreeSet集合对象,该集合对象使用指定规则对元素进行排序/** * Created by PengZhiLin on 2021/8/5 10:54 */ public class Test2_指定规则排序 { public static void main(String[] args) { // 创建TreeSet集合,限制集合元素的类型为Integer TreeSet<Integer> set1 = new TreeSet<>(new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o2 - o1; } }); // 添加元素 set1.add(500); set1.add(100); set1.add(400); set1.add(200); set1.add(300); // set1:[500, 400, 300, 200, 100]---->降序 System.out.println("set1:"+set1); System.out.println("---------------------"); // 创建TreeSet集合,限制集合元素的类型为Student TreeSet<Student> set2 = new TreeSet<>(new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { return o1.age - o2.age; } }); // 添加元素 set2.add(new Student("张三1",48)); set2.add(new Student("张三2",28)); set2.add(new Student("张三3",58)); set2.add(new Student("张三4",18)); set2.add(new Student("张三5",38)); // 循环遍历集合 for (Student student : set2) { System.out.println(student); } } }
 
-  
第三章 Map集合
3.1 Map概述
-  概述: java.util.Map双列集合的顶层接口,用来存储具备映射关系对象的集合接口定义 
-  双列集合: 以键值对的形式进行存储数据 - eg: 黄晓明=杨颖
- eg: 文章=马伊琍
- eg: 谢霆锋=王菲
- eg: 李亚鹏=王菲
- eg: 文章=姚笛------>存储进去,姚笛就会覆盖之前的马伊琍
- …
 
-  特点: - 键唯一,值可以重复,如果键重复了,值就会覆盖
- Map集合根据键取值
 
-  实现类: HashMap: 键值对存取无序 底层采用的是哈希表结构,由哈希表保证键唯一 LinkedHashMap:键值对存取顺序一致 底层采用的是哈希表+链表结构,由哈希表保证键唯一,由链表保证键值对存取顺序一致 TreeMap:底层采用的是红黑色结构,可以对键进行排序 底层采用的是红黑色结构,使用的比较器对键进行排序 ...
3.2 Map的常用方法
- public V put(K key, V value): 把指定的键与指定的值添加到Map集合中。
- public V remove(Object key): 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。
- public V get(Object key)根据指定的键,在Map集合中获取对应的值。
- public boolean containsKey(Object key):判断该集合中是否有此键
- public boolean containsValue(Object value):判断该集合中是否有此值
- public Set<K> keySet(): 获取Map集合中所有的键,存储到Set集合中。
- public Set<Map.Entry<K,V>> entrySet(): 获取到Map集合中所有的 键值对对象 的集合(Set集合)。
Map接口的方法演示
/**
 * Created by PengZhiLin on 2021/8/5 11:06
 */
public class Test {
    public static void main(String[] args) {
        // 创建Map集合,限制键的类型String,限制值的类型String
        Map<String, String> map = new HashMap<>();
        //`public V put(K key, V value)`:  把指定的键与指定的值添加到Map集合中。
        map.put("黄晓明", "杨颖");
        map.put("文章", "马伊琍");
        map.put("谢霆锋", "王菲");
        String v1 = map.put("李亚鹏", "王菲");
        String v2 = map.put("文章", "姚笛");
        //map:{文章=姚笛, 谢霆锋=王菲, 李亚鹏=王菲, 黄晓明=杨颖}
        System.out.println("map:" + map);
        System.out.println("v1:" + v1);// null
        System.out.println("v2:" + v2);// 马伊琍
        //- `public V remove(Object key)`: 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。
        // 删除文章这个键对应的键值对
        String v3 = map.remove("文章");
        System.out.println("v3:" + v3);// 姚笛
        //map:{谢霆锋=王菲, 李亚鹏=王菲, 黄晓明=杨颖}
        System.out.println("map:" + map);
        //- `public V get(Object key)` 根据指定的键,在Map集合中获取对应的值。
        String v4 = map.get("李亚鹏");
        String v5 = map.get("文章");
        System.out.println("v4:" + v4 + ",v5:" + v5);// 王菲,null
        //- `public boolean containsKey(Object key)`:判断该集合中是否有此键
        boolean res1 = map.containsKey("李亚鹏");
        boolean res2 = map.containsKey("文章");
        System.out.println("res1:" + res1 + ",res2:" + res2);// true,false
        //- `public boolean containsValue(Object value):`判断该集合中是否有此值
        boolean res4 = map.containsValue("王菲");
        boolean res5 = map.containsValue("马伊琍");
        System.out.println("res4:" + res4 + ",res5:" + res5);// true,false
        //- `public Set<K> keySet()`: 获取Map集合中所有的键,存储到Set集合中。
        Set<String> keys = map.keySet();
        // keys:[谢霆锋, 李亚鹏, 黄晓明]
        System.out.println("keys:" + keys);
        //- `public Collection<V> values()`: 获取Map集合中所有的值,存储到Collection集合中。
        Collection<String> values = map.values();
        System.out.println("values:" + values);
        //- `public Set<Map.Entry<K,V>> entrySet()`: 获取到Map集合中所有的 键值对对象 的集合(Set集合)。
        // Map.Entry<K,V>: 表示键值对对象
        // Entry是Map接口的成员内部接口,表示方式: Map.Entry<K,V>
        Set<Map.Entry<String, String>> set = map.entrySet();
        for (Map.Entry<String, String> entry : set) {
            System.out.println(entry);
        }
    }
}
3.3 Map的遍历
-  方式1:键找值方式 -  获取Map集合的所有键—>keySet()方法 
-  循环遍历所有的键 
-  根据键找值—>get(K k)方法 
-  案例: /** * Created by PengZhiLin on 2021/8/5 11:39 */ public class Test1_键找值方式 { public static void main(String[] args) { // 创建Map集合,限制键的类型String,限制值的类型String Map<String, String> map = new HashMap<>(); // 添加键值对 map.put("黄晓明", "杨颖"); map.put("文章", "马伊琍"); map.put("谢霆锋", "王菲"); map.put("李亚鹏", "王菲"); //- 获取Map集合的所有键--->keySet()方法 Set<String> keys = map.keySet(); //- 循环遍历所有的键 for (String key : keys) { //- 根据键找值--->get(K k)方法 String value = map.get(key); System.out.println("key:"+key+",value:"+value); } } }
 
-  
-  方式2:键值对对象方式 -  获取所有的 键值对对象----> entrySet()方法 
-  循环遍历所有的 键值对对象 
-  使用 键值对对象 获取键和值—>使用Entry接口的方法 
-  Entry<K,V>接口: - Entry接口是Map接口的成员内部接口,使用的方式是Map.Entry<K,V>
- Entry表示键值对对象,也就是说Entry是用来封装 键值对的
- Entry接口里面的常用方法: 
      - K getKey(); 获取键值对对象封装的键
- V getValue(); 获取键值对对象封装的值
 
 
-  案例: /** * Created by PengZhiLin on 2021/8/5 11:43 */ public class Test2_键值对对象方式 { public static void main(String[] args) { // 创建Map集合,限制键的类型String,限制值的类型String Map<String, String> map = new HashMap<>(); // 添加键值对 map.put("黄晓明", "杨颖"); map.put("文章", "马伊琍"); map.put("谢霆锋", "王菲"); map.put("李亚鹏", "王菲"); // 获取所有的 键值对对象 Set<Map.Entry<String, String>> entries = map.entrySet(); // 循环遍历所有的 键值对对象 for (Map.Entry<String, String> entry : entries) { // 根据键值对对象获取键和值 String key = entry.getKey(); String value = entry.getValue(); System.out.println("key:" + key + ",value:" + value); } } }
 
-  
3.5 HashMap存储自定义类型的键
练习:每位学生(姓名,年龄)都有自己的家庭住址。那么,既然有对应关系,则将学生对象和家庭住址存储到map集合中。学生作为键, 家庭住址作为值。
- 结论: Map集合的键是自定义类型,那么该类必须重写hashCode和equals方法保证键唯一
实现:
/**
 * Created by PengZhiLin on 2021/8/5 11:46
 */
public class Test {
    public static void main(String[] args) {
        /*
            练习:每位学生(姓名,年龄)都有自己的家庭住址。那么,既然有对应关系,则将学生对象和家庭住址存储到map集合中。
                 学生作为键, 家庭住址作为值。
            > 注意,学生姓名相同并且年龄相同视为同一名学生。
         */
        // 创建HashMap集合,限制键的类型为Student,值的类型为String
        HashMap<Student,String> map = new HashMap<>();
        // 往map集合中存储键值对
        Student stu1 = new Student("张三", 18);
        Student stu2 = new Student("李四", 28);
        Student stu3 = new Student("王五", 38);
        Student stu4 = new Student("赵六", 48);
        Student stu5 = new Student("张三", 18);
        map.put(stu1,"深圳");
        map.put(stu2,"北京");
        map.put(stu3,"上海");
        map.put(stu4,"广州");
        map.put(stu5,"香港");
        // 遍历Map集合
        Set<Student> keys = map.keySet();
        for (Student key : keys) {
            System.out.println(key+","+map.get(key));
        }
    }
}
/**
 * Created by PengZhiLin on 2021/8/5 11:48
 */
public class Student {
    String name;
    int age;
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    @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(name, age);
    }
}
3.6 LinkedHashMap
-  概述:LinkedHashMap是HashMap子类,底层由链表和哈希表组合,由哈希表保证键唯一,由链表保证键值对存取顺序一致 
-  结论: 如果键是自定义类型的元素,要保证键唯一,那么该键所属的类需要重写hashCode和equals方法 
-  代码: /** * Created by PengZhiLin on 2021/8/5 11:54 */ public class Test { public static void main(String[] args) { // 创建HashMap集合,限制键的类型为Student,值的类型为String LinkedHashMap<Student,String> map = new LinkedHashMap<>(); // 往map集合中存储键值对 Student stu1 = new Student("张三", 18); Student stu2 = new Student("李四", 28); Student stu3 = new Student("王五", 38); Student stu4 = new Student("赵六", 48); Student stu5 = new Student("张三", 18); map.put(stu1,"深圳"); map.put(stu2,"北京"); map.put(stu3,"上海"); map.put(stu4,"广州"); map.put(stu5,"香港"); // 遍历Map集合 Set<Student> keys = map.keySet(); for (Student key : keys) { System.out.println(key+","+map.get(key)); } } }
3.7 TreeMap集合
-  概述: TreeMap是Map实现类,底层由红黑树实现,可以对元素的键进行排序 
-  构造方法: -  public TreeMap();创建TreeMap集合对象,使用默认规则对键进行排序- 要求键所属的类必须实现Comparable接口,重写compareTo方法,在compareTo方法中写好排序规则
 
-  public TreeMap(Comparator<? super K> comparator);创建TreeMap集合对象,使用指定规则对键进行排序- 通过Comparator接口指定对键的排序规则
 
-  案例1: import java.util.Comparator; import java.util.TreeMap; /** * Created by PengZhiLin on 2021/8/5 11:59 */ public class Test1 { public static void main(String[] args) { // 创建TreeMap集合,限制键的类型为Integer,值的类型为String------>键的默认规则排序 TreeMap<Integer,String> map = new TreeMap<>(); // 往集合中添加键值对的 map.put(5,"深圳"); map.put(1,"广州"); map.put(4,"惠州"); map.put(2,"东莞"); map.put(3,"佛山"); map.put(3,"中山"); System.out.println(map); System.out.println("-----------------------"); // 创建TreeMap集合,限制键的类型为Integer,值的类型为String------>键的指定规则排序 TreeMap<Integer,String> map1 = new TreeMap<>(new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o2 - o1; } }); // 往集合中添加键值对的 map1.put(5,"深圳"); map1.put(1,"广州"); map1.put(4,"惠州"); map1.put(2,"东莞"); map1.put(3,"佛山"); map1.put(3,"中山"); System.out.println(map1); } }
-  案例2: /** * Created by PengZhiLin on 2021/8/5 11:48 */ public class Student implements Comparable<Student>{ String name; int age; public Student(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } @Override public int compareTo(Student o) { return this.age - o.age;// 年龄升序 } }/** * Created by PengZhiLin on 2021/8/5 12:03 */ public class Test2 { public static void main(String[] args) { // 创建TreeMap集合,限制键的类型为Student,值的类型为String--->默认规则排序 TreeMap<Student,String> map = new TreeMap<>(); // 添加键值对 Student stu1 = new Student("张三", 18); Student stu2 = new Student("李四", 48); Student stu3 = new Student("王五", 28); Student stu4 = new Student("赵六", 58); Student stu5 = new Student("田七", 18); map.put(stu1,"深圳"); map.put(stu2,"广州"); map.put(stu3,"惠州"); map.put(stu4,"东莞"); map.put(stu5,"佛山"); // 遍历map集合 Set<Student> keys = map.keySet(); for (Student key : keys) { String value = map.get(key); System.out.println(key+","+value); } System.out.println("----------------------"); // 创建TreeMap集合,限制键的类型为Student,值的类型为String--->指定规则排序 TreeMap<Student,String> map1 = new TreeMap<>(new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { return o2.age - o1.age; } }); // 添加键值对 map1.put(stu1,"深圳"); map1.put(stu2,"广州"); map1.put(stu3,"惠州"); map1.put(stu4,"东莞"); map1.put(stu5,"佛山"); // 遍历map集合 Set<Student> keys1 = map1.keySet(); for (Student key : keys1) { String value = map1.get(key); System.out.println(key+","+value); } } }
 
-  
3.8 Map集合练习
需求
- 输入一个字符串,统计该字符串中每个字符出现次数。
分析
步骤:
1.键盘输入一个字符串
2.创建Map集合,限制键的类型为Character,值的类型为Integer
3.循环遍历字符串
4.在循环中,获取遍历出来的字符
5.在循环中,使用该字符作为键,判断Map集合中是否含有该字符的键
6.如果没有该字符的键,说明第一次出现,那么该字符作为键,值为1,存储到Map集合中
7.如果有该字符的键,说明已经出现过了,那么根据该字符作为键取出对应的值,然后值+1,作为新值,再存储到Map集合中
8.打印Map集合
实现
/**
 * Created by PengZhiLin on 2021/8/5 12:20
 */
public class Test {
    public static void main(String[] args) {
        //步骤:
        //1.键盘输入一个字符串
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入一个字符串:");
        String str = sc.nextLine();
        //2.创建Map集合,限制键的类型为Character,值的类型为Integer
        HashMap<Character, Integer> map = new HashMap<>();
        //3.循环遍历字符串
        for (int i = 0; i < str.length(); i++) {
            //4.在循环中,获取遍历出来的字符
            char c = str.charAt(i);
            //5.在循环中,使用该字符作为键,判断Map集合中是否含有该字符的键
            boolean res = map.containsKey(c);
            if (res == false) {
                //6.如果没有该字符的键,说明第一次出现,那么该字符作为键,值为1,存储到Map集合中
                map.put(c, 1);
            } else {
                //7.如果有该字符的键,说明已经出现过了,那么根据该字符作为键取出对应的值,然后值+1,作为新值,再存储到Map集合中
                Integer value = map.get(c);
                value++;
                map.put(c, value);
            }
        }
        //8.打印Map集合
        System.out.println(map);
    }
}
第四章 集合的嵌套
- 总述:任何集合内部都可以存储其它任何集合
4.1 集合的嵌套
List嵌套List
public class Test1_List嵌套List {
    public static void main(String[] args) {
        // List嵌套List: List集合的元素类型是List
        // 创建List集合,并存储元素
        List<String> list1 = new ArrayList<>();
        list1.add("蔡徐坤");
        list1.add("鹿晗");
        // 创建List集合,并存储元素
        List<String> list2 = new ArrayList<>();
        list2.add("吴亦凡");
        list2.add("黄子韬");
        // 需求: 定义一个List集合,存储以上2个集合,并遍历集合所有元素
        List<List<String>> list = new ArrayList<>();
        // 添加元素
        list.add(list1);
        list.add(list2);
        // 循环遍历集合
        for (List<String> stringList : list) {
            System.out.println(stringList);
            // 循环遍历
            for (String name : stringList) {
                System.out.println(name);
            }
        }
    }
}
List嵌套Map
/**
 * Created by PengZhiLin on 2021/8/5 14:33
 */
public class Test {
    public static void main(String[] args) {
        // List嵌套Map: List集合元素的类型是Map
        // 创建Map集合,添加键值对
        HashMap<String,String> map1 = new HashMap<>();
        map1.put("itheima001","古力娜扎");
        map1.put("itheima002","马尔扎哈");
        // 创建Map集合,添加键值对
        HashMap<String,String> map2 = new HashMap<>();
        map2.put("itcast001","迪丽热巴");
        map2.put("itcast002","欧阳娜娜");
        // 创建List集合,存储以上2个map集合,,并遍历集合所有元素
        List<HashMap<String,String>> list = new ArrayList<>();
        list.add(map1);
        list.add(map2);
        // 循环遍历
        for (HashMap<String, String> map : list) {
            System.out.println(map);
            // 获取map的所有键
            Set<String> keys = map.keySet();
            // 循环遍历所有的键
            for (String key : keys) {
                // 根据键找值
                System.out.println(key+","+map.get(key));
            }
        }
    }
}
Map嵌套Map
/**
 * Created by PengZhiLin on 2021/8/5 14:38
 */
public class Test {
    public static void main(String[] args) {
        // Map嵌套Map: Map集合中存储Map集合
        // 创建Map集合,添加键值对
        HashMap<String, String> map1 = new HashMap<>();
        map1.put("itheima001", "古力娜扎");
        map1.put("itheima002", "马尔扎哈");
        // 创建Map集合,添加键值对
        HashMap<String, String> map2 = new HashMap<>();
        map2.put("itcast001", "迪丽热巴");
        map2.put("itcast002", "欧阳娜娜");
        // 创建Map集合,存储以上2个Map集合(作为值),并遍历集合所有元素
        HashMap<String, HashMap<String, String>> map = new HashMap<>();
        // 添加键值对
        map.put("黑马程序员", map1);
        map.put("传智播客", map2);
        // 获取map集合的所有键
        Set<String> keys = map.keySet();// 黑马程序员,传智播客
        for (String key : keys) {
            // 根据键找值
            HashMap<String, String> valueMap = map.get(key);
            System.out.println(key + " : " + valueMap);
            // 获取valueMap集合的所有键
            Set<String> valueKeys = valueMap.keySet();
            // 循环遍历所有的键
            for (String valueKey : valueKeys) {
                System.out.println(valueKey + ":" + valueMap.get(valueKey));
            }
        }
    }
}
第五章 模拟斗地主洗牌发牌
需求
按照斗地主的规则,完成洗牌发牌的动作。

具体规则:
- 组装54张扑克牌(从大到小进行造牌,然后每张牌给个标记)
- 54张牌顺序打乱
- 三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张留作底牌。
- 查看三人各自手中的牌(按照牌的大小排序)、底牌
分析
1.造牌:
1.1 创建Map集合,用来存储牌的标记和牌
1.2 创建花色的单列集合,用来存储4个花色
1.3 创建牌面值的单列集合,用来存储13个牌面值(从大到小)
1.4 定义一个int类型的变量,用来存储牌的标记,初始值为0
1.5 标记作为键,大王作为值,存储到Map集合中,然后标记+1
1.6 标记作为键,小王作为值,存储到Map集合中,然后标记+1
1.7 牌面值作为外层循环,花色作为内层循环
1.8 在循环中,拼接牌,标记作为键,拼接后的牌作为值,存储到Map集合中,然后标记+1
2.洗牌
2.1 获取map集合中所有的键(所有牌的标记)
2.2 对标记进行打乱顺序
    
3.发牌:
3.1 创建4个单列集合,分别用来存储玩家1,玩家2,玩家3,底牌的标记
3.2 循环遍历打乱顺序之后的标记
3.3 如果遍历出来的标记的索引 >= 51,该标记给底牌
3.3 如果遍历出来的标记的索引%3==0,该标记给玩家1
3.3 如果遍历出来的标记的索引%3==1,该标记给玩家2
3.3 如果遍历出来的标记的索引%3==2,该标记给玩家3
3.4 对玩家1,玩家2,玩家3,底牌获得标记进行从小到大排序
3.5 创建4个单列集合,分别用来存储玩家1,玩家2,玩家3,底牌的牌
3.6 循环遍历排好序的标记,去map集合中取牌
3.7 展示牌(打印输出)
实现
/**
 * Created by PengZhiLin on 2021/8/5 15:03
 */
public class Test {
    public static void main(String[] args) {
        //1.造牌:
        //1.1 创建Map集合,用来存储牌的标记和牌
        HashMap<Integer, String> map = new HashMap<>();
        //1.2 创建花色的单列集合,用来存储4个花色
        ArrayList<String> colors = new ArrayList<>();
        colors.add("♠");
        colors.add("♥");
        colors.add("♣");
        colors.add("♦");
        //1.3 创建牌面值的单列集合,用来存储13个牌面值(从大到小)
        ArrayList<String> numbers = new ArrayList<>();
        Collections.addAll(numbers, "2", "A", "K", "Q", "J", "10", "9", "8", "7", "6", "5", "4", "3");
        //1.4 定义一个int类型的变量,用来存储牌的标记,初始值为0
        int mark = 0;
        //1.5 标记作为键,大王作为值,存储到Map集合中,然后标记+1
        map.put(mark++, "大王");
        //1.6 标记作为键,小王作为值,存储到Map集合中,然后标记+1
        map.put(mark++, "小王");
        //1.7 牌面值作为外层循环,花色作为内层循环
        for (String number : numbers) {
            for (String color : colors) {
                //1.8 在循环中,拼接牌,标记作为键,拼接后的牌作为值,存储到Map集合中,然后标记+1
                String pai = color + number;
                map.put(mark++, pai);
            }
        }
        System.out.println("map:" + map);
        System.out.println("map:" + map.size());
        //2.洗牌
        //2.1 获取map集合中所有的键(所有牌的标记)
        Set<Integer> marksSet = map.keySet();
        ArrayList<Integer> markList = new ArrayList<>();
        markList.addAll(marksSet);
        //2.2 对标记进行打乱顺序
        Collections.shuffle(markList);
        System.out.println("打乱顺序之后的标记:" + markList);
        System.out.println("打乱顺序之后的标记:" + markList.size());
        //3.发牌:
        //3.1 创建4个单列集合,分别用来存储玩家1,玩家2,玩家3,底牌的标记
        ArrayList<Integer> player1Mark = new ArrayList<>();
        ArrayList<Integer> player2Mark = new ArrayList<>();
        ArrayList<Integer> player3Mark = new ArrayList<>();
        ArrayList<Integer> diPaiMark = new ArrayList<>();
        //3.2 循环遍历打乱顺序之后的标记
        for (int i = 0; i < markList.size(); i++) {
            // 获取标记
            Integer paiMark = markList.get(i);
            // 判断
            if (i >= 51) {
                //3.3 如果遍历出来的标记的索引 >= 51,该标记给底牌
                diPaiMark.add(paiMark);
            } else if (i % 3 == 0) {
                //3.3 如果遍历出来的标记的索引%3==0,该标记给玩家1
                player1Mark.add(paiMark);
            } else if (i % 3 == 1) {
                //3.3 如果遍历出来的标记的索引%3==1,该标记给玩家2
                player2Mark.add(paiMark);
            } else if (i % 3 == 2) {
                //3.3 如果遍历出来的标记的索引%3==2,该标记给玩家3
                player3Mark.add(paiMark);
            }
        }
        //3.4 对玩家1,玩家2,玩家3,底牌获得标记进行从小到大排序
        Collections.sort(player1Mark);
        Collections.sort(player2Mark);
        Collections.sort(player3Mark);
        Collections.sort(diPaiMark);
        //3.5 创建4个单列集合,分别用来存储玩家1,玩家2,玩家3,底牌的牌
        ArrayList<String> player1 = new ArrayList<>();
        ArrayList<String> player2 = new ArrayList<>();
        ArrayList<String> player3 = new ArrayList<>();
        ArrayList<String> diPai = new ArrayList<>();
        //3.6 循环遍历排好序的标记,去map集合中取牌
        for (Integer paiMark : player1Mark) {
            // 根据标记去map中取牌
            String pai = map.get(paiMark);
            // 把牌添加到集合中
            player1.add(pai);
        }
        for (Integer paiMark : player2Mark) {
            // 根据标记去map中取牌
            String pai = map.get(paiMark);
            // 把牌添加到集合中
            player2.add(pai);
        }
        for (Integer paiMark : player3Mark) {
            // 根据标记去map中取牌
            String pai = map.get(paiMark);
            // 把牌添加到集合中
            player3.add(pai);
        }
        for (Integer paiMark : diPaiMark) {
            // 根据标记去map中取牌
            String pai = map.get(paiMark);
            // 把牌添加到集合中
            diPai.add(pai);
        }
        //3.7 展示牌(打印输出)
        System.out.println("玩家1:" + player1+",牌数:"+player1.size());
        System.out.println("玩家2:" + player2+",牌数:"+player2.size());
        System.out.println("玩家3:" + player3+",牌数:"+player3.size());
        System.out.println("底牌:" + diPai);
    }
}
总结
必须练习:
	1.Collections工具类的三个方法
    2.可变参数
    3.所有Set集合的特点--->默写
    4.HashSet\HashMap\LinkedHashSet\LinkedHashMap集合保证元素\键唯一的原理--->默写
    5.所有Map集合的特点--->默写
    6.Map集合的常用方法
    7.TreeMap的使用
    8.HashSet,HashMap存储自定义类型的元素或者键
        
- 能够使用集合工具类
    public static void shuffle(List<?> list) :打乱集合顺序。
    public static <T> void sort(List<T> list):将集合中元素按照默认规则排序。
    public static <T> void sort(List<T> list,Comparator<? super T> ):将集合中元素按照指定规则排序
    public static <T> boolean addAll(Collection<T> c, T... elements)  :往集合中添加一些元素。
- 能够使用Comparator比较器进行排序
    传入比较器接口的匿名内部类,在里面重写compare方法,实现排序规则
    前减后: 升序   后减前: 降序
        
- 能够使用可变参数
   格式:参数类型... 形参名
   注意事项:
      一个方法只能有一个可变参数
      如果方法中有多个参数,可变参数要放到最后。
- 能够说出Set集合的特点
   元素唯一,元素无索引
          
- 能够说出哈希表的特点
   哈希表可以保证元素唯一,哈希表底层由数组+链表+红黑树组成
          
- 使用HashSet集合存储自定义元素
   该元素所属的类必须重写hashCode和equals方法
          
- 能够说出Map集合特点
   键唯一,值可以重复,键重复了,值就可以覆盖
   根据键找值
          
- 使用Map集合添加方法保存数据
    public V put(K key, V value):  把指定的键与指定的值添加到Map集合中。
    public V remove(Object key): 把指定的键所对应的键值对元素在Map集合中删除,返回被删除元素的值。
    public V get(Object key) 根据指定的键,在Map集合中获取对应的值。
    public Set<K> keySet(): 获取Map集合中所有的键,存储到Set集合中。
    public Set<Map.Entry<K,V>> entrySet(): 获取到Map集合中所有的键值对对象的集合(Set集合)。
    public boolean containKey(Object key):判断该集合中是否有此键。
          
- 使用”键找值”的方式遍历Map集合
    获取所有的键
    循环遍历所有的键
    根据键找值
          
- 使用”键值对对象”的方式遍历Map集合
    获取所有的键值对对象
    循环遍历所有的键值对对象
    根据键值对对象获取键和获取值
          
- 能够使用HashMap存储自定义键值对的数据
     键所属的类必须重写hashCode和equals方法
- 能够完成斗地主洗牌发牌案例
       略










