一、集合

1.1、集合的体系结构

(1)集合类的特点

  • 特点:提供一种存储空间可变的存储模型,存储的数据容量可以随时发生改变
  • 集合类的体系图01.png

    (2)collection集合的概述和基本使用

  • Collection集合概述

    • 是单例集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素
    • JDK 不提供此接口的任何直接实现,它提供更具体的子接口(如Set和List)实现
  • Collection集合基本使用

    1. public class CollectionDemo01 {
    2. public static void main(String[] args) {
    3. //创建Collection集合的对象
    4. Collection<String> c = new ArrayList<String>();
    5. //添加元素:boolean add(E e)
    6. c.add("hello");
    7. c.add("world");
    8. c.add("java");
    9. //输出集合对象
    10. System.out.println(c);
    11. }
    12. }
  • Collection集合接口的常用方法 | 方法名 | 功能说明 | | —- | —- | | boolean add(E e) | 添加元素 | | boolean remove(Object o) | 从集合中移除指定的元素 | | void clear() | 清空集合中的元素 | | boolean contains(Object o) | 判断集合中是否存在指定的元素 | | boolean isEmpty() | 判断集合是否为空 | | int size() | 集合的长度,也就是集合中元素的个数 |

  • Collcetion集合的遍历

    • 迭代器的介绍
      • 迭代器,集合的专用遍历方式
      • Iterator iterator():返回此集合中元素的迭代器,通过集合的iterator()方法得到
      • 迭代器是通过集合的iterator()方法得到的,所以我们说它是依赖于集合而存在的
  • 迭代器遍历集合代码演示

    1. public class IteratorDemo {
    2. public static void main(String[] args) {
    3. //创建集合对象
    4. Collection<String> c = new ArrayList<>();
    5. //添加元素
    6. c.add("hello");
    7. c.add("world");
    8. c.add("java");
    9. c.add("javaee");
    10. //Iterator<E> iterator():返回此集合中元素的迭代器,通过集合的iterator()方法得到
    11. Iterator<String> it = c.iterator();
    12. //用while循环改进元素的判断和获取
    13. while (it.hasNext()) {
    14. String s = it.next();
    15. System.out.println(s);
    16. }
    17. }
    18. }
  • 迭代器的执行步骤图解02.png

    (3)collection集合使用的案例

  • 案例需求:创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合

  • 代码实现 ```java / 学生类 / public class Student { private String name; private int age;

    //空参构造方法 public Student() { }

    //全参构造方法 public Student(String name, int age) {

    1. this.name = name;
    2. this.age = age;

    }

    //============================= //相对全局变量的set&get方法 public String getName() {

    1. return name;

    }

    public void setName(String name) {

    1. this.name = name;

    }

    public int getAge() {

    1. return age;

    }

    public void setAge(int age) {

    1. this.age = age;

    } }

/ 测试类 / public class CollectionDemo { public static void main(String[] args) { //创建Collection集合对象 Collection c = new ArrayList();

  1. //创建学生对象并利用全参构造方法给全局变量赋值
  2. Student s1 = new Student("林青霞", 30);
  3. Student s2 = new Student("张曼玉", 35);
  4. Student s3 = new Student("王祖贤", 33);
  5. //把学生添加到集合
  6. c.add(s1);
  7. c.add(s2);
  8. c.add(s3);
  9. //遍历集合(迭代器方式)
  10. Iterator<Student> it = c.iterator();
  11. while (it.hasNext()) {
  12. Student s = it.next();
  13. System.out.println(s.getName() + "," + s.getAge());
  14. }
  15. }

}

  1. <a name="4PzN0"></a>
  2. ## 1.2、list集合接口
  3. <a name="1O1dG"></a>
  4. ### (1)list集合接口的认识
  5. - list集合接口的概述
  6. - 有序集合(也称为序列),用户可以精确控制列表中每个元素的插入位置。用户可以通过整数索引访问元素,并搜索列表中的元素
  7. - 与Set集合不同,列表通常允许重复的元素
  8. - List集合接口特点
  9. - 有索引
  10. - 可以存储重复元素
  11. - 元素存取有序
  12. - list集合接口的特有的方法
  13. | 方法名 | 功能说明 |
  14. | --- | --- |
  15. | void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
  16. | E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
  17. | E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
  18. | E get(int index) | 返回指定索引处的元素 |
  19. <a name="uxEtM"></a>
  20. ### (2)list集合接口案例:存储学生对象并遍历
  21. - 案例需求:创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合
  22. - 代码实现
  23. ```java
  24. /*
  25. 学生类
  26. */
  27. public class Student {
  28. private String name;
  29. private int age;
  30. //空参构造方法
  31. public Student() {
  32. }
  33. //全参构造方法
  34. public Student(String name, int age) {
  35. this.name = name;
  36. this.age = age;
  37. }
  38. //相关的属性的get&set方法
  39. public String getName() {
  40. return name;
  41. }
  42. public void setName(String name) {
  43. this.name = name;
  44. }
  45. public int getAge() {
  46. return age;
  47. }
  48. public void setAge(int age) {
  49. this.age = age;
  50. }
  51. }
  52. /*
  53. 测试类
  54. */
  55. public class ListDemo {
  56. public static void main(String[] args) {
  57. //创建List集合对象
  58. List<Student> list = new ArrayList<Student>();
  59. //创建学生对象
  60. Student s1 = new Student("林青霞", 30);
  61. Student s2 = new Student("张曼玉", 35);
  62. Student s3 = new Student("王祖贤", 33);
  63. //把学生添加到集合
  64. list.add(s1);
  65. list.add(s2);
  66. list.add(s3);
  67. //迭代器方式
  68. Iterator<Student> it = list.iterator();
  69. while (it.hasNext()) {
  70. Student s = it.next();
  71. System.out.println(s.getName() + "," + s.getAge());
  72. }
  73. System.out.println("--------");
  74. //for循环方式
  75. for(int i=0; i<list.size(); i++) {
  76. Student s = list.get(i);
  77. System.out.println(s.getName() + "," + s.getAge());
  78. }
  79. }
  80. }

(3)并发修改异常(使用迭代器出现的异常)

  • 出现的原因
    • 迭代器遍历的过程中,通过集合对象修改了集合中的元素,造成了迭代器获取元素中判断预期修改值和实际修改值不一致,则会出现:ConcurrentModificationException
  • 解决的方案
    • 用for循环遍历,然后用集合对象做对应的操作即可
  • 示例代码

    1. public class ListDemo {
    2. public static void main(String[] args) {
    3. //创建集合对象
    4. List<String> list = new ArrayList<String>();
    5. //添加元素
    6. list.add("hello");
    7. list.add("world");
    8. list.add("java");
    9. //遍历集合,得到每一个元素,看有没有"world"这个元素,如果有,我就添加一个"javaee"元素,请写代码实现
    10. // Iterator<String> it = list.iterator();
    11. // while (it.hasNext()) {
    12. // String s = it.next();
    13. // if(s.equals("world")) {
    14. // list.add("javaee");
    15. // }
    16. // }
    17. for(int i=0; i<list.size(); i++) {
    18. String s = list.get(i);
    19. if(s.equals("world")) {
    20. list.add("javaee");
    21. }
    22. }
    23. //输出集合对象
    24. System.out.println(list);
    25. }
    26. }

    (4)列表迭代器

  • ListIterator的介绍

    • 通过List集合的listIterator()方法得到,所以说它是List集合特有的迭代器
    • 用于允许程序员沿任一方向遍历的列表迭代器,在迭代期间修改列表,并获取列表中迭代器的当前位置
  • 示例代码

    1. public class ListIteratorDemo {
    2. public static void main(String[] args) {
    3. //创建集合对象
    4. List<String> list = new ArrayList<String>();
    5. //添加元素
    6. list.add("hello");
    7. list.add("world");
    8. list.add("java");
    9. //获取列表迭代器
    10. ListIterator<String> lit = list.listIterator();
    11. while (lit.hasNext()) {
    12. String s = lit.next();
    13. if(s.equals("world")) {
    14. lit.add("javaee");
    15. }
    16. }
    17. System.out.println(list);
    18. }
    19. }

    (5)增强for循环

  • 作用:简化数组和Collection集合的遍历

  • 原理:jdk5之后出现的,其内部原理是一个iterator迭代器
  • 使用增强for循环的前提: 实现了iterable接口的类才可以使用
  • 增强for循环使用注意事项:增强for循环的变量是第三方变量,修改第三方变量不会影响集合的元素
  • 定义格式

    1. for(元素数据类型 变量名 : 数组/集合对象名) {
    2. 循环体;
    3. }
  • 示例代码

    1. public class ForDemo {
    2. public static void main(String[] args) {
    3. int[] arr = {1,2,3,4,5};
    4. for(int i : arr) {
    5. System.out.println(i);
    6. }
    7. System.out.println("--------");
    8. String[] strArray = {"hello","world","java"};
    9. for(String s : strArray) {
    10. System.out.println(s);
    11. }
    12. System.out.println("--------");
    13. List<String> list = new ArrayList<String>();
    14. list.add("hello");
    15. list.add("world");
    16. list.add("java");
    17. for(String s : list) {
    18. System.out.println(s);
    19. }
    20. System.out.println("--------");
    21. //内部原理是一个Iterator迭代器
    22. /*
    23. for(String s : list) {
    24. if(s.equals("world")) {
    25. list.add("javaee"); //ConcurrentModificationException
    26. }
    27. }
    28. */
    29. }
    30. }

    (6)案例:三种遍历方式

  • 三种遍历方式的使用场景:

    • 如果需要操作索引(比如根据索引判断或查找元素),使用普通for循环
    • 如果需要删除集合元素,使用迭代器
    • 如果仅仅只是想遍历,使用增强for循环
  • 案例需求:创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合 ```java / 学生类 / public class Student { private String name; private int age;

    public Student() { }

    public Student(String name, int age) {

    1. this.name = name;
    2. this.age = age;

    }

    public String getName() {

    1. return name;

    }

    public void setName(String name) {

    1. this.name = name;

    }

    public int getAge() {

    1. return age;

    }

    public void setAge(int age) {

    1. this.age = age;

    } }

/ 测试类 / public class ListDemo { public static void main(String[] args) { //创建List集合对象 List list = new ArrayList();

  1. //创建学生对象
  2. Student s1 = new Student("林青霞", 30);
  3. Student s2 = new Student("张曼玉", 35);
  4. Student s3 = new Student("王祖贤", 33);
  5. //把学生添加到集合
  6. list.add(s1);
  7. list.add(s2);
  8. list.add(s3);
  9. //迭代器:集合特有的遍历方式
  10. Iterator<Student> it = list.iterator();
  11. while (it.hasNext()) {
  12. Student s = it.next();
  13. System.out.println(s.getName()+","+s.getAge());
  14. }
  15. System.out.println("--------");
  16. //普通for:带有索引的遍历方式
  17. for(int i=0; i<list.size(); i++) {
  18. Student s = list.get(i);
  19. System.out.println(s.getName()+","+s.getAge());
  20. }
  21. System.out.println("--------");
  22. //增强for:最方便的遍历方式
  23. for(Student s : list) {
  24. System.out.println(s.getName()+","+s.getAge());
  25. }
  26. }

}

  1. <a name="5OxMm"></a>
  2. ## 1.3、数据结构和list集合接口的实现类
  3. <a name="YhZ9H"></a>
  4. ### (1)常见的数据及分类
  5. - 数据结构之栈和队列
  6. - 栈结构(stack /stæk/):先进后出
  7. - 队列结构(queue /kjuː/):先进先出
  8. - 数据结构之数组和链表
  9. - 数组结构:查询快,增删慢(相对于链表而言的)
  10. - 链表结构:查询慢,增删快(相对于数组而言的)
  11. <a name="7XIzs"></a>
  12. ### (2)list集合接口的实现类
  13. - list集合接口的子类的特点
  14. - ArrayList集合:底层是数组结构实现;查询快,增删慢(相对于链表而言的)
  15. - LinkedList集合:底层是链表结构(双向链表)实现:链表结构:查询慢,增删快(相对于数组而言的)
  16. - ArrayList集合相关的三种遍历方式案例
  17. - 需求:创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合
  18. - 代码实现
  19. ```java
  20. /*
  21. 学生类
  22. */
  23. public class Student {
  24. private String name;
  25. private int age;
  26. public Student() {
  27. }
  28. public Student(String name, int age) {
  29. this.name = name;
  30. this.age = age;
  31. }
  32. public String getName() {
  33. return name;
  34. }
  35. public void setName(String name) {
  36. this.name = name;
  37. }
  38. public int getAge() {
  39. return age;
  40. }
  41. public void setAge(int age) {
  42. this.age = age;
  43. }
  44. }
  45. /*
  46. 测试类
  47. */
  48. public class ArrayListDemo {
  49. public static void main(String[] args) {
  50. //创建ArrayList集合对象
  51. ArrayList<Student> array = new ArrayList<Student>();
  52. //创建学生对象
  53. Student s1 = new Student("林青霞", 30);
  54. Student s2 = new Student("张曼玉", 35);
  55. Student s3 = new Student("王祖贤", 33);
  56. //把学生添加到集合
  57. array.add(s1);
  58. array.add(s2);
  59. array.add(s3);
  60. //迭代器:集合特有的遍历方式
  61. Iterator<Student> it = array.iterator();
  62. while (it.hasNext()) {
  63. Student s = it.next();
  64. System.out.println(s.getName() + "," + s.getAge());
  65. }
  66. System.out.println("--------");
  67. //普通for:带有索引的遍历方式
  68. for(int i=0; i<array.size(); i++) {
  69. Student s = array.get(i);
  70. System.out.println(s.getName() + "," + s.getAge());
  71. }
  72. System.out.println("--------");
  73. //增强for:最方便的遍历方式
  74. for(Student s : array) {
  75. System.out.println(s.getName() + "," + s.getAge());
  76. }
  77. }
  78. }

(3)LinkedList集合特有的成员方法

方法名 功能说明
public void addFirst(E e) 在该列表开头插入指定的元素
public void addLast(E e) 将指定的元素追加到此列表的末尾
public E getFirst() 返回此列表中的第一个元素
public E getLast() 返回此列表中的最后一个元素
public E removeFirst() 从此列表中删除并返回第一个元素
public E removeLast() 从此列表中删除并返回最后一个元素

1.4、set集合接口

(1)set集合接口的认识

  • set集合接口的特点:
    • 元素存取无序
    • 没有索引、只能通过迭代器或增强for循环遍历
    • 不能存储重复元素
  • set集合接口的基本使用

    1. /*
    2. 往集合中添加元素,并遍历集合
    3. */
    4. public class SetDemo {
    5. public static void main(String[] args) {
    6. //创建集合对象
    7. Set<String> set = new HashSet<String>();
    8. //添加元素
    9. set.add("hello");
    10. set.add("world");
    11. set.add("java");
    12. //不包含重复元素的集合
    13. set.add("world");
    14. //遍历
    15. for(String s : set) {
    16. System.out.println(s);
    17. }
    18. }
    19. }
  • 哈希值

    • 哈希值的简介:是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
    • 如何获取哈希值:Object类中的public int hashCode():返回对象的哈希码值
    • 哈希值的特点:同一个对象多次调用hashCode()方法返回的哈希值是相同的;默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同
    • 示例代码 ```java / 学生类 / public class Student { private String name; private int age;

      public Student() { }

      public Student(String name, int age) { this.name = name; this.age = age; }

      public String getName() { return name; }

      public void setName(String name) { this.name = name; }

      public int getAge() { return age; }

      public void setAge(int age) { this.age = age; }

      @Override public int hashCode() { return 0; } }

/ 测试类 / public class HashDemo { public static void main(String[] args) { //创建学生对象 Student s1 = new Student(“林青霞”,30);

  1. //同一个对象多次调用hashCode()方法返回的哈希值是相同的
  2. System.out.println(s1.hashCode()); //1060830840
  3. System.out.println(s1.hashCode()); //1060830840
  4. System.out.println("--------");
  5. Student s2 = new Student("林青霞",30);
  6. //默认情况下,不同对象的哈希值是不相同的
  7. //通过方法重写,可以实现不同对象的哈希值是相同的
  8. System.out.println(s2.hashCode()); //2137211482
  9. System.out.println("--------");
  10. System.out.println("hello".hashCode()); //99162322
  11. System.out.println("world".hashCode()); //113318802
  12. System.out.println("java".hashCode()); //3254818
  13. System.out.println("world".hashCode()); //113318802
  14. System.out.println("--------");
  15. System.out.println("重地".hashCode()); //1179395
  16. System.out.println("通话".hashCode()); //1179395
  17. }

}

  1. <a name="yq4u5"></a>
  2. ### (2)HashSet集合的概述与特点
  3. - HashSet集合的特点
  4. - 底层数据结构是哈希表
  5. - 对集合的迭代顺序不作任何保证,也就是说不保证存储和取出的元素顺序一致(无序性)
  6. - 没有带索引的方法,所以不能使用普通for循环遍历
  7. - 由于是Set集合,所以是不包含重复元素的集合( 可以去除重复)
  8. - HashSet集合的基本使用
  9. ```java
  10. public class HashSetDemo01 {
  11. public static void main(String[] args) {
  12. //创建集合对象
  13. HashSet<String> hs = new HashSet<String>();
  14. //添加元素
  15. hs.add("hello");
  16. hs.add("world");
  17. hs.add("java");
  18. hs.add("world");
  19. //遍历
  20. for(String s : hs) {
  21. System.out.println(s);
  22. }
  23. }
  24. }

(3)HashSet集合集合保证元素唯一性的源码分析

  • HashSet集合保证元素唯一性的原理

    • 根据对象的哈希值计算存储位置
      • 如果当前位置没有元素则直接存入
      • 如果当前位置有元素存在,则进入第二步
    • 当前元素的元素和已经存在的元素比较哈希值
      • 如果哈希值不同,则将当前元素进行存储
      • 如果哈希值相同,则进入第三步
    • 通过equals()方法比较两个元素的内容
      • 如果内容不相同,则将当前元素进行存储
      • 如果内容相同,则不存储当前元素
    • HashSet集合保证元素唯一性的图解02.png
    • HashSet集合案例:存储学生对象并遍历
      • 需求:创建一个存储学生对象的集合,存储多个学生对象,使用程序在控制台遍历输出,特别说明,学生对象的成员变量值相同,我们就认为是同一个对象
    • 代码实现 ```java / 学生类 / public class Student { private String name; private int age;

      public Student() { }

      public Student(String name, int age) { this.name = name; this.age = age; }

      public String getName() { return name; }

      public void setName(String name) { this.name = name; }

      public int getAge() { return age; }

      public void setAge(int age) { this.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;

      if (age != student.age) return false; return name != null ? name.equals(student.name) : student.name == null; }

      @Override public int hashCode() { //通过成员变量name获取哈希值,相同的成员变量获得到的哈希值是相同的 int result = name != null ? name.hashCode() : 0; //如果成员变量age也相同,则哈希值就一定相同,哈希值相同则表示是同一个对象了, //就符合我们的要求了 result = 31 * result + age; return result; } }

/ 测试类 / public class HashSetDemo02 { public static void main(String[] args) { //创建HashSet集合对象 HashSet hs = new HashSet();

  1. //创建学生对象
  2. Student s1 = new Student("林青霞", 30);
  3. Student s2 = new Student("张曼玉", 35);
  4. Student s3 = new Student("王祖贤", 33);
  5. Student s4 = new Student("王祖贤", 33);
  6. //把学生添加到集合
  7. hs.add(s1);
  8. hs.add(s2);
  9. hs.add(s3);
  10. hs.add(s4);
  11. //遍历集合(增强for)
  12. for (Student s : hs) {
  13. System.out.println(s.getName() + "," + s.getAge());
  14. }
  15. }

}

  1. ![HashSet的示例代码.png](https://cdn.nlark.com/yuque/0/2021/png/12492094/1612842931194-12c121b0-8002-4cf7-b3e2-510059421c35.png#crop=0&crop=0&crop=1&crop=1&height=664&id=QJerV&margin=%5Bobject%20Object%5D&name=HashSet%E7%9A%84%E7%A4%BA%E4%BE%8B%E4%BB%A3%E7%A0%81.png&originHeight=664&originWidth=1173&originalType=binary&ratio=1&rotation=0&showTitle=false&size=553069&status=done&style=none&title=&width=1173)
  2. <a name="lDlJg"></a>
  3. ### (4)LinkedHashSet集合的概述和特点
  4. - LinkedHashSet集合特点
  5. - 哈希表和链表实现的Set接口,具有可预测的迭代次序
  6. - 由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
  7. - 由哈希表保证元素唯一,也就是说没有重复的元素
  8. - LinkedHashSet集合基本使用
  9. ```java
  10. public class LinkedHashSetDemo {
  11. public static void main(String[] args) {
  12. //创建集合对象
  13. LinkedHashSet<String> linkedHashSet = new LinkedHashSet<String>();
  14. //添加元素
  15. linkedHashSet.add("hello");
  16. linkedHashSet.add("world");
  17. linkedHashSet.add("java");
  18. linkedHashSet.add("world");
  19. //遍历集合
  20. for(String s : linkedHashSet) {
  21. System.out.println(s);
  22. }
  23. }
  24. }

1.5、set集合的排序

(1)TreeSet集合的排序分类

  • TreeSet集合排序的概述
    • 可以按照一定的规则进行排序,来实现元素有序,具体排序方式取决于构造方法
      • TreeSet():根据其元素的自然排序进行排序
      • TreeSet(Comparator comparator) :根据指定的比较器进行排序
  • 自然排序Comparable的使用
    • 案例需求:存储学生对象并遍历,创建TreeSet集合使用无参构造方法
    • 案例要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
  • 实现步骤:
    • 用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
    • 自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(T o)方法
    • 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
  • 自然排序的步骤图解李清照.png
  • 代码实现

    1. /*
    2. 学生类
    3. */
    4. public class Student implements Comparable<Student> {
    5. private String name;
    6. private int age;
    7. public Student() {
    8. }
    9. public Student(String name, int age) {
    10. this.name = name;
    11. this.age = age;
    12. }
    13. public String getName() {
    14. return name;
    15. }
    16. public void setName(String name) {
    17. this.name = name;
    18. }
    19. public int getAge() {
    20. return age;
    21. }
    22. public void setAge(int age) {
    23. this.age = age;
    24. }
    25. @Override
    26. public int compareTo(Student s) {
    27. // return 0;
    28. // return 1;
    29. // return -1;
    30. //按照年龄从小到大排序
    31. int num = this.age - s.age;
    32. // int num = s.age - this.age;
    33. //年龄相同时,按照姓名的字母顺序排序
    34. int num2 = num==0?this.name.compareTo(s.name):num;
    35. return num2;
    36. }
    37. }

    HashSet的自然排序.png

    1. /*
    2. 测试类
    3. */
    4. public class TreeSetDemo02 {
    5. public static void main(String[] args) {
    6. //创建集合对象
    7. TreeSet<Student> ts = new TreeSet<Student>();
    8. //创建学生对象
    9. Student s1 = new Student("xishi", 29);
    10. Student s2 = new Student("wangzhaojun", 28);
    11. Student s3 = new Student("diaochan", 30);
    12. Student s4 = new Student("yangyuhuan", 33);
    13. Student s5 = new Student("linqingxia",33);
    14. Student s6 = new Student("linqingxia",33);
    15. //把学生添加到集合
    16. ts.add(s1);
    17. ts.add(s2);
    18. ts.add(s3);
    19. ts.add(s4);
    20. ts.add(s5);
    21. ts.add(s6);
    22. //遍历集合
    23. for (Student s : ts) {
    24. System.out.println(s.getName() + "," + s.getAge());
    25. }
    26. }
    27. }

(2)比较其排序Comparator的使用

  • 案例需求
    • 存储学生对象并遍历,创建TreeSet集合使用带参构造方法
    • 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
  • 实现步骤
    • 用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的
    • 比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare(T o1,T o2)方法
    • 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
  • 代码实现 ```java / 学生类 / public class Student { private String name; private int age;

    public Student() { }

    public Student(String name, int age) {

    1. this.name = name;
    2. this.age = age;

    }

    public String getName() {

    1. return name;

    }

    public void setName(String name) {

    1. this.name = name;

    }

    public int getAge() {

    1. return age;

    }

    public void setAge(int age) {

    1. this.age = age;

    } }

/ 测试类 / public class TreeSetDemo { public static void main(String[] args) { //创建集合对象 TreeSet ts = new TreeSet(new Comparator() { @Override public int compare(Student s1, Student s2) { //this.age - s.age //s1(当前要存储的元素),s2(下一个要存的元素) int num = s1.getAge() - s2.getAge(); int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num; return num2; } });

  1. //创建学生对象
  2. Student s1 = new Student("xishi", 29);
  3. Student s2 = new Student("wangzhaojun", 28);
  4. Student s3 = new Student("diaochan", 30);
  5. Student s4 = new Student("yangyuhuan", 33);
  6. Student s5 = new Student("linqingxia",33);
  7. Student s6 = new Student("linqingxia",33);
  8. //把学生添加到集合
  9. ts.add(s1);
  10. ts.add(s2);
  11. ts.add(s3);
  12. ts.add(s4);
  13. ts.add(s5);
  14. ts.add(s6);
  15. //遍历集合
  16. for (Student s : ts) {
  17. System.out.println(s.getName() + "," + s.getAge());
  18. }
  19. }

}

  1. <a name="he6MQ"></a>
  2. ### (3)排序案例:成绩排序
  3. - 案例需求:用TreeSet集合存储多个学生信息(姓名,语文成绩,数学成绩),并遍历该集合
  4. - 要求:按照总分从高到低出现
  5. - 代码实现
  6. ```java
  7. /*
  8. 学生类
  9. */
  10. public class Student {
  11. private String name;
  12. private int chinese;
  13. private int math;
  14. public Student() {
  15. }
  16. public Student(String name, int chinese, int math) {
  17. this.name = name;
  18. this.chinese = chinese;
  19. this.math = math;
  20. }
  21. public String getName() {
  22. return name;
  23. }
  24. public void setName(String name) {
  25. this.name = name;
  26. }
  27. public int getChinese() {
  28. return chinese;
  29. }
  30. public void setChinese(int chinese) {
  31. this.chinese = chinese;
  32. }
  33. public int getMath() {
  34. return math;
  35. }
  36. public void setMath(int math) {
  37. this.math = math;
  38. }
  39. public int getSum() {
  40. return this.chinese + this.math;
  41. }
  42. }
  43. /*
  44. 测试类
  45. */
  46. public class TreeSetDemo {
  47. public static void main(String[] args) {
  48. //创建TreeSet集合对象,通过比较器排序进行排序
  49. TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
  50. @Override
  51. public int compare(Student s1, Student s2) {
  52. // int num = (s2.getChinese()+s2.getMath())-(s1.getChinese()+s1.getMath());
  53. //主要条件
  54. int num = s2.getSum() - s1.getSum();
  55. //次要条件
  56. int num2 = num == 0 ? s1.getChinese() - s2.getChinese() : num;
  57. int num3 = num2 == 0 ? s1.getName().compareTo(s2.getName()) : num2;
  58. return num3;
  59. }
  60. });
  61. //创建学生对象
  62. Student s1 = new Student("林青霞", 98, 100);
  63. Student s2 = new Student("张曼玉", 95, 95);
  64. Student s3 = new Student("王祖贤", 100, 93);
  65. Student s4 = new Student("柳岩", 100, 97);
  66. Student s5 = new Student("风清扬", 98, 98);
  67. Student s6 = new Student("左冷禅", 97, 99);
  68. // Student s7 = new Student("左冷禅", 97, 99);
  69. Student s7 = new Student("赵云", 97, 99);
  70. //把学生对象添加到集合
  71. ts.add(s1);
  72. ts.add(s2);
  73. ts.add(s3);
  74. ts.add(s4);
  75. ts.add(s5);
  76. ts.add(s6);
  77. ts.add(s7);
  78. //遍历集合
  79. for (Student s : ts) {
  80. System.out.println(s.getName() + "," + s.getChinese() + "," + s.getMath() + "," + s.getSum());
  81. }
  82. }
  83. }

(4)不重复的随机数案例

  • 案例需求:编写一个程序,获取10个1-20之间的随机数,要求随机数不能重复,并在控制台输出
  • 代码实现

    1. public class SetDemo {
    2. public static void main(String[] args) {
    3. //创建Set集合对象
    4. // Set<Integer> set = new HashSet<Integer>();
    5. Set<Integer> set = new TreeSet<Integer>();
    6. //创建随机数对象
    7. Random r = new Random();
    8. //判断集合的长度是不是小于10
    9. while (set.size()<=10) {
    10. //产生一个随机数,添加到集合
    11. int number = r.nextInt(20) + 1;
    12. set.add(number);
    13. }
    14. //遍历集合
    15. for(Integer i : set) {
    16. System.out.println(i);
    17. }
    18. }
    19. }

1.6、Map集合接口

(1)Map集合接口的认识

  • Map集合接口的特点及概述
    • interface Map K:键的数据类型;V:值的数据类型
    • 键不能重复,值可以重复;
    • 键和值是一一对应的,每个键只能找到自己对应的值
    • (键+值)这个整体我们称之为“键值对”或者“键值对对象”,在Java中叫做“Entry /ˈentri/ 对象”
    • 元素存取是无序的
  • Map集合接口的基本的使用

    1. public class MapDemo01 {
    2. public static void main(String[] args) {
    3. //创建集合对象
    4. Map<String,String> map = new HashMap<String,String>();
    5. //V put(K key, V value) 将指定的值与该映射中的指定键相关联
    6. map.put("itheima001","林青霞");
    7. map.put("itheima002","张曼玉");
    8. map.put("itheima003","王祖贤");
    9. map.put("itheima003","柳岩");
    10. //输出集合对象
    11. System.out.println(map);
    12. }
    13. }
  • Map集合接口的常用方法 | 方法名 | 功能说明 | | —- | —- | | V put(K key,V value) | 添加元素 | | V remove(Object key) | 根据键删除键值对元素 | | void clear() | 移除所有的键值对元素 | | boolean containsKey(Object key) | 判断集合是否包含指定的键 | | boolean containsValue(Object value) | 判断集合是否包含指定的值 | | boolean isEmpty() | 判断集合是否为空 | | int size() | 集合的长度,也就是集合中键值对的个数 |

  • Map集合接口的获取数据的功能 | 方法名 | 功能说明 | | —- | —- | | V get(Object key) | 根据键获取值 | | Set keySet() | 获取所有键的集合 | | Collection values() | 获取所有值的集合 | | Set> entrySet() | 获取所有键值对对象的集合 |

  • 示例代码

    1. public class MapDemo03 {
    2. public static void main(String[] args) {
    3. //创建集合对象
    4. Map<String, String> map = new HashMap<String, String>();
    5. //添加元素
    6. map.put("张无忌", "赵敏");
    7. map.put("郭靖", "黄蓉");
    8. map.put("杨过", "小龙女");
    9. //V get(Object key):根据键获取值
    10. // System.out.println(map.get("张无忌"));
    11. // System.out.println(map.get("张三丰"));
    12. //Set<K> keySet():获取所有键的集合
    13. // Set<String> keySet = map.keySet();
    14. // for(String key : keySet) {
    15. // System.out.println(key);
    16. // }
    17. //Collection<V> values():获取所有值的集合
    18. Collection<String> values = map.values();
    19. for(String value : values) {
    20. System.out.println(value);
    21. }
    22. }
    23. }

    (2)Map集合的遍历方式

  • 方式一:调用keySet方法,返回Map集合中所有的键并封装到一个Set集合类型的对象中, 然后用增强for循环遍历在里面调用Map集合对象调用get方法

  • 代码示例

    1. public class MapDemo01 {
    2. public static void main(String[] args) {
    3. //创建集合对象
    4. Map<String, String> map = new HashMap<String, String>();
    5. //添加元素
    6. map.put("张无忌", "赵敏");
    7. map.put("郭靖", "黄蓉");
    8. map.put("杨过", "小龙女");
    9. //获取所有键的集合。用keySet()方法实现
    10. Set<String> keySet = map.keySet();
    11. //遍历键的集合,获取到每一个键。用增强for实现
    12. for (String key : keySet) {
    13. //根据键去找值。用get(Object key)方法实现
    14. String value = map.get(key);
    15. System.out.println(key + "," + value);
    16. }
    17. }
    18. }
  • 方式二:调用Set> entrySet()方法,获取所有键值对对象的集合

  • 代码示例

    1. public class MapDemo02 {
    2. public static void main(String[] args) {
    3. //创建集合对象
    4. Map<String, String> map = new HashMap<String, String>();
    5. //添加元素
    6. map.put("张无忌", "赵敏");
    7. map.put("郭靖", "黄蓉");
    8. map.put("杨过", "小龙女");
    9. //获取所有键值对对象的集合
    10. Set<Map.Entry<String, String>> entrySet = map.entrySet();
    11. //遍历键值对对象的集合,得到每一个键值对对象
    12. for (Map.Entry<String, String> me : entrySet) {
    13. //根据键值对对象获取键和值
    14. String key = me.getKey();
    15. String value = me.getValue();
    16. System.out.println(key + "," + value);
    17. }
    18. }
    19. }

    Map集合的遍历方式.png

  • 方法三:通过map.entrySet返回的set集合中的迭代器进行遍历。

    1. //方法三
    2. System.out.println("通过Map.entrySet使用iterator遍历key和value:");
    3. Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
    4. while (it.hasNext()) {
    5. Map.Entry<String, String> entry = it.next();
    6. System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
    7. }
  • 方法四:通过map中的values方法进行遍历所有的value,但不能遍历key

    1. //第四种
    2. System.out.println("通过Map.values()遍历所有的value,但不能遍历key");
    3. for (String v : map.values()) {
    4. System.out.println("value= " + v);
    5. }
    6. }

(3)Map集合接口的案例

  • 案例需求:创建一个HashMap集合,键是学号(String),值是学生对象(Student)。存储三个键值对元素,并遍历
  • 代码示例 ```java / 学生类 / public class Student { private String name; private int age;

    public Student() { }

    public Student(String name, int age) {

    1. this.name = name;
    2. this.age = age;

    }

    public String getName() {

    1. return name;

    }

    public void setName(String name) {

    1. this.name = name;

    }

    public int getAge() {

    1. return age;

    }

    public void setAge(int age) {

    1. this.age = age;

    } }

/ 测试类 / /* 需求: 创建一个HashMap集合,键是学号(String),值是学生对象(Student)。存储三个键值对元素,并遍历

  1. 思路:
  2. 1:定义学生类
  3. 2:创建HashMap集合对象
  4. 3:创建学生对象
  5. 4:把学生添加到集合
  6. 5:遍历集合
  7. 方式1:键找值
  8. 方式2:键值对对象找键和值

*/ public class HashMapDemo { public static void main(String[] args) { //创建HashMap集合对象 HashMap hm = new HashMap();

  1. //创建学生对象
  2. Student s1 = new Student("林青霞", 30);
  3. Student s2 = new Student("张曼玉", 35);
  4. Student s3 = new Student("王祖贤", 33);
  5. //把学生添加到集合
  6. hm.put("itheima001", s1);
  7. hm.put("itheima002", s2);
  8. hm.put("itheima003", s3);
  9. //方式1:键找值
  10. Set<String> keySet = hm.keySet();
  11. for (String key : keySet) {
  12. Student value = hm.get(key);
  13. System.out.println(key + "," + value.getName() + "," + value.getAge());
  14. }
  15. System.out.println("--------");
  16. //方式2:键值对对象找键和值
  17. Set<Map.Entry<String, Student>> entrySet = hm.entrySet();
  18. for (Map.Entry<String, Student> me : entrySet) {
  19. String key = me.getKey();
  20. Student value = me.getValue();
  21. System.out.println(key + "," + value.getName() + "," + value.getAge());
  22. }
  23. }

}

  1. - 案例需求二:创建一个HashMap集合,键是学生对象(Student),值是居住地 (String)。存储多个元素,并遍历。
  2. - 要求保证键的唯一性:如果学生对象的成员变量值相同,我们就认为是同一个对象
  3. - 代码示例
  4. ```java
  5. /*
  6. 学生类
  7. */
  8. public class Student {
  9. private String name;
  10. private int age;
  11. public Student() {
  12. }
  13. public Student(String name, int age) {
  14. this.name = name;
  15. this.age = age;
  16. }
  17. public String getName() {
  18. return name;
  19. }
  20. public void setName(String name) {
  21. this.name = name;
  22. }
  23. public int getAge() {
  24. return age;
  25. }
  26. public void setAge(int age) {
  27. this.age = age;
  28. }
  29. @Override
  30. public boolean equals(Object o) {
  31. if (this == o) return true;
  32. if (o == null || getClass() != o.getClass()) return false;
  33. Student student = (Student) o;
  34. if (age != student.age) return false;
  35. return name != null ? name.equals(student.name) : student.name == null;
  36. }
  37. /**
  38. 重新设定哈希规则
  39. */
  40. @Override
  41. public int hashCode() {
  42. int result = name != null ? name.hashCode() : 0;
  43. result = 31 * result + age;
  44. return result;
  45. }
  46. }
  47. /*
  48. 测试类
  49. */
  50. public class HashMapDemo {
  51. public static void main(String[] args) {
  52. //创建HashMap集合对象
  53. HashMap<Student, String> hm = new HashMap<Student, String>();
  54. //创建学生对象
  55. Student s1 = new Student("林青霞", 30);
  56. Student s2 = new Student("张曼玉", 35);
  57. Student s3 = new Student("王祖贤", 33);
  58. Student s4 = new Student("王祖贤", 33);
  59. //把学生添加到集合
  60. hm.put(s1, "西安");
  61. hm.put(s2, "武汉");
  62. hm.put(s3, "郑州");
  63. hm.put(s4, "北京");
  64. //遍历集合
  65. Set<Student> keySet = hm.keySet();
  66. for (Student key : keySet) {
  67. String value = hm.get(key);
  68. System.out.println(key.getName() + "," + key.getAge() + "," + value);
  69. }
  70. }
  71. }

(4)ArrayList集合嵌套HashMap集合案例

  • 案例需求:创建一个ArrayList集合,存储三个元素,每一个元素都是HashMap
  • 每一个HashMap的键和值都是String,并遍历。
  • 代码示例

    1. public class ArrayListIncludeHashMapDemo {
    2. public static void main(String[] args) {
    3. //创建ArrayList集合
    4. ArrayList<HashMap<String, String>> array = new ArrayList<HashMap<String, String>>();
    5. //创建HashMap集合,并添加键值对元素
    6. HashMap<String, String> hm1 = new HashMap<String, String>();
    7. hm1.put("孙策", "大乔");
    8. hm1.put("周瑜", "小乔");
    9. //把HashMap作为元素添加到ArrayList集合
    10. array.add(hm1);
    11. HashMap<String, String> hm2 = new HashMap<String, String>();
    12. hm2.put("郭靖", "黄蓉");
    13. hm2.put("杨过", "小龙女");
    14. //把HashMap作为元素添加到ArrayList集合
    15. array.add(hm2);
    16. HashMap<String, String> hm3 = new HashMap<String, String>();
    17. hm3.put("令狐冲", "任盈盈");
    18. hm3.put("林平之", "岳灵珊");
    19. //把HashMap作为元素添加到ArrayList集合
    20. array.add(hm3);
    21. //遍历ArrayList集合
    22. for (HashMap<String, String> hm : array) {
    23. Set<String> keySet = hm.keySet();
    24. for (String key : keySet) {
    25. String value = hm.get(key);
    26. System.out.println(key + "," + value);
    27. }
    28. }
    29. }
    30. }

    (5)统计字符出现的次数

  • 案例需求:键盘录入一个字符串,要求统计字符串中每个字符串出现的次数。举例:键盘录入“aababcabcdabcde” 在控制台输出:“a(5)b(4)c(3)d(2)e(1)”

  • 代码实现

    1. public class HashMapDemo {
    2. public static void main(String[] args) {
    3. //键盘录入一个字符串
    4. Scanner sc = new Scanner(System.in);
    5. System.out.println("请输入一个字符串:");
    6. String line = sc.nextLine();
    7. //创建HashMap集合,键是Character,值是Integer
    8. // HashMap<Character, Integer> hm = new HashMap<Character, Integer>();
    9. TreeMap<Character, Integer> hm = new TreeMap<Character, Integer>();
    10. //遍历字符串,得到每一个字符
    11. for (int i = 0; i < line.length(); i++) {
    12. char key = line.charAt(i);
    13. //拿得到的每一个字符作为键到HashMap集合中去找对应的值,看其返回值
    14. Integer value = hm.get(key);
    15. if (value == null) {
    16. //如果返回值是null:说明该字符在HashMap集合中不存在,就把该字符作为键,1作为值存储
    17. hm.put(key,1);
    18. } else {
    19. //如果返回值不是null:说明该字符在HashMap集合中存在,把该值加1,然后重新存储该字符和对应的值
    20. value++;
    21. hm.put(key,value);
    22. }
    23. }
    24. //遍历HashMap集合,得到键和值,按照要求进行拼接
    25. StringBuilder sb = new StringBuilder();
    26. Set<Character> keySet = hm.keySet();
    27. for(Character key : keySet) {
    28. Integer value = hm.get(key);
    29. sb.append(key).append("(").append(value).append(")");
    30. }
    31. String result = sb.toString();
    32. //输出结果
    33. System.out.println(result);
    34. }
    35. }

1.7、Conllections集合工具类的使用

(1)Conllections集合工具类的功能介绍

  • Conllections工具类的常用方法 | 方法名 | 功能说明 | | —- | —- | | public static void sort(List list) | 将指定的列表按升序排序 | | public static void reverse(List<?> list) | 反转指定列表中元素的顺序 | | public static void shuffle(List<?> list) | 使用默认的随机源随机排列指定的列表 |

  • 示例代码

    1. public class CollectionsDemo01 {
    2. public static void main(String[] args) {
    3. //创建集合对象
    4. List<Integer> list = new ArrayList<Integer>();
    5. //添加元素
    6. list.add(30);
    7. list.add(20);
    8. list.add(50);
    9. list.add(10);
    10. list.add(40);
    11. //public static <T extends Comparable<? super T>> void sort(List<T> list):将指定的列表按升序排序
    12. // Collections.sort(list);
    13. //public static void reverse(List<?> list):反转指定列表中元素的顺序
    14. // Collections.reverse(list);
    15. //public static void shuffle(List<?> list):使用默认的随机源随机排列指定的列表
    16. Collections.shuffle(list);
    17. System.out.println(list);
    18. }
    19. }

    (2)Conllections工具类的案例

  • 案例需求:ArrayList存储学生对象,使用Collections对ArrayList进行排序

  • 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
  • 代码实现 ```java / 学生类 / public class Student { private String name; private int age;

    public Student() { }

    public Student(String name, int age) {

    1. this.name = name;
    2. this.age = age;

    }

    public String getName() {

    1. return name;

    }

    public void setName(String name) {

    1. this.name = name;

    }

    public int getAge() {

    1. return age;

    }

    public void setAge(int age) {

    1. this.age = age;

    } }

/ 测试类 / public class CollectionsDemo02 { public static void main(String[] args) { //创建ArrayList集合对象 ArrayList array = new ArrayList();

  1. //创建学生对象
  2. Student s1 = new Student("linqingxia", 30);
  3. Student s2 = new Student("zhangmanyu", 35);
  4. Student s3 = new Student("wangzuxian", 33);
  5. Student s4 = new Student("liuyan", 33);
  6. //把学生添加到集合
  7. array.add(s1);
  8. array.add(s2);
  9. array.add(s3);
  10. array.add(s4);
  11. //使用Collections对ArrayList集合排序
  12. //sort(List<T> list, Comparator<? super T> c)
  13. Collections.sort(array, new Comparator<Student>() {
  14. @Override
  15. public int compare(Student s1, Student s2) {
  16. //按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
  17. int num = s1.getAge() - s2.getAge();
  18. int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
  19. return num2;
  20. }
  21. });
  22. //遍历集合
  23. for (Student s : array) {
  24. System.out.println(s.getName() + "," + s.getAge());
  25. }
  26. }

}

  1. <a name="VOnRC"></a>
  2. ### (3)利用Conllections工具类模拟斗地主的发牌,看牌洗牌的操作
  3. - 案例需求:通过程序实现斗地主过程中的洗牌,发牌和看牌
  4. - 代码实现
  5. ```java
  6. public class PokerDemo {
  7. public static void main(String[] args) {
  8. //创建一个牌盒,也就是定义一个集合对象,用ArrayList集合实现
  9. ArrayList<String> array = new ArrayList<String>();
  10. //往牌盒里面装牌
  11. /*
  12. ♦2,♦3,♦4...♦K,♦A
  13. ♣2,...
  14. ♥2,...
  15. ♠2,...
  16. 小王,大王
  17. */
  18. //定义花色数组
  19. String[] colors = {"♦", "♣", "♥", "♠"};
  20. //定义点数数组
  21. String[] numbers = {"2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"};
  22. for (String color : colors) {
  23. for (String number : numbers) {
  24. array.add(color + number);
  25. }
  26. }
  27. array.add("小王");
  28. array.add("大王");
  29. //洗牌,也就是把牌打撒,用Collections的shuffle()方法实现
  30. Collections.shuffle(array);
  31. // System.out.println(array);
  32. //发牌,也就是遍历集合,给三个玩家发牌
  33. ArrayList<String> lqxArray = new ArrayList<String>();
  34. ArrayList<String> lyArray = new ArrayList<String>();
  35. ArrayList<String> fqyArray = new ArrayList<String>();
  36. ArrayList<String> dpArray = new ArrayList<String>();
  37. for (int i = 0; i < array.size(); i++) {
  38. String poker = array.get(i);
  39. if (i >= array.size() - 3) {
  40. dpArray.add(poker);
  41. } else if (i % 3 == 0) {
  42. lqxArray.add(poker);
  43. } else if (i % 3 == 1) {
  44. lyArray.add(poker);
  45. } else if (i % 3 == 2) {
  46. fqyArray.add(poker);
  47. }
  48. }
  49. //看牌,也就是三个玩家分别遍历自己的牌
  50. lookPoker("林青霞", lqxArray);
  51. lookPoker("柳岩", lyArray);
  52. lookPoker("风清扬", fqyArray);
  53. lookPoker("底牌", dpArray);
  54. }
  55. //看牌的方法
  56. public static void lookPoker(String name, ArrayList<String> array) {
  57. System.out.print(name + "的牌是:");
  58. for (String poker : array) {
  59. System.out.print(poker + " ");
  60. }
  61. System.out.println();
  62. }
  63. }
  64. /*
  65. 升级版
  66. */
  67. public class PokerDemo {
  68. public static void main(String[] args) {
  69. //创建HashMap,键是编号,值是牌
  70. HashMap<Integer, String> hm = new HashMap<Integer, String>();
  71. //创建ArrayList,存储编号
  72. ArrayList<Integer> array = new ArrayList<Integer>();
  73. //创建花色数组和点数数组
  74. String[] colors = {"♦", "♣", "♥", "♠"};
  75. String[] numbers = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"};
  76. //从0开始往HashMap里面存储编号,并存储对应的牌。同时往ArrayList里面存储编号
  77. int index = 0;
  78. for (String number : numbers) {
  79. for (String color : colors) {
  80. hm.put(index, color + number);
  81. array.add(index);
  82. index++;
  83. }
  84. }
  85. hm.put(index, "小王");
  86. array.add(index);
  87. index++;
  88. hm.put(index, "大王");
  89. array.add(index);
  90. //洗牌(洗的是编号),用Collections的shuffle()方法实现
  91. Collections.shuffle(array);
  92. //发牌(发的也是编号,为了保证编号是排序的,创建TreeSet集合接收)
  93. TreeSet<Integer> lqxSet = new TreeSet<Integer>();
  94. TreeSet<Integer> lySet = new TreeSet<Integer>();
  95. TreeSet<Integer> fqySet = new TreeSet<Integer>();
  96. TreeSet<Integer> dpSet = new TreeSet<Integer>();
  97. for (int i = 0; i < array.size(); i++) {
  98. int x = array.get(i);
  99. if (i >= array.size() - 3) {
  100. dpSet.add(x);
  101. } else if (i % 3 == 0) {
  102. lqxSet.add(x);
  103. } else if (i % 3 == 1) {
  104. lySet.add(x);
  105. } else if (i % 3 == 2) {
  106. fqySet.add(x);
  107. }
  108. }
  109. //调用看牌方法
  110. lookPoker("林青霞", lqxSet, hm);
  111. lookPoker("柳岩", lySet, hm);
  112. lookPoker("风清扬", fqySet, hm);
  113. lookPoker("底牌", dpSet, hm);
  114. }
  115. //定义方法看牌(遍历TreeSet集合,获取编号,到HashMap集合找对应的牌)
  116. public static void lookPoker(String name, TreeSet<Integer> ts, HashMap<Integer, String> hm) {
  117. System.out.print(name + "的牌是:");
  118. for (Integer key : ts) {
  119. String poker = hm.get(key);
  120. System.out.print(poker + " ");
  121. }
  122. System.out.println();
  123. }
  124. }

二、泛型和可变参数

2.1、泛型的概述和优点

(1)泛型的认识

  • 泛型概述
    • 是JDK5中引入的特性,它提供了编译时类型安全检测机制,该机制允许在编译时检测到非法的类型它的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,然后在使用/调用时传入具体的类型。这种参数类型可以用在类、方法和接口中,分别被称为泛型类、泛型方法、泛型接口
  • 泛型定义格式
    • <类型>:指定一种类型的格式。这里的类型可以看成是形参
    • <类型1,类型2…>:指定多种类型的格式,多种类型之间用逗号隔开。这里的类型可以看成是形参
    • 将来具体调用时候给定的类型可以看成是实参,并且实参的类型只能是引用数据类型
  • 泛型的优点

    • 把运行时期的问题提前到了编译期间
    • 避免了强制类型转换

      (2)泛型类的使用和定义

  • 定义格式

    1. 修饰符 class 类名<类型> { }
  • 示例代码 ```java public class Generic { //成员变量的类型用泛型表示 private T t;

    public T getT() {

    1. return t;

    }

    public void setT(T t) {

    1. this.t = t;

    } }

/ 测试类 / public class GenericDemo { public static void main(String[] args) { //创建泛型类对象时必须自定具体的类型 Generic g1 = new Generic(); g1.setT(“林青霞”); System.out.println(g1.getT());

  1. Generic<Integer> g2 = new Generic<Integer>();
  2. g2.setT(30);
  3. System.out.println(g2.getT());
  4. Generic<Boolean> g3 = new Generic<Boolean>();
  5. g3.setT(true);
  6. System.out.println(g3.getT());
  7. }

}

  1. <a name="orCSU"></a>
  2. ### (3)泛型方法的使用和定义
  3. - 定义格式
  4. ```java
  5. 修饰符 <类型> 返回值类型 方法名(类型 变量名) { }
  • 示例代码
    • 带有泛型方法的类 ```java public class Generic { public void show(T t) { System.out.println(t); } }

/ 测试类 / public class GenericDemo { public static void main(String[] args) { Generic g = new Generic(); g.show(“林青霞”); g.show(30); g.show(true); g.show(12.34); } }

  1. <a name="CGxlQ"></a>
  2. ### (4)泛型接口的使用和定义
  3. - 定义格式
  4. ```java
  5. 修饰符 interface 接口名<类型> { }
  • 示例代码 ```java / 泛型接口 / public interface Generic { void show(T t); }

/ 泛型接口的实现类 / public class GenericImpl implements Generic { @Override public void show(T t) { System.out.println(t); } }

/ 测试类 / public class GenericDemo { public static void main(String[] args) { Generic g1 = new GenericImpl(); g1.show(“林青霞”);

  1. Generic<Integer> g2 = new GenericImpl<Integer>();
  2. g2.show(30);
  3. }

}

  1. <a name="A5Khb"></a>
  2. ### (5)类型通配符
  3. - 类型通配符的作用
  4. - 为了表示各种泛型List的父类,可以使用类型通配符
  5. - 类型通配符的分类
  6. - 类型通配符:<?>
  7. - List<?>:表示元素类型未知的List,它的元素可以匹配任何的类型
  8. - 这种带通配符的List仅表示它是各种泛型List的父类,并不能把元素添加到其中
  9. - 类型通配符上限:<? extends 类型>
  10. - List<? extends Number>:它表示的类型是Number或者其子类型
  11. - 类型通配符下限:<? super 类型>
  12. - List<? super Number>:它表示的类型是Number或者其父类型
  13. - 类型通配符的基本使用
  14. ```java
  15. public class GenericDemo {
  16. public static void main(String[] args) {
  17. //类型通配符:<?>
  18. List<?> list1 = new ArrayList<Object>();
  19. List<?> list2 = new ArrayList<Number>();
  20. List<?> list3 = new ArrayList<Integer>();
  21. System.out.println("--------");
  22. //类型通配符上限:<? extends 类型>
  23. // List<? extends Number> list4 = new ArrayList<Object>();
  24. List<? extends Number> list5 = new ArrayList<Number>();
  25. List<? extends Number> list6 = new ArrayList<Integer>();
  26. System.out.println("--------");
  27. //类型通配符下限:<? super 类型>
  28. List<? super Number> list7 = new ArrayList<Object>();
  29. List<? super Number> list8 = new ArrayList<Number>();
  30. // List<? super Number> list9 = new ArrayList<Integer>();
  31. }
  32. }

2.2、可变参数

(1)可变参数的认识

  • 可变参数介绍
    • 可变参数又称参数个数可变,用作方法的形参出现,那么方法参数个数就是可变的了
  • 可变参数定义格式

    1. 修饰符 返回值类型 方法名(数据类型… 变量名) { }
  • 可变参数的注意事项

    • 这里的变量其实是一个数组
    • 如果一个方法有多个参数,包含可变参数,可变参数要放在最后
  • 可变参数的基本使用 ```java public class ArgsDemo01 { public static void main(String[] args) {

    1. System.out.println(sum(10, 20));
    2. System.out.println(sum(10, 20, 30));
    3. System.out.println(sum(10, 20, 30, 40));
    4. System.out.println(sum(10,20,30,40,50));
    5. System.out.println(sum(10,20,30,40,50,60));
    6. System.out.println(sum(10,20,30,40,50,60,70));
    7. System.out.println(sum(10,20,30,40,50,60,70,80,90,100));

    }

// public static int sum(int b,int… a) { // return 0; // }

  1. public static int sum(int... a) {
  2. int sum = 0;
  3. for(int i : a) {
  4. sum += i;
  5. }
  6. return sum;
  7. }

}

  1. <a name="biNbx"></a>
  2. ### (2)可变参数的应用
  3. - Arrays工具类中有一个静态方法:
  4. - public static <T> List<T> asList(T... a):返回由指定数组支持的固定大小的列表
  5. - 返回的集合不能做增删操作,可以做修改操作
  6. - List接口中有一个静态方法:
  7. - public static <E> List<E> of(E... elements):返回包含任意数量元素的不可变列表
  8. - 返回的集合不能做增删改操作
  9. - Set接口中有一个静态方法:
  10. - public static <E> Set<E> of(E... elements) :返回一个包含任意数量元素的不可变集合
  11. - 在给元素的时候,不能给重复的元素
  12. - 返回的集合不能做增删操作,没有修改的方法
  13. - 示例代码
  14. ```java
  15. public class ArgsDemo02 {
  16. public static void main(String[] args) {
  17. //public static <T> List<T> asList(T... a):返回由指定数组支持的固定大小的列表
  18. // List<String> list = Arrays.asList("hello", "world", "java");
  19. //
  20. //// list.add("javaee"); //UnsupportedOperationException
  21. //// list.remove("world"); //UnsupportedOperationException
  22. // list.set(1,"javaee");
  23. //
  24. // System.out.println(list);
  25. //public static <E> List<E> of(E... elements):返回包含任意数量元素的不可变列表
  26. // List<String> list = List.of("hello", "world", "java", "world");
  27. //
  28. //// list.add("javaee");//UnsupportedOperationException
  29. //// list.remove("java");//UnsupportedOperationException
  30. //// list.set(1,"javaee");//UnsupportedOperationException
  31. //
  32. // System.out.println(list);
  33. //public static <E> Set<E> of(E... elements) :返回一个包含任意数量元素的不可变集合
  34. // Set<String> set = Set.of("hello", "world", "java","world"); //IllegalArgumentException
  35. //Set<String> set = Set.of("hello", "world", "java");
  36. // set.add("javaee");//UnsupportedOperationException
  37. // set.remove("world");//UnsupportedOperationException
  38. //System.out.println(set);
  39. }
  40. }

三、file类

3.1、file类的认识与使用

(1)file类的认识

  • File类介绍
    • 它是文件和目录路径名的抽象表示
    • 文件和目录是可以通过File封装成对象的
    • 对于File而言,其封装的并不是一个真正存在的文件,仅仅是一个路径名而已。它可以是存在的,也可以是不存在的。将来是要通过具体的操作把这个路径的内容转换为具体存在的
  • File类的构造方法 | 方法名 | 功能说明 | | —- | —- | | File(String pathname) | 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例 | | File(String parent, String child) | 从父路径名字符串和子路径名字符串创建新的 File实例 | | File(File parent, String child) | 从父抽象路径名和子路径名字符串创建新的 File实例 |

  • 示例代码

    1. public class FileDemo01 {
    2. public static void main(String[] args) {
    3. //File(String pathname):通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。
    4. File f1 = new File("E:\\itcast\\java.txt");
    5. System.out.println(f1);
    6. //File(String parent, String child):从父路径名字符串和子路径名字符串创建新的 File实例。
    7. File f2 = new File("E:\\itcast","java.txt");
    8. System.out.println(f2);
    9. //File(File parent, String child):从父抽象路径名和子路径名字符串创建新的 File实例。
    10. File f3 = new File("E:\\itcast");
    11. File f4 = new File(f3,"java.txt");
    12. System.out.println(f4);
    13. }
    14. }

    File类常用的方法之一.png

  • File类的常用方法 | 方法名 | 功能说明 | | —- | —- | | public boolean createNewFile() | 当具有该名称的文件不存在时,创建一个由该抽象路径名命名的新空文件(注意:如果文件所在的文件夹不存在“abc\\a.txt路径中abc不存在”,则会报错) | | public boolean mkdir() | 创建由此抽象路径名命名的目录 | | public boolean mkdirs() | 创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录 | | length() | 返回文件的字节大小 | | lastModified() | 获取最后一次的修改时间,毫秒值 |

  • 示例代码 ```java package com.xiaoha.day14_advanced;

import java.io.File; import java.io.IOException;

/**

  • File类测试 *
  • @author HausenLee
  • @date 2021/05/13 */ public class FileTest01 { public static void main(String[] args) throws IOException {

    1. //需求1:在在当前项目模块下创建一个目录JavaSE
    2. File f2 = new File("JavaSE");
    3. System.out.println(f2.mkdir());
    4. System.out.println("--------");
    5. //需求2:在当前项目模块下的“JavaSE”目录下创建一个多级目录JavaSE\\test
    6. File f3 = new File("JavaSE\\test");

    // System.out.println(f3.mkdir());//返回结果为false,说明该方法不能创建多级文件目录

    1. System.out.println(f3.mkdirs());
    2. System.out.println("--------");
    3. //需求4:在当前项目模块下的JavaSE下的test下创建一个文件javase.txt
    4. File f4 = new File("JavaSE\\test\\javase.txt");

    // System.out.println(f4.mkdir());

    1. System.out.println(f4.createNewFile());

    } } ```

  • File类常用的判断方法 | 方法名 | 功能说明 | | —- | —- | | public boolean isDirectory() | 测试此抽象路径名表示的File是否为目录 | | public boolean isFile() | 测试此抽象路径名表示的File是否为文件 | | public boolean exists() | 测试此抽象路径名表示的File是否存在 |

  • File类常用的获取方法 | 方法名 | 功能说明 | | —- | —- | | public String getAbsolutePath() | 返回此抽象路径名的绝对路径名字符串 | | public String getPath() | 将此抽象路径名转换为路径名字符串 | | public String getName() | 返回由此抽象路径名表示的文件或目录的名称 | | public String[] list() | 返回此抽象路径名表示的目录中的文件和目录的名称字符串数组 | | public File[] listFiles() | 返回此抽象路径名表示的目录中的文件和目录的File对象数组 |

  • 相对路径和绝对路径

    • 相对路径:指的是当前项目下的; 不完整的路径,不确定,可以找多个资源
    • 绝对路径:从磁盘开始下的路径, 一个完整的路径,只能确定唯一的资源
  • 示例代码 ```java public class FileDemo04 { public static void main(String[] args) {
    1. //创建一个File对象
    2. File f = new File("myFile\\java.txt");

// public boolean isDirectory():测试此抽象路径名表示的File是否为目录 // public boolean isFile():测试此抽象路径名表示的File是否为文件 // public boolean exists():测试此抽象路径名表示的File是否存在 System.out.println(f.isDirectory()); System.out.println(f.isFile()); System.out.println(f.exists());

// public String getAbsolutePath():返回此抽象路径名的绝对路径名字符串 // public String getPath():将此抽象路径名转换为路径名字符串 // public String getName():返回由此抽象路径名表示的文件或目录的名称 System.out.println(f.getAbsolutePath()); System.out.println(f.getPath()); System.out.println(f.getName()); System.out.println(“————“);

// public String[] list():返回此抽象路径名表示的目录中的文件和目录的名称字符串数组 // public File[] listFiles():返回此抽象路径名表示的目录中的文件和目录的File对象数组 File f2 = new File(“E:\itcast”);

  1. String[] strArray = f2.list();
  2. for(String str : strArray) {
  3. System.out.println(str);
  4. }
  5. System.out.println("--------");
  6. File[] fileArray = f2.listFiles();
  7. for(File file : fileArray) {

// System.out.println(file); // System.out.println(file.getName()); if(file.isFile()) { System.out.println(file.getName()); } } } }

  1. ![File类常用的方法之二.png](https://cdn.nlark.com/yuque/0/2021/png/12492094/1612858448398-dc5c08f9-2884-423f-9d6f-49aa0930906a.png#crop=0&crop=0&crop=1&crop=1&height=743&id=TNvrC&margin=%5Bobject%20Object%5D&name=File%E7%B1%BB%E5%B8%B8%E7%94%A8%E7%9A%84%E6%96%B9%E6%B3%95%E4%B9%8B%E4%BA%8C.png&originHeight=743&originWidth=1190&originalType=binary&ratio=1&rotation=0&showTitle=false&size=459609&status=done&style=none&title=&width=1190)
  2. - File类的删除方法
  3. - public boolean delete():删除由此抽象路径名表示的文件或目录
  4. - 示例代码
  5. ```java
  6. public class FileDemo03 {
  7. public static void main(String[] args) throws IOException {
  8. // File f1 = new File("E:\\itcast\\java.txt");
  9. //需求1:在当前模块目录下创建java.txt文件
  10. File f1 = new File("myFile\\java.txt");
  11. // System.out.println(f1.createNewFile());
  12. //需求2:删除当前模块目录下的java.txt文件
  13. System.out.println(f1.delete());
  14. System.out.println("--------");
  15. //需求3:在当前模块目录下创建itcast目录
  16. File f2 = new File("myFile\\itcast");
  17. // System.out.println(f2.mkdir());
  18. //需求4:删除当前模块目录下的itcast目录
  19. System.out.println(f2.delete());
  20. System.out.println("--------");
  21. //需求5:在当前模块下创建一个目录itcast,然后在该目录下创建一个文件java.txt
  22. File f3 = new File("myFile\\itcast");
  23. // System.out.println(f3.mkdir());
  24. File f4 = new File("myFile\\itcast\\java.txt");
  25. // System.out.println(f4.createNewFile());
  26. //需求6:删除当前模块下的目录itcast
  27. System.out.println(f4.delete());
  28. System.out.println(f3.delete());
  29. }
  30. }

File类的删除方法.png

(2)File类的使用——递归

  • 递归的介绍
    • 以编程的角度来看,递归指的是方法定义中调用方法本身的现象
    • 把一个复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解
    • 递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算
  • 递归的基本使用

    1. public class DiGuiDemo {
    2. public static void main(String[] args) {
    3. //回顾不死神兔问题,求第20个月兔子的对数
    4. //每个月的兔子对数:1,1,2,3,5,8,...
    5. int[] arr = new int[20];
    6. arr[0] = 1;
    7. arr[1] = 1;
    8. for (int i = 2; i < arr.length; i++) {
    9. arr[i] = arr[i - 1] + arr[i - 2];
    10. }
    11. System.out.println(arr[19]);
    12. System.out.println(f(20));
    13. }
    14. /*
    15. 递归解决问题,首先就是要定义一个方法:
    16. 定义一个方法f(n):表示第n个月的兔子对数
    17. 那么,第n-1个月的兔子对数该如何表示呢?f(n-1)
    18. 同理,第n-2个月的兔子对数该如何表示呢?f(n-2)
    19. StackOverflowError:当堆栈溢出发生时抛出一个应用程序递归太深
    20. */
    21. public static int f(int n) {
    22. if(n==1 || n==2) {
    23. return 1;
    24. } else {
    25. return f(n - 1) + f(n - 2);
    26. }
    27. }
    28. }
  • 递归的注意事项

    • 递归一定要有出口。否则内存溢出
    • 递归虽然有出口,但是递归的次数也不宜过多。否则内存溢出

      (3)递归求阶乘

  • 案例需求:用递归求5的阶乘,并把结果在控制台输出

  • 代码实现

    1. public class DiGuiDemo01 {
    2. public static void main(String[] args) {
    3. //调用方法
    4. int result = jc(5);
    5. //输出结果
    6. System.out.println("5的阶乘是:" + result);
    7. }
    8. //定义一个方法,用于递归求阶乘,参数为一个int类型的变量
    9. public static int jc(int n) {
    10. //在方法内部判断该变量的值是否是1
    11. if(n == 1) {
    12. //是:返回1
    13. return 1;
    14. } else {
    15. //不是:返回n*(n-1)!
    16. return n*jc(n-1);
    17. }
    18. }
    19. }

    (4)递归遍历文件夹

  • 案例需求

    • 给定一个路径(E:\itcast),通过递归完成遍历该目录下所有内容,并把所有文件的绝对路径输出在控制台
  • 代码实现

    1. public class DiGuiDemo02 {
    2. public static void main(String[] args) {
    3. //根据给定的路径创建一个File对象
    4. // File srcFile = new File("E:\\itcast");
    5. File srcFile = new File("E:\\itheima");
    6. //调用方法
    7. getAllFilePath(srcFile);
    8. }
    9. //定义一个方法,用于获取给定目录下的所有内容,参数为第1步创建的File对象
    10. public static void getAllFilePath(File srcFile) {
    11. //获取给定的File目录下所有的文件或者目录的File数组
    12. File[] fileArray = srcFile.listFiles();
    13. //遍历该File数组,得到每一个File对象
    14. if(fileArray != null) {
    15. for(File file : fileArray) {
    16. //判断该File对象是否是目录
    17. if(file.isDirectory()) {
    18. //是:递归调用
    19. getAllFilePath(file);
    20. } else {
    21. //不是:获取绝对路径输出在控制台
    22. System.out.println(file.getAbsolutePath());
    23. }
    24. }
    25. }
    26. }
    27. }

    (5)统计文件的种类个数

  • 代码实现 ```java public static void main(String[]args){ File file = new File(“要统计文件种类个数的文件夹路径”); //定义map集合进行统计文件种类的个数;键存文件的后缀名,值存对应的文件种类个数 Mapmap = new HashMap(); //调用递归统计的方法;将集合,file对象传递过去 getCount(map,file); //打印集合 System.out.println(map); }

//定义递归的方法 public static void getCount(Mapmap,File file){ //获取该文件夹下所有的文件及文件夹 File[] files = file.listFiles(); //做files对象的为空判断 if(files!=null){ for(File f:files){ if(f.isFile()){ //如果是文件就获取文件的名字 String fileName = f.getName(); String[] fileNameArr = fileName.split(“\.”); //如果数组长度为二 if(fileNameArr.length==2){ String fileEndName = fileNameArr[1]; if(map.containsKey(fileEndName)){ //如果集合中存在,则表示不是第一次添加,就先获取对应的数值 Integer count = map.get(fileEndName); //然后进行自增 count++; //再添加到集合中 map.put(fileEndName,count); }else{ map.put(fileEndName,1); } } } } } }

  1. <a name="cXo9g"></a>
  2. ### (6)递归——数组的快排
  3. ```java
  4. package com.HomeWork.QianwutianHomeWork.Test;
  5. /*
  6. 数组的快排
  7. */
  8. public class digui1 {
  9. public static void main(String[] args) {
  10. //定义一个数组,随便添加几个元素
  11. int arr[]={2,5,4,7,6,1,3};
  12. //定义左指针
  13. int left = 0;
  14. //定义右指针
  15. int reght = arr.length-1;
  16. //调用快排方法,将数组、左右指针传过去
  17. kuaipai(arr,left,reght);
  18. for (int i = 0; i < arr.length; i++) {
  19. System.out.print(arr[i]+" ");
  20. }
  21. }
  22. //定义快排的方法进行递归
  23. private static void kuaipai(int[] arr, int left, int reght) {
  24. if (reght<left){
  25. //如果右边的指针小于左边的指针
  26. //结束方法
  27. return;
  28. }
  29. //定义临时的左指针,用于递归程序的运算
  30. int left0 = left;
  31. //定义临时的右指针,用于递归程序的运算
  32. int reght0 = reght;
  33. //定义基准指元素
  34. int ben = arr[left0];
  35. while (left!=reght){
  36. //如果做指针不等于右指针就不结束循环
  37. while (ben <=arr[reght]&&left<reght){
  38. reght--;
  39. }
  40. while (ben >=arr[left]&&left<reght){
  41. left++;
  42. }
  43. int t = arr[reght];
  44. arr[reght]=arr[left];
  45. arr[left]=t;
  46. }
  47. int t = arr[left];
  48. arr[left]=arr[left0];
  49. arr[left0]=t;
  50. kuaipai(arr,left0,reght-1);
  51. kuaipai(arr,left+1,reght0);
  52. }
  53. }
  54. package com.Study.Qianwutian.Binarr;
  55. /*
  56. 快排
  57. */
  58. public class quicksort {
  59. public static void main(String[] args) {
  60. int arr[] = {6, 4, 8, 3, 7, 9, 2,};
  61. int left = 0, right = arr.length - 1;
  62. //
  63. quickSort(arr,left,right);
  64. for (int i = 0; i < arr.length; i++) {
  65. System.out.print(arr[i]+" ");
  66. }
  67. }
  68. private static void quickSort(int[] arr, int left, int right) {
  69. if (left>right){
  70. return;
  71. }
  72. //把左右索引的值临时存储
  73. int left0 = left;
  74. int right0 = right;
  75. //定义基准数
  76. int ben = arr[left0];
  77. //因为两个指针超中间靠,迟早会相遇(相等)
  78. //相等了就跳出循环执行下一步基准数的归位
  79. while (left!=right) {
  80. //从右边开始
  81. while (ben <= arr[right]&&left<right) {
  82. //要找的数比基准数大,则不进行交换 操作
  83. //然后右指针进行减减
  84. right--;
  85. }
  86. //从左边开始
  87. while (ben >= arr[left]&&left<right) {
  88. //要找的数比基准数小,则不进行交换 操作
  89. //然后右指针进行加加;
  90. left++;
  91. }
  92. //到这一步后,说明找到需要交换的数据
  93. //执行数据交换
  94. int t = arr[left];
  95. arr[left] = arr[right];
  96. arr[right] = t;
  97. }
  98. //跳出循环后,说明左右指针相遇(即相等)
  99. //执行基准数归位
  100. int t = arr[left];
  101. arr[left] = arr[left0];
  102. arr[left0] = t;
  103. quickSort(arr,left0,right-1);
  104. quickSort(arr,left+1,right0);
  105. }
  106. }

3.2、IO流和Stream流

(1)IO流的认识

  • IO流的概述和分类

    • IO流介绍:IO:输入/输出(Input可以将数据从本地文件中读取出来/Output可以将数据从内存中保存到本地文件);流:是一种抽象概念,是对数据传输的总称。也就是说数据在设备间的传输称为流,流的本质是数据传输
    • IO流就是用来处理设备间数据传输问题的。常见的应用:文件复制;文件上传;文件下载
    • IO流的分类
      • 按照数据的流向
        • 输入流:读数据
        • 输出流:写数据
      • 按照数据类型来分
        • 字节流
          • 字节输入流
          • 字节输出流
        • 字符流
          • 字符输入流
          • 字符输出流IO流的分类.png
      • IO流的使用场景
        • 如果操作的是纯文本文件,优先使用字符流
        • 如果操作的是图片、视频、音频等二进制文件。优先使用字节流
        • 如果不确定文件类型,优先使用字节流。字节流是万能的流

          (2)字节流的认识

  • 字节流抽象基类

    • InputStream:这个抽象类是表示字节输入流的所有类的超类
    • OutputStream:这个抽象类是表示字节输出流的所有类的超类
    • 子类名特点:子类名称都是以其父类名作为子类名的后缀
  • 字节输出流
    • FileOutputStream(String name):创建文件输出流以指定的名称写入文件(参数为字符串的路径)
  • 使用字节输出流写数据的步骤
    • 创建字节输出流对象(调用系统功能创建了文件,创建字节输出流对象,让字节输出流对象指向文件)
    • 调用字节输出流对象的写数据方法
    • 释放资源(关闭此文件输出流并释放与此流相关联的任何系统资源)
  • 示例代码

    1. public class FileOutputStreamDemo01 {
    2. public static void main(String[] args) throws IOException {
    3. //创建字节输出流对象
    4. //FileOutputStream(String name):创建文件输出流以指定的名称写入文件
    5. FileOutputStream fos = new FileOutputStream("myByteStream\\fos.txt");
    6. /*
    7. 做了三件事情:
    8. A:调用系统功能创建了文件
    9. B:创建了字节输出流对象
    10. C:让字节输出流对象指向创建好的文件
    11. */
    12. //void write(int b):将指定的字节写入此文件输出流
    13. fos.write(97);//相当于在文件中写了一个小写的a
    14. // fos.write(57);
    15. // fos.write(55);
    16. //最后都要释放资源
    17. //void close():关闭此文件输出流并释放与此流相关联的任何系统资源。
    18. fos.close();
    19. }
    20. }

    (3)字节流写数据的三种方法

  • 写数据的重载方法 | 方法名 | 功能说明 | | —- | —- | | void write(int b) | 将指定的字节写入此文件输出流 一次写一个字节数据 | | void write(byte[] b) | 将 b.length字节从指定的字节数组写入此文件输出流 一次写一个字节数组数据 | | void write(byte[] b, int off, int len) | 将 len字节从指定的字节数组开始,从偏移量off开始写入此文件输出流 一次写一个字节数组的部分数据 |

  • 示例代码 ```java public class FileOutputStreamDemo02 { public static void main(String[] args) throws IOException {

    1. //FileOutputStream(String name):创建文件输出流以指定的名称写入文件
    2. FileOutputStream fos = new FileOutputStream("myByteStream\\fos.txt");
    3. //new File(name)

    // FileOutputStream fos = new FileOutputStream(new File(“myByteStream\fos.txt”));

    1. //FileOutputStream(File file):创建文件输出流以写入由指定的 File对象表示的文件

    // File file = new File(“myByteStream\fos.txt”); // FileOutputStream fos2 = new FileOutputStream(file); // FileOutputStream fos2 = new FileOutputStream(new File(“myByteStream\fos.txt”));

    1. //void write(int b):将指定的字节写入此文件输出流

    // fos.write(97); // fos.write(98); // fos.write(99); // fos.write(100); // fos.write(101);

// void write(byte[] b):将 b.length字节从指定的字节数组写入此文件输出流 // byte[] bys = {97, 98, 99, 100, 101}; //byte[] getBytes():返回字符串对应的字节数组 byte[] bys = “abcde”.getBytes(); // fos.write(bys);

  1. //void write(byte[] b, int off, int len):将 len字节从指定的字节数组开始,从偏移量off开始写入此文件输出流

// fos.write(bys,0,bys.length);参数2表示从数组byte[] b 的第几个索引开始写,int len 表示要写的数据长度

  1. fos.write(bys,1,3);
  2. //释放资源
  3. fos.close();
  4. }

}

  1. - 字节流写数据的两个小问题
  2. - 字节流写数据如何实现换行
  3. - windows:\r\n
  4. - linux:\n
  5. - mac:\r
  6. - 字节流写数据如何实现追加写入
  7. - public FileOutputStream(String name,boolean append)
  8. - 创建文件输出流以指定的名称写入文件。如果第二个参数为true ,则字节将写入文件的末尾而不是开头
  9. - 示例代码
  10. ```java
  11. public class FileOutputStreamDemo03 {
  12. public static void main(String[] args) throws IOException {
  13. //创建字节输出流对象
  14. // FileOutputStream fos = new FileOutputStream("myByteStream\\fos.txt");
  15. FileOutputStream fos = new FileOutputStream("myByteStream\\fos.txt",true);
  16. //写数据
  17. for (int i = 0; i < 10; i++) {
  18. fos.write("hello".getBytes());
  19. fos.write("\r\n".getBytes());
  20. }
  21. //释放资源
  22. fos.close();
  23. }
  24. }
  • 字节流写数据加异常处理的方式
    • try-catch-finally
  • 代码示例 ```java try{ 可能出现异常的代码; }catch(异常类名 变量名){ 异常的处理代码; }finally{ 执行所有清除操作; }

//finally特点:被finally控制的语句一定会执行,除非JVM退出

public class FileOutputStreamDemo04 { public static void main(String[] args) { //加入finally来实现释放资源 FileOutputStream fos = null; try { fos = new FileOutputStream(“myByteStream\fos.txt”); fos.write(“hello”.getBytes()); } catch (IOException e) { e.printStackTrace(); } finally { if(fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } } }

  1. <a name="axeen"></a>
  2. ### (4)字节流读数据(一次读一个字节)
  3. - 字节输入流
  4. - FileInputStream(String name):通过打开与实际文件的连接来创建一个FileInputStream ,该文件由文件系统中的路径名name命名
  5. - 字节输入流读取数据的步骤
  6. - 创建字节输入流对象
  7. - 调用字节输入流对象的读数据方法
  8. - 释放资源
  9. - 示例代码
  10. ```java
  11. package com.xiaoha.day14_advanced;
  12. import java.io.File;
  13. import java.io.FileInputStream;
  14. import java.io.IOException;
  15. /**
  16. * 字节流读取文件操作
  17. *
  18. * @author HausenLee
  19. * @date 2021/05/13
  20. */
  21. public class StreamReaderTest01 {
  22. public static void main(String[] args) {
  23. File file = new File("D:\\IdeaProjects\\java-se-study\\JavaSE\\test\\javase.txt");
  24. FileInputStream fis = null;
  25. try{
  26. //创建字节输入流对象
  27. //构造方法一:FileInputStream(String name)
  28. //FileInputStream fis = new FileInputStream("myByteStream\\fos.txt");
  29. //构造方法二:FileInputStream(File file)
  30. fis = new FileInputStream(file);
  31. int by;
  32. /*
  33. fis.read():读数据
  34. by=fis.read():把读取到的数据赋值给by
  35. by != -1:判断读取到的数据是否是-1
  36. */
  37. while ((by=fis.read())!=-1) {
  38. System.out.print((char)by);
  39. }
  40. }catch(IOException e){
  41. e.printStackTrace();
  42. }finally{
  43. //释放资源
  44. try {
  45. if(fis!=null){
  46. fis.close();
  47. }
  48. } catch (IOException e) {
  49. e.printStackTrace();
  50. }
  51. }
  52. }
  53. }

(5)字节流复制文本文件

  • 案例需求:把“E:\itcast\窗里窗外.txt”复制到模块目录下的“窗里窗外.txt”
  • 实现步骤
    • 复制文本文件,其实就把文本文件的内容从一个文件中读取出来(数据源),然后写入到另一个文件中(目的地)
    • 数据源:E:\itcast\窗里窗外.txt —- 读数据 —- InputStream —- FileInputStream
    • 目的地:myByteStream\窗里窗外.txt —- 写数据 —- OutputStream —- FileOutputStream
  • 代码实现

    1. public class CopyTxtDemo {
    2. public static void main(String[] args) throws IOException {
    3. //根据数据源创建字节输入流对象
    4. FileInputStream fis = new FileInputStream("E:\\itcast\\窗里窗外.txt");
    5. //根据目的地创建字节输出流对象
    6. FileOutputStream fos = new FileOutputStream("myByteStream\\窗里窗外.txt");
    7. //读写数据,复制文本文件(一次读取一个字节,一次写入一个字节)
    8. int by;
    9. while ((by=fis.read())!=-1) {
    10. fos.write(by);
    11. }
    12. //释放资源
    13. fos.close();
    14. fis.close();
    15. }
    16. }
  • 读数据优化之小数组

    • public int read(byte[] b):从输入流读取最多b.length个字节的数据
    • 返回的是读入缓冲区的总字节数,也就是实际的读取字节个数
  • 示例代码

    1. public class FileInputStreamDemo02 {
    2. public static void main(String[] args) throws IOException {
    3. //创建字节输入流对象
    4. FileInputStream fis = new FileInputStream("myByteStream\\fos.txt");
    5. /*
    6. hello\r\n
    7. world\r\n
    8. 第一次:hello
    9. 第二次:\r\nwor
    10. 第三次:ld\r\nr
    11. */
    12. byte[] bys = new byte[1024]; //1024及其整数倍
    13. int len;
    14. while ((len=fis.read(bys))!=-1) {
    15. System.out.print(new String(bys,0,len));
    16. }
    17. //释放资源
    18. fis.close();
    19. }
    20. }

    (6)字节流复制图片

  • 案例需求:把“E:\itcast\mn.jpg”复制到模块目录下的“mn.jpg”

  • 实现步骤
    • 根据数据源创建字节输入流对象
    • 根据目的地创建字节输出流对象
    • 读写数据,复制图片(一次读取一个字节数组,一次写入一个字节数组)
    • 释放资源
  • 代码实现

    1. public class CopyJpgDemo {
    2. public static void main(String[] args) throws IOException {
    3. //根据数据源创建字节输入流对象
    4. FileInputStream fis = new FileInputStream("E:\\itcast\\mn.jpg");
    5. //根据目的地创建字节输出流对象
    6. FileOutputStream fos = new FileOutputStream("myByteStream\\mn.jpg");
    7. //读写数据,复制图片(一次读取一个字节数组,一次写入一个字节数组)
    8. byte[] bys = new byte[1024];
    9. int len;
    10. while ((len=fis.read(bys))!=-1) {
    11. fos.write(bys,0,len);
    12. }
    13. //释放资源
    14. fos.close();
    15. fis.close();
    16. }
    17. }

(7)字符流的认识

  • 字符流的作用:为了解决字节流存数据出现的乱码现象而存在的
  • 编码和解码编码与解码.png
  • 字符流和字节流的使用场景
    • 想要进行拷贝,一律使用字节流或字节缓冲流
    • 想要把文本文件中的数据读到内存中,请使用字符输入流;想要把内存中的数据写到文本文件中,请使用字符输出流。
  • UTF-8编码表与GBK编码表的区别

    • GBK码表一个中文占两个字节(byte),UTF-8编码格式一个中文占三个字节

      (8)字符输出流的使用

  • 构造方法 | 方法名 | 功能说明 | | —- | —- | | FileWriter(File file) | 给一个File对象构造一个FileWriter对象 | | FileWriter(File file,Boolean append) | 一个方法的重载方法,起到追加写数据的效果 | | FileWriter(String FileName) | 给一个指定的文件的路径,构造一个FileWriter对象 |

  • 常用的成员方法 | 方法名 | 功能说明 | | —- | —- | | void write(int c) | 写一个字符 | | void write(char[]cbuf) | 写出一个字符数组 | | void write(char[]cbuf , int off, int len) | 写出一个字符数组的一部分 | | void write(String str) | 写出一个字符串 | | void write(String str , int off , int len) | 写出一个字符串的一部分 |

字符输出流示例代码.png

  • 字符输出流中的flush()方法和close()方法 | 方法名 | 功能说明 | | —- | —- | | flush() | 刷新流,还可以继续写数据 | | close() | 关闭流,释放资源,但是在关闭流之前,会先刷新流,一旦关闭流,就不能写数据了 |

(9)字符输入流的认识

  • 构造方法 | 方法名 | 功能说明 | | —- | —- | | FileReader(File file) | 创建一个新的FileReader对象,通过给定的File对象 | | FileReader(String fileName) | 通过给定的文件的路径创建一个FileReader对象 |

  • 常见的读数据的方法的使用案例字符输入流的案例代码.png

  • 字符流文本复制操作 ```java package com.xiaoha.day14_advanced;

import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException;

/**

  • 利用字符流复制文本文件 *
  • @author HausenLee
  • @date 2021/05/13 */ public class TextCopy { public static void main(String[] args) {
    1. File fileSource = new File("D:\\IdeaProjects\\java-se-study\\JavaSE\\test\\javase.txt");
    2. File fileTarget= new File("D:\\IdeaProjects\\java-se-study\\JavaSE\\test\\TEST_TEXT.txt");
    3. FileWriter fw = null;
    4. FileReader fr = null;
    5. try{
    6. fr = new FileReader(fileSource);
    7. fw = new FileWriter(fileTarget);
    8. char[]chars = new char[1024];
    9. int len;
    10. while((len=fr.read(chars))!=-1){
    11. fw.write(chars,0,len);
    12. }
    13. }catch(IOException e){
    14. e.printStackTrace();
    15. }finally {
    16. if(fw!=null&&fr!=null){
    17. try{
    18. fr.close();
    19. fw.close();
    20. }catch(IOException e){
    21. e.printStackTrace();
    22. }
    23. }
    24. }
    } } ```
  • 缓冲输出流
    • 常用写数据的方法:和字符输出流的一样

缓冲输出流.png

  • 缓冲输入流

    • 常用的读数据的方法和字符输入流一样

      (10)缓冲流的特有方法

  • BufferedWriter:

    • void newLine():写一行行分隔符,行分隔符字符串由系统属性定义(通俗来讲就是换行)
  • BufferedReader
    • String readLine():读一行文字,结果包含了行的内容的字符串,不包括行终止字符,如果流的结尾已经到达,则为null
  • 示例代码字符缓冲流的代码示例.png

(11)转换流

  • 转换流的认识
    • 读的过程中是将字节流转换为字符流读到内存中,写的过程是将字符流转换为字节流写到硬盘中转换流的概念图1.png
    • 转换流的分类转换流概念图.png
    • 转换流的使用转换流的使用.png

(12)对象操作流

  • 对象操作流的分类对象操作流的分类.png
    • 对象操作输出流(对象序列化):就是将对象写到本地文件中,或者在网络中传输对象
    • 对象操作输入流(对象反序列化流):把写到本地文件中的对象读到内存中,或者接收网络中传输的对象
  • 对象操作流的使用注意事项
  • 给对象所属的类定义一个serialVersionUID:public static final long serialVersionUID= xxxL;
  • 被transient和static关键修饰的成员变量:成员变量中被这个关键字修饰表示该成员变量不参与序列化的过程

(13)properties集合

  • Properties介绍
    • 是一个Map体系的集合类
    • Properties可以保存到流中或从流中加载
    • 属性列表中的每个键及其对应的值都是一个字符串
  • Properties基本使用(作为Map集合使用案例)

    1. public class PropertiesDemo01 {
    2. public static void main(String[] args) {
    3. //创建集合对象
    4. // Properties<String,String> prop = new Properties<String,String>(); //错误
    5. Properties prop = new Properties();
    6. //存储元素
    7. prop.put("itheima001", "林青霞");
    8. prop.put("itheima002", "张曼玉");
    9. prop.put("itheima003", "王祖贤");
    10. //遍历集合
    11. Set<Object> keySet = prop.keySet();
    12. for (Object key : keySet) {
    13. Object value = prop.get(key);
    14. System.out.println(key + "," + value);
    15. }
    16. }
    17. }
  • properties集合特有的方法 | 方法名 | 功能说明 | | —- | —- | | Object setProperty(String key, String value) | 设置集合的键和值,都是String类型,底层调用 Hashtable方法 put | | String getProperty(String key) | 使用此属性列表中指定的键搜索属性 | | Set stringPropertyNames() | 从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串 |

  • 示例代码 ```java public class PropertiesDemo02 { public static void main(String[] args) {

    1. //创建集合对象
    2. Properties prop = new Properties();
    3. //Object setProperty(String key, String value):设置集合的键和值,都是String类型,底层调用Hashtable方法put
    4. prop.setProperty("itheima001", "林青霞");
    5. /*
    6. Object setProperty(String key, String value) {
    7. return put(key, value);
    8. }
    9. Object put(Object key, Object value) {
    10. return map.put(key, value);
    11. }
    12. */
    13. prop.setProperty("itheima002", "张曼玉");
    14. prop.setProperty("itheima003", "王祖贤");
    15. //String getProperty(String key):使用此属性列表中指定的键搜索属性

    // System.out.println(prop.getProperty(“itheima001”)); // System.out.println(prop.getProperty(“itheima0011”));

// System.out.println(prop);

  1. //Set<String> stringPropertyNames():从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串
  2. Set<String> names = prop.stringPropertyNames();
  3. for (String key : names) {

// System.out.println(key); String value = prop.getProperty(key); System.out.println(key + “,” + value); } } }

  1. <a name="zj37N"></a>
  2. ### (14)properties集合与io流相结合的使用
  3. - 和IO流结合的方法
  4. | 方法名 | 功能说明 |
  5. | --- | --- |
  6. | void load(InputStream inStream) | 从输入字节流读取属性列表(键和元素对) |
  7. | void load(Reader reader) | 从输入字符流读取属性列表(键和元素对) |
  8. | void store(OutputStream out, String comments) | 将此属性列表(键和元素对)写入此 Properties表中,以适合于使用 load(InputStream)方法的格式写入输出字节流 |
  9. | void store(Writer writer, String comments) | 将此属性列表(键和元素对)写入此 Properties表中,以适合使用 load(Reader)方法的格式写入输出字符流 |
  10. - 示例代码
  11. ```java
  12. public class PropertiesDemo03 {
  13. public static void main(String[] args) throws IOException {
  14. //把集合中的数据保存到文件
  15. // myStore();
  16. //把文件中的数据加载到集合
  17. myLoad();
  18. }
  19. private static void myLoad() throws IOException {
  20. Properties prop = new Properties();
  21. //void load(Reader reader):
  22. FileReader fr = new FileReader("myOtherStream\\fw.txt");
  23. prop.load(fr);
  24. fr.close();
  25. System.out.println(prop);
  26. }
  27. private static void myStore() throws IOException {
  28. Properties prop = new Properties();
  29. prop.setProperty("itheima001","林青霞");
  30. prop.setProperty("itheima002","张曼玉");
  31. prop.setProperty("itheima003","王祖贤");
  32. //void store(Writer writer, String comments):
  33. FileWriter fw = new FileWriter("myOtherStream\\fw.txt");
  34. prop.store(fw,null);
  35. fw.close();
  36. }
  37. }

(15)properties集合案例之游戏次数

  • 案例需求
    • 实现猜数字小游戏只能试玩3次,如果还想玩,提示:游戏试玩已结束,想玩请充值
  • 分析步骤
    • 写一个游戏类,里面有一个猜数字的小游戏
    • 写一个测试类,测试类中有main()方法,main()方法中写如下代码:
      • 从文件中读取数据到Properties集合,用load()方法实现
      • 文件已经存在:game.txt
      • 里面有一个数据值:count=0
      • 通过Properties集合获取到玩游戏的次数
      • 判断次数是否到到3次了
        • 如果到了,给出提示:游戏试玩已结束,想玩请充值
        • 如果不到3次:次数+1,重新写回文件,用Properties的store()方法实现玩游戏
  • 代码实现

    1. public class PropertiesTest {
    2. public static void main(String[] args) throws IOException {
    3. //从文件中读取数据到Properties集合,用load()方法实现
    4. Properties prop = new Properties();
    5. FileReader fr = new FileReader("myOtherStream\\game.txt");
    6. prop.load(fr);
    7. fr.close();
    8. //通过Properties集合获取到玩游戏的次数
    9. String count = prop.getProperty("count");
    10. int number = Integer.parseInt(count);
    11. //判断次数是否到到3次了
    12. if(number >= 3) {
    13. //如果到了,给出提示:游戏试玩已结束,想玩请充值(www.itcast.cn)
    14. System.out.println("游戏试玩已结束,想玩请充值(www.itcast.cn)");
    15. } else {
    16. //玩游戏
    17. GuessNumber.start();
    18. //次数+1,重新写回文件,用Properties的store()方法实现
    19. number++;
    20. prop.setProperty("count",String.valueOf(number));
    21. FileWriter fw = new FileWriter("myOtherStream\\game.txt");
    22. prop.store(fw,null);
    23. fw.close();
    24. }
    25. }
    26. }

(16)Stream流的认识

  • 作用:
    • 简化集合的使用代码
  • Stream流的获取方法
    • 单列集合:可以使用Collection接口中的默认方法stream();生成流:Default Streamstream();
    • 双列集合:间接的生成流:可以通过keySet或者entrySet获取一个Set集合,再获取stream流
    • 数组:通过Arrays类中的静态方法stream生成流。
    • 同种数据类型的多个数据:
      • 1,2,3,4,5……
      • “aaa”, “bbb” , “ccc”……
      • 使用Stream.of(T…values);生成流。
  • Stream流的常见中间操作方法

    • Streamlimit(long maxSize): 截取指定参数个数的数据(参数为几,就保前

    几个数据)。

    • Streamskip(long n); 跳过指定参数个数的数据(参数为几,就除去前几个

    数据,保留后面的数据。

    • static Streamconcat(Stream a, Stream b); 合并a和b两个流为一个

    • Streamdistinct(); 去除流中的重复的元素 。依赖(hashCode和equals方

    法。

  • 常见的结尾操作方法

    • void forEach(Consumer consumer); 参数为接口且接口中只有一个抽象方

    法,因此可以使用lambda表达式(该方法底层是对stream流里的数据进行了
    循环遍历,并循环调用Consumer接口中的抽象方法,把每个数据传入到Consumer接口中的抽象方法中,因此我们只要重写抽象方法用输出语句打印参数就可以。)

    • long count(); 返回此流中的元素数Stream流的结尾方法.png
    • 收集方法Stream流的收集方法.pngTOMAP.png