集合也叫做容器,顾名思义就是用来装一些东西的容器。

Collection集合

image.png
Interface Collection<E> E-此集合中的元素类型
在Java中所有的容器都属于Collection接口的子类,就像异常都属于Throwable的子类一样。

Java标准库自带的java.util包提供了集合接口:Collection,它是除Map外所有其他集合类的根接口。Java的java.util包主要提供了以下三种类型的集合:

  • List:一种有序列表的集合,例如,按索引排列的StudentList
  • Set:一种保证没有重复元素的集合,例如,所有无重复名称的StudentSet
  • Map:一种通过键值(key-value)查找的映射表集合,例如,根据Studentname查找对应StudentMap

Java集合的设计有几个特点:一是实现了接口和实现类相分离,例如,有序表的接口是List,具体的实现类有ArrayListLinkedList等。二是支持泛型,我们可以限制在一个集合中只能放入同一种数据类型的元素,例如:

  1. List<String> list = new ArrayList<>(); // 只能放入String类型

最后,Java遍历集合是通过统一的方式——迭代器(Iterator)来实现,它最明显的好处在于无需知道集合内部元素是按什么方式存储的。
由于Java的集合设计非常久远,中间经历过大规模改进;

Collection集合常用方法

image.png
Collection不能直接实例化,要通过其子类实例,
image.png
案例:创建学生类并添加到Collection集合中
image.png

set 集合

Interface Set<E>,Set是一个接口不能直接使用,我们一般使用HashSetTreeSet
Set集合特点:

  • 不包含重复元素;
  • 没有索引,不能通过for循环遍历,可以用foreach或Iterator遍历;


哈希值:**

是JDK根据对象的地址或字符串或数字根据哈希算法算出来的int类型数值

image.png
image.png
image.png
哈希表
https://www.bilibili.com/video/BV1si4y1b7Rs?from=search&seid=2727536263524650394

HashSet集合

特点:

  • 底层数据结构是哈希表;
  • 不保证集合的迭代顺序;
  • 此类实现Set接口,不包含重复元素;
  • 没有索引,不能通过for循环遍历;

瞎摆->存储的速度快;
继承关系:

  1. java.lang.Object
  2. java.util.AbstractCollection<E>
  3. java.util.AbstractSet<E>
  4. java.util.HashSet<E>

image.png

LinkedHashSet

特点:

  • Hash表和Set接口的链表实现,具有可预测的迭代顺序;
  • 此实现与HashSet不同之处在于它维护了一个贯穿其所有条目的双向链表, 此链接列表定义迭代排序,即元素插入集合( 插入顺序 ) 的顺序,保证元素有序的;

继承关系:

  1. Class LinkedHashSet<E>
  2. java.lang.Object
  3. java.util.AbstractCollection<E>
  4. java.util.AbstractSet<E>
  5. java.util.HashSet<E>
  6. java.util.LinkedHashSet<E>

image.png

TreeSet

按顺序摆->存储的时候帮你排序, 存储的速度就慢;
继承关系:

  1. java.lang.Object
  2. java.util.AbstractCollection<E>
  3. java.util.AbstractSet<E>
  4. java.util.TreeSet<E>
  5. public class TreeSet<E>
  6. extends AbstractSet<E>
  7. implements NavigableSet<E>, Cloneable, Serializable
  8. //一个NavigableSet实现基于一个TreeMap
  9. //元件使用其有序natural ordering ,或由Comparator集合创建时提供,这取决于所使用的构造方法。

image.png
image.png
案例:对存储在TreeSet集合中的学生对象按照年龄排序,若年龄相同,则按名字字母排序

  • Student类要实现 Comparable接口,重写compareTo方法

    此接口对实现它的每个类的对象强加一个总排序。 这种排序被称为类的自然顺序 ,类的compareTo方法被称为其自然比较方法

SetDemo.java

  1. TreeSet<Student> ts = new TreeSet<>();
  2. Student s1 = new Student("diaocan", 26);
  3. Student s2 = new Student("xisi", 33);
  4. Student s3 = new Student("wangzhaojun", 28);
  5. Student s4 = new Student("yangguifei", 35);
  6. Student s5 = new Student("zhangmanyu", 35);
  7. Student s6 = new Student("lingqqingxia", 35);
  8. Student s7 = new Student("lingqqingxia", 35);
  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. ts.add(s7);
  16. for (Student s:ts
  17. ) {
  18. System.out.println("name="+s.getName()+",age="+s.getAge());
  19. }

Student.java

  1. public class Student implements Comparable<Student> {
  2. private String name;
  3. private int age;
  4. public Student() { }
  5. public Student(String name, int age) {
  6. this.name = name;
  7. this.age = age;
  8. }
  9. public String getName() {
  10. return name;
  11. }
  12. public void setName(String name) {
  13. this.name = name;
  14. }
  15. public int getAge() {
  16. return age;
  17. }
  18. public void setAge(int age) {
  19. this.age = age;
  20. }
  21. @Override
  22. public int compareTo(Student s) {
  23. // return 0; //相同,不添加
  24. // return 1; //后一个比前一个大,正序
  25. // return -1; //后一个比前一个小,倒序
  26. int num = this.age - s.age; //this 后一项,s 当前项
  27. //如果age相同则比较name 否则直接返回num
  28. return num==0?this.name.compareTo(s.name):num; //String重写了compareTo方法
  29. }
  30. }

打印:

name=diaocan,age=26 name=wangzhaojun,age=28 name=xisi,age=33 name=lingqqingxia,age=35 name=yangguifei,age=35 name=zhangmanyu,age=35

image.png

总结:

  • 自然排序:要在要比较的对象类中实现_Comparable_并重写compareTo方法;
  • 比较器排序:在TreeSet构造方法中定义Comparator实例对象;

案例:

  1. TreeSet<StudentScore> ts = new TreeSet<>(new Comparator<StudentScore>() {
  2. @Override
  3. public int compare(StudentScore o1, StudentScore o2) {
  4. Double ret = o1.scoreSum() - o2.scoreSum();
  5. return ret>0?-1:1;
  6. }
  7. });
  8. StudentScore s1 = new StudentScore("zhangsan", 76.5,92.6);
  9. StudentScore s2 = new StudentScore("lisi", 79.5,88.9);
  10. StudentScore s3 = new StudentScore("wangwu", 91.6,78.6);
  11. ts.add(s1);
  12. ts.add(s2);
  13. ts.add(s3);
  14. for (StudentScore ss:ts
  15. ) {
  16. System.out.println(ss.getName()+":"+ss.scoreSum());
  17. }
  18. //打印结果
  19. /*
  20. wangwu:170.2
  21. zhangsan:169.1
  22. lisi:168.4
  23. */

List 列表

两种数据结构:
数组有索引,查询非常快,但是增加删除节点 该位置后面的节点都要移动,效率非常低;
链表是一种增删快的模型(对比数组)
链表是一种查询慢的模型(对比数组),查询每一个元素都要从头开始查

Interface List<E> java.util包
public interface ``List<E> ``extendsCollection<E>
List接口特点:

  • 有序集合,该接口的用户可以精确控制列表中每个元素的插入位置,用户可以通过整数索引(列表中的位置)访问元素,并搜索列表中的元素;
  • 与Set集合不同,列表通常允许重复元素;

    List实例方法

  • add(obj) 添加元素

  • get(i) 查看第i个元素
  • size() 查看列表中的数据个数
  • isEmpty() 判断是否是空列表
  • indexOf(xxx) 查看xxx元素在列表中的位置
  • lastIndexOf(xxx) 查看xxx元素在列表中最后一次出现的位置
  • contains(xxx) 判断列表中是否包含了xxx
  • subList(start. end) 从列表中start开始截取, 截取到end 但不包括end
  • toArray() 转化成数组
  • remove(obj) 删除某个元素
  • remove(i) 删除某个位置的元素
  • clear() 清空列表
    1. List lst = new ArrayList();
    2. lst.add("美团外卖");
    3. lst.add("饿了么");
    4. lst.add("百度外卖");
    5. lst.add("我不吃");
    6. lst.add("外卖");
    7. System.out.println(lst); // [美团外卖, 饿了么, 百度外卖, 我不吃, 外卖]
    8. System.out.println(lst.get(0)); // 查询第0个元素
    9. System.out.println(lst.get(2)); // 查询第2个元素
    10. System.out.println(lst.size()); // 列表中元素的个数
    11. // 遍历出列表中所有的数据
    12. for(int i = 0 ; i < lst.size(); i++){
    13. System.out.println(lst.get(i));
    14. }
    15. // 判断列表是否是空列表
    16. System.out.println(lst.isEmpty()); // false
    17. //查看"我不吃"元素在列表中的位置
    18. System.out.println(lst.indexOf("我不吃")); // 3
    19. //查看最后一个"百度外卖"在列表中的位置
    20. System.out.println(lst.lastIndexOf("百度外卖")); // 2
    21. //判断是否包含饿了么
    22. System.out.println(lst.contains("饿了么")); // true
    23. //列表的截取, 从1截取到3 [1,3)区间
    24. System.out.println(lst.subList(1, 3)); // [饿了么, 百度外卖]
    25. // 转化成数组
    26. System.out.println(lst.toArray());
    27. lst.remove("外卖"); // 删除"外卖"
    28. lst.remove(2); // 删除索引为2的元素

    ArrayList

    List接口的可调整大小的阵列实现,底层数据结构是数组,查询快,增删慢

    根据异常信息分析源码

    描述:

    一个线程正在迭代List 而另一个线程去修改List(Collection 多态),会抛出ConcurrentModificationException异常

image.png

  1. //异常
  2. Exception in thread "main" java.util.ConcurrentModificationException
  3. at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1013)
  4. at java.base/java.util.ArrayList$Itr.next(ArrayList.java:967)
  5. at com.xjt.myList.ListDemo.main(ListDemo.java:18)

分析源码:
异常信息要从下往上读
1、at com.xjt.myList.ListDemo.main(ListDemo.java:18),在myList.ListDemo.main函数第18行
2、at java.base/java.util.ArrayList$Itr.next(ArrayList.java:967),ArrayList.java中第967行
image.png

执行itr.next()方法时,会先去执行 checkForComodification``() 方法

3、at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1013) 在ArrayList.java文件的1013行
image.png
判断 modCount != expectedModCount ,如果不相等 抛出 ConcurrentModificationException异常
image.png
当执行arrayList.add("python")modCount+1,下次循环进来的时候expectedModCount 和modCount就不相等了。

那么 modCount 和 expectedModCount又是什么东东呢?
我们可以发现在 类Itr 里面定义了:
image.png
ArrayList.java中 Ctrl+F搜索发现modCount没有定义,是直接调用的,所有我们去它父类中找
image.png
image.png
问题找到了,那么我们怎么在遍历List时改变它的内容呢?

  1. //for循环遍历
  2. for (int i = 0; i < arrayList.size(); i++) {
  3. if(arrayList.get(i).equals("java")){
  4. arrayList.add("python");
  5. }
  6. }

注意:增强for(foreach)内部其实就是Iterator
image.png
通过ArrayList实例的ListIterator实现遍历
ListIterator:列表的迭代器,允许程序员在任一方向上遍历列表,在迭代期间修改列表,并获取迭代器在列表中的当前位置。

  1. ListIterator<String> lit = arrayList.listIterator();
  2. while (lit.hasNext()) {
  3. String s = lit.next();
  4. if (s.equals("java")) {
  5. arrayList.add("python");
  6. }
  7. }

这么多种遍历List方式我们怎么选择呢?

  • 只是取List每一项值,不需要修改时,使用foreach最方便
  • 需要获取索引值或需要修改List项的值时,选择for循环
  • ListIterator可以往前遍历往后遍历或从某一项索引开始遍历,且能修改List,使用最灵活
  • Iterator遍历List可以删除/获取List每一项

    LinkedList

    双链表实现了ListDeque接口,底层数据结构是链表,查询慢,增删快
    1. public class LinkedList<E> extends AbstractSequentialList<E>
    2. implements List<E>, Deque<E>, Cloneable, Serializable
    image.png

ArrayList和 LinkedList的区别对比:

功能 ArrayList LinkedList
获取指定元素 速度很快 需要从头开始查找元素,慢
添加元素到末尾 速度很快 速度很快
在指定位置添加/删除 该位置后面的所有元素都要移动 不需要移动元素
内存占用 较大

ArrayList的优势是查询;
LinkedList的优势是删除和添加;

List.of()创建列表

除了使用ArrayListLinkedList,我们还可以通过List接口提供的of()方法,根据给定元素快速创建List

  1. List<Integer> list = List.of(1, 2, 5);

但是List.of()方法不接受null值,如果传入null,会抛出NullPointerException异常。

Map 集合

image.png
image.png
image.png

Map实例的基本方法

  • put(key,value) 向映射中添加一个键值对,相同的key会把前面保存的数据顶掉;
  • get(key) 使用key查询value;
  • isEmpty() 判断Map是否是空的;
  • size() 返回key, value键值对的个数;
  • containsKey(key) 判断是否存在key 返回布尔值;
  • containsValue(Object value) 判断映射中是否包含值==value 返回布尔值
  • remove(key) 根据key删除信息;
  • keySet() 获取到map中key的集合set;
  • map.values() 获取到map中所有value值;
  • map1.equals(map2) 判断两个Set集合的元素是否相同;

用法示例:

  1. //map实例化
  2. Map<String,String> map = new HashMap<>();
  3. ////基本方法
  4. //添加元素
  5. map.put("name","xiong");
  6. map.put("gender","男");
  7. Map<String,String> map2 = new HashMap<>();
  8. map2.put("2", "B");
  9. map2.put("3", "C");
  10. map.putAll(map2); //把一个map集合合并到另一个map集合里
  11. System.out.println(map);
  12. //修改元素
  13. map.remove("2","2B");
  14. //删除元素
  15. map.remove("3");
  16. //查询元素
  17. System.out.println("name="+map.get("name"));
  18. //获取集合大小
  19. System.out.println(map.size());
  20. //获取集合所有值/所有键
  21. System.out.println(map.keySet()); //所有键
  22. System.out.println(map.values()); //所有值
  23. //清空集合
  24. //map.clear();
  25. //获得map中一组组键值对的集合
  26. System.out.println(map.entrySet()); //[2=B, gender=男, name=xiong]
  27. //原始方式遍历
  28. for (Map.Entry<String,String> entry:map.entrySet()){
  29. System.out.println("key:"+entry.getKey()+";value:"+entry.getValue());
  30. }
  31. //map的forEach方式(java8新特性)
  32. map.forEach((k,v) ->
  33. System.out.println("key:"+k+";value:"+v)
  34. );
  35. //迭代器方式遍历
  36. System.out.println(map); //{2=B, gender=男, name=xiong}
  37. System.out.println(map.entrySet()); //[2=B, gender=男, name=xiong]
  38. Iterator it = map.entrySet().iterator(); // 拿到map内部的entry,逗号分隔的每一项都是一个entry
  39. while(it.hasNext()){
  40. Map.Entry entry = (Map.Entry) it.next();
  41. System.out.println(entry.getKey()+"=>"+entry.getValue());
  42. }

image.png
image.png
示例:
image.png
示例2:
image.png
示例3:
image.png
示例4:
image.png

  1. ArrayList<HashMap<String, String>> arrayList = new ArrayList<>();
  2. HashMap<String, String> hm1 = new HashMap<>();
  3. hm1.put("令狐冲","任盈盈");
  4. hm1.put("岳不群","宁中则");
  5. hm1.put("林平之","岳灵珊");
  6. arrayList.add(hm1);
  7. HashMap<String, String> hm2 = new HashMap<>();
  8. hm2.put("杨过","小龙女");
  9. hm2.put("郭靖","黄蓉");
  10. arrayList.add(hm2);
  11. HashMap<String, String> hm3 = new HashMap<>();
  12. hm3.put("张无忌","赵敏");
  13. hm3.put("宋青书","周芷若");
  14. hm3.put("张无忌","小昭");
  15. arrayList.add(hm3);
  16. for (HashMap<String, String> hm:arrayList) {
  17. //Set<String> keys = hm.keySet();
  18. //for (String key:keys) {
  19. //System.out.println(key+"="+hm.get(key));
  20. //}
  21. //HashSet集合的第二种遍历方法
  22. Set<Map.Entry<String, String>> entries = hm.entrySet();
  23. for (Map.Entry<String, String> entry:entries
  24. ) {
  25. String key = entry.getKey();
  26. String val = entry.getValue();
  27. System.out.println(key+"="+val);
  28. }
  29. }

示例5:
image.png

  1. HashMap<String, ArrayList<String>> stringArrayListHashMap = new HashMap<>();
  2. ArrayList<String> arr1 = new ArrayList<>();
  3. arr1.add("张无忌");
  4. arr1.add("赵敏");
  5. arr1.add("小昭");
  6. arr1.add("周芷若");
  7. stringArrayListHashMap.put("倚天屠龙记",arr1);
  8. ArrayList<String> arr2 = new ArrayList<>();
  9. arr2.add("杨过");
  10. arr2.add("小龙女");
  11. arr2.add("李莫愁");
  12. arr2.add("金轮法王");
  13. stringArrayListHashMap.put("神雕侠侣",arr2);
  14. ArrayList<String> arr3 = new ArrayList<>();
  15. arr3.add("郭靖");
  16. arr3.add("黄蓉");
  17. arr3.add("杨康");
  18. arr3.add("穆念慈");
  19. arr3.add("欧阳克");
  20. stringArrayListHashMap.put("射雕英雄传",arr3);
  21. Set<Map.Entry<String, ArrayList<String>>> entries = stringArrayListHashMap.entrySet();
  22. for (Map.Entry<String, ArrayList<String>> entry:entries) {
  23. String key = entry.getKey();
  24. ArrayList<String> values = entry.getValue();
  25. System.out.println(key+":");
  26. for (String value:values
  27. ) {
  28. System.out.println("\t"+value);
  29. }
  30. }

示例6:
image.png

  1. HashMap<Character, Integer> hm = new HashMap<>();
  2. System.out.println("请输入一串字符:");
  3. Scanner sc = new Scanner(System.in);
  4. String line = sc.nextLine();
  5. for (int i = 0; i < line.length(); i++) {
  6. char key = line.charAt(i);
  7. Integer value = hm.get(key);
  8. if(value == null){
  9. hm.put(key,1);
  10. }else{
  11. value++;
  12. hm.put(key,value);
  13. }
  14. }
  15. StringBuilder sb = new StringBuilder();
  16. Set<Map.Entry<Character, Integer>> entries = hm.entrySet();
  17. for (Map.Entry<Character, Integer> entry:entries) {
  18. Character key = entry.getKey();
  19. Integer value = entry.getValue();
  20. sb.append(key).append("(").append(value).append(")");
  21. }
  22. System.out.println(sb.toString());

Collections工具类

image.png
Collections.sort()
Collections.reverse()
Collections.shuffle()
这些都是静态方法,通过类Collections直接调用
示例:
image.png
示例:用Collection.sort()方法对学生年龄排序,若年龄相同按学生姓名排序
image.png
案例:模拟斗地主
image.png

  1. public static void main(String[] args) {
  2. //1.创建一副牌
  3. //♦ 3 4 5 6 7 8 9 10 J Q K A 2
  4. //♣ 3 4 5 6 7 8 9 10 J Q K A 2
  5. //♥ 3 4 5 6 7 8 9 10 J Q K A 2
  6. //♠ 3 4 5 6 7 8 9 10 J Q K A 2
  7. List<String> pokerList = new ArrayList<>();
  8. //1.1创建花色和数字列表
  9. String[] colors = {"♦","♣","♥","♠"};
  10. String[] nums = {"3","4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"};
  11. for (String color:colors
  12. ) {
  13. for (String num:nums
  14. ) {
  15. pokerList.add(color+num);
  16. }
  17. }
  18. pokerList.add("大王");
  19. pokerList.add("小王");
  20. //1.2洗牌
  21. Collections.shuffle(pokerList);
  22. System.out.println(pokerList);
  23. //2.创建3个玩家列表和一个底牌列表
  24. ArrayList<String> playerAList = new ArrayList<>();
  25. ArrayList<String> playerBList = new ArrayList<>();
  26. ArrayList<String> playerCList = new ArrayList<>();
  27. ArrayList<String> dipaiList = new ArrayList<>();
  28. //3.发牌
  29. for (int i = 0; i < pokerList.size(); i++) {
  30. if(i>=pokerList.size()-3){
  31. dipaiList.add(pokerList.get(i));
  32. }else if(i%3 == 0){
  33. playerAList.add(pokerList.get(i));
  34. }else if(i%3 == 1){
  35. playerBList.add(pokerList.get(i));
  36. }else if(i%3 == 2){
  37. playerCList.add(pokerList.get(i));
  38. }
  39. }
  40. //4.三位玩家看牌,并亮出底牌
  41. showPocker("张三丰",playerAList);
  42. showPocker("张无忌",playerBList);
  43. showPocker("张翠山",playerCList);
  44. showPocker("底牌",dipaiList);
  45. }
  46. public static void showPocker(String name, ArrayList<String> array){
  47. System.out.println("玩家-"+name+"手里的牌是:");
  48. for (String pocker:array
  49. ) {
  50. System.out.print(pocker+" "); //System.out.println()这个是在输出内容之后换行,而 System.out.print()输出内容之后不换行
  51. }
  52. System.out.println(); //空行
  53. }

打印:
image.png
image.png
image.png

  1. public static void main(String[] args) {
  2. //1.创建一副牌
  3. //♦ 3 4 5 6 7 8 9 10 J Q K A 2
  4. //♣ 3 4 5 6 7 8 9 10 J Q K A 2
  5. //♥ 3 4 5 6 7 8 9 10 J Q K A 2
  6. //♠ 3 4 5 6 7 8 9 10 J Q K A 2
  7. //1.1HashMap键值0 1 2 3 ...52 值对应的牌♦3 ♦4...
  8. HashMap<Integer, String> pockerHM = new HashMap<>();
  9. ArrayList<Integer> array = new ArrayList<>(); //存储牌的键值
  10. Integer index = 0;
  11. //1.1创建花色和数字列表
  12. String[] colors = {"♦","♣","♥","♠"};
  13. String[] nums = {"3","4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"};
  14. for (String color:colors
  15. ) {
  16. for (String num:nums
  17. ) {
  18. array.add(index);
  19. pockerHM.put(index,color+num);
  20. index++;
  21. }
  22. }
  23. array.add(index);
  24. pockerHM.put(index,"大王");
  25. index++;
  26. array.add(index);
  27. pockerHM.put(index,"小王");
  28. //1.2洗牌
  29. Collections.shuffle(array);
  30. // System.out.println(array);
  31. // System.out.println(pockerHM);
  32. //2.创建3个玩家列表和一个底牌列表(存储的HashMap键值)
  33. TreeSet<Integer> playerATreeSet = new TreeSet<>();
  34. TreeSet<Integer> playerBTreeSet = new TreeSet<>();
  35. TreeSet<Integer> playerCTreeSet = new TreeSet<>();
  36. TreeSet<Integer> dipaiTreeSet = new TreeSet<>();
  37. //3.发牌
  38. for (int i = 0; i < pockerHM.size(); i++) {
  39. Integer idx = array.get(i);
  40. if(i>=pockerHM.size()-3){
  41. dipaiTreeSet.add(idx);
  42. }else if(i%3 == 0){
  43. playerATreeSet.add(idx);
  44. }else if(i%3 == 1){
  45. playerBTreeSet.add(idx);
  46. }else if(i%3 == 2){
  47. playerCTreeSet.add(idx);
  48. }
  49. }
  50. //4.三位玩家看牌,并亮出底牌
  51. showPocker("张三丰",playerATreeSet,pockerHM);
  52. showPocker("张无忌",playerBTreeSet,pockerHM);
  53. showPocker("张翠山",playerCTreeSet,pockerHM);
  54. showPocker("底牌",dipaiTreeSet,pockerHM);
  55. }
  56. public static void showPocker(String name, TreeSet<Integer> array,HashMap<Integer,String> playerHashMap){
  57. System.out.println(name+"手里的牌是:");
  58. for (Integer i:array
  59. ) {
  60. System.out.print(playerHashMap.get(i)+" "); //System.out.println()这个是在输出内容之后换行,而 System.out.print()输出内容之后不换行
  61. }
  62. System.out.println(); //空行
  63. }

打印:
image.png
作业:

  1. 有字符串”k:1,k1:2,k2:3,k3:4” 处理成Map: {‘k’=1,’k1’=2….}

    1. package com.xjt.homework;
    2. import java.util.HashMap;
    3. import java.util.Map;
    4. public class Work1 {
    5. public static void main(String[] args) {
    6. /*1. 有字符串”k:1,k1:2,k2:3,k3:4” 处理成Map: {‘k’=1,’k1’=2….}
    7. */
    8. String str = "k:1,k1:2,k2:3,k3:4";
    9. Map<String,Integer> mymap = new HashMap<String,Integer>();
    10. String[] lst = str.split(",");
    11. for (String item:lst
    12. ) {
    13. String StrKey = item.split(":")[0];
    14. String StrValue = item.split(":")[1];
    15. mymap.put(StrKey,Integer.parseInt(StrValue));
    16. }
    17. System.out.println(mymap);
    18. }
    19. }
  2. 元素分类, 有如下值 int[] li= {11,22,33,44,55,66,77,88,99,90},将所有大于 等于66 的值保存至Map的第一个key中,将小于 66 的值保存至第二个key的值中。
    即: {‘k1’: 大于66的所有值列表, ‘k2’: 小于66的所有值列表}

    1. package com.xjt.homework;
    2. import java.util.*;
    3. public class Work2 {
    4. public static void main(String[] args) {
    5. int[] li= {11,22,33,44,55,66,77,88,99,90};
    6. //定义一个有泛型映射
    7. Map<String, List<Integer>> map = new HashMap<String,List<Integer>>();
    8. //定义两个有泛型的列表
    9. List<Integer> lst1 = new ArrayList<>();
    10. List<Integer> lst2 = new ArrayList<>();
    11. for (Integer item:li
    12. ) {
    13. if(item>=66){
    14. lst1.add(item);
    15. }else{
    16. lst2.add(item);
    17. }
    18. }
    19. map.put("k1",lst1);
    20. map.put("k2",lst2);
    21. System.out.println(map); //{k1=[66, 77, 88, 99, 90], k2=[11, 22, 33, 44, 55]}
    22. }
    23. }

    作业参考答案:
    6、Java集合 - 图43

  3. 给定一组连续的整数,例如:10,11,12,……,20,但其中缺失一个数字,试找出缺失的数字:

    1. package com.xjt.homework;
    2. import java.util.ArrayList;
    3. import java.util.Collections;
    4. import java.util.List;
    5. public class Test1 {
    6. static int findMissingNumber(int start, int end, List<Integer> list) {
    7. int ret=0;
    8. for (int i = start; i <= end; i++) {
    9. if (!list.contains(i)) {
    10. ret = i;
    11. }
    12. }
    13. return ret;
    14. }
    15. public static void main(String[] args) {
    16. // 构造从start到end的序列:
    17. final int start = 10;
    18. final int end = 20;
    19. List<Integer> list = new ArrayList<>();
    20. for (int i = start; i <= end; i++) {
    21. list.add(i);
    22. }
    23. // 洗牌算法shuffle可以随机交换List中的元素位置:
    24. Collections.shuffle(list);
    25. // 随机删除List中的一个元素:
    26. int removed = list.remove((int) (Math.random() * list.size()));
    27. int found = findMissingNumber(start, end, list);
    28. System.out.println(list.toString());
    29. System.out.println("missing number: " + found);
    30. System.out.println(removed == found ? "测试成功" : "测试失败");
    31. }
    32. }

    Iterator 迭代器

    我们可以使用get() size() 搭配for循环来完成遍历,但是想一个问题. set好像没有get()方法. 那set集合怎么遍历啊….这就要用到咱们本节课讲的这个叫Iterator的东西. 这个东西叫迭代器。

    啥是迭代器. 想一个事儿. 我们村以前有那种乡村诊所. 病人来了之后呢就在门口一站. 然后大夫在诊治的时候会喊一嗓子~ 下一个. 下一个人就进来了. 大夫不关心外面多少人, 也不关心外面的病人是谁. 他只关心”下一个”. OK. 咱们今天要说的这个叫迭代器的东西, 和乡村诊所里的大夫是一样一样的~~.

先看用法:

  1. Set set = new HashSet();
  2. set.add("王大锤");
  3. set.add("二麻子");
  4. set.add("三愣子");
  5. set.add("哈利路呀大大大锤");
  6. // 创建迭代器对象, 相当于创建一个医生
  7. Iterator it = set.iterator();
  8. // 吼一嗓子, 进来一个人
  9. String s1 = (String) it.next();
  10. System.out.println(s1);
  11. // 吼一嗓子, 进来一个人
  12. String s2 = (String) it.next();
  13. System.out.println(s2);
  14. // 吼一嗓子, 进来一个人
  15. String s3 = (String) it.next();
  16. System.out.println(s3);
  17. // 吼一嗓子, 进来一个人
  18. String s4 = (String) it.next();
  19. System.out.println(s4);
  20. // 没人了. 还吼, 吼破了喉咙也不会有人理你的. 报错
  21. String s5 = (String) it.next(); //Exception in thread "main" java.util.NoSuchElementException
  22. System.out.println(s5);

next() 会自动帮我们获取到元素,并且指针指向下一个元素。
但是呢,这时候我们发现迭代器, 太容易出错了,我也不知道啥时候还有没有元素了。怎么解决呢? 在next()之前, 我们可以做一个判断, 判断是否还有下一个元素。
set集合使用迭代器遍历

  1. Set set = new HashSet();
  2. set.add("王大锤");
  3. set.add("王小锤");
  4. set.add("王不大不小锤");
  5. set.add("哈利路呀大大大锤");
  6. // 拿到迭代器, 相当于创建一个医生
  7. Iterator it = set.iterator(); //集合迭代器
  8. while(it.hasNext()){ // 判断是否还有下一个元素
  9. String s = (String) it.next();
  10. System.out.println(s);
  11. }

List使用迭代器遍历

  1. package com.xyq.collection;
  2. import java.util.*;
  3. public class TestIter {
  4. public static void main(String[] args) {
  5. List list = new ArrayList();
  6. list.add("武大郎");
  7. list.add("武二郎");
  8. list.add("武三郎");
  9. Iterator it = list.iterator(); //列表迭代器
  10. while(it.hasNext()){
  11. System.out.println(it.next());
  12. }
  13. }
  14. }

Map使用迭代器进行遍历

  1. Map map = new HashMap();
  2. map.put("浪里白条", "张顺");
  3. map.put("及时雨", "松江");
  4. map.put("行者", "悟空");
  5. Iterator it = map.keySet().iterator(); //map映射迭代器,要对map映射的所有键进行迭代
  6. while(it.hasNext()){
  7. Object key = it.next();
  8. Object value = map.get(key); // 通过key获取到value
  9. System.out.println(key+"=>"+value);
  10. }

Map集合还可以这样遍历.

  1. Map map = new HashMap();
  2. map.put("浪里白条", "张顺");
  3. map.put("及时雨", "松江");
  4. map.put("行者", "悟空");
  5. Iterator it = map.entrySet().iterator(); // 拿到map内部的entry,逗号分隔的每一项都是一个entry
  6. while(it.hasNext()){
  7. Map.Entry entry = (Map.Entry) it.next();
  8. System.out.println(entry.getKey()+"=>"+entry.getValue());
  9. }

迭代器存在的意义:
一是可以让不同的数据类型拥有相同的遍历方式,List, Set, Map,完全不同的三种数据类型,但是都可以使用Iterator进行遍历;
二是:使用迭代器遍历比for循环遍历效率要高;

1.for each == iterator

有童鞋可能觉得使用Iterator访问List的代码比使用索引更复杂。但是,要记住,通过Iterator遍历List永远是最高效的方式。并且,由于Iterator遍历是如此常用,所以,Java的for each循环本身就可以帮我们使用Iterator遍历。把上面的代码再改写如下:

  1. List<String> list = List.of("apple", "pear", "banana");
  2. for (String s : list) {
  3. System.out.println(s);
  4. }

上述代码就是我们编写遍历List的常见代码。
实际上,只要实现了Iterable接口的集合类都可以直接用for each循环来遍历,Java编译器本身并不知道如何遍历集合对象,但它会自动把for each循环变成Iterator的调用,原因就在于Iterable接口定义了一个Iterator iterator()方法,强迫集合类必须返回一个Iterator实例。

2.List和Array转换

List变为Array有三种方法,第一种是调用toArray()方法直接返回一个Object[]数组:

  1. List<String> list = List.of("apple", "pear", "banana");
  2. Object[] array = list.toArray();
  3. for (Object s : array) {
  4. System.out.println(s);
  5. }

这种方法会丢失类型信息,所以实际应用很少。
第二种方式是给toArray(T[])传入一个类型相同的ArrayList内部自动把元素复制到传入的Array中:

  1. List<Integer> list = List.of(12, 34, 56);
  2. Integer[] array = list.toArray(new Integer[3]);
  3. for (Integer n : array) {
  4. System.out.println(n);
  5. }

注意到这个toArray(T[])方法的泛型参数并不是List接口定义的泛型参数,所以,我们实际上可以传入其他类型的数组,例如我们传入Number类型的数组,返回的仍然是Number类型:

  1. List<Integer> list = List.of(12, 34, 56);
  2. Number[] array = list.toArray(new Number[3]);
  3. for (Number n : array) {
  4. System.out.println(n);
  5. }

但是,如果我们传入类型不匹配的数组,例如,String[]类型的数组,由于List的元素是Integer,所以无法放入String数组,这个方法会抛出ArrayStoreException
如果我们传入的数组大小和List实际的元素个数不一致怎么办?根据List接口)的文档,我们可以知道:
如果传入的数组不够大,那么List内部会创建一个新的刚好够大的数组,填充后返回;如果传入的数组比List元素还要多,那么填充完元素后,剩下的数组元素一律填充null
实际上,最常用的是传入一个“恰好”大小的数组:

  1. Integer[] array = list.toArray(new Integer[list.size()]);

最后一种更简洁的写法是通过List接口定义的T[] toArray(IntFunction generator)方法:

  1. Integer[] array = list.toArray(Integer[]::new);

这种函数式写法我们会在后续讲到。
反过来,把Array变为List就简单多了,通过List.of(T...)方法最简单:

  1. Integer[] array = { 1, 2, 3 };
  2. List<Integer> list = List.of(array);

对于JDK 11之前的版本,可以使用Arrays.asList(T...)方法把数组转换成List
要注意的是,返回的List不一定就是ArrayList或者LinkedList,因为List只是一个接口,如果我们调用List.of(),它返回的是一个只读List

  1. List<Integer> list = List.of(12, 34, 56); //返回一个只读的List
  2. list.add(999); // UnsupportedOperationException

对只读List调用add()remove()方法会抛出UnsupportedOperationException

泛型

泛型概述

前面我们说了三种容器. 这三种容器有一个共同的特点, 就是所有存放在容器中的内容最终都被当成了Object来保存。当前, 我们能理解为了通用性把所有的元素向上转型成Object。
但是这样存储是有问题的。比如,酱油瓶里就应该装酱油,醋瓶就应该装醋。混着装不是不能装,是不能用 对吧。所以呢,我们要对容器进行规范,不能乱七八糟的乱装,这就需要用到泛型。通过泛型可以强制规定容器能装什么东西。就好比,给你家里的瓶瓶罐罐贴上标签来说明这个瓶子是用来装什么的。

规范集合存储数据类型,取数据时不需要强转了,在以后的代码中, 如果没有特殊要求,推荐使用泛型

image.png
image.png

  1. List<String> list = new ArrayList<String>(); // 强制规定这个列表只能装字符串,第二个尖括号里面的String可以省略
  2. // list.add(1);// 报错. 只能装字符串
  3. list.add("NB"); // OK
  4. // 好处是, 从list中获取到的数据不需要强转了.
  5. String s = list.get(0) ;// 这个列表只能装字符串, 拿出来也一定是字符串
  6. System.out.println(s);

创建一个只能装歌手的列表(自定义泛型类)

  1. package com.xjt.fanxing;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. public class Test1 {
  5. public static void main(String[] args) {
  6. List<Singer> singerList = new ArrayList<Singer>(); // 只能装歌手类型
  7. Singer singer1 = new Singer("周杰伦",45);
  8. singerList.add(singer1); // 添加歌手
  9. Singer singer2 = new Singer("王菲",50);
  10. singerList.add(singer2); // 添加歌手
  11. Singer ss = singerList.get(0); // 查询歌手
  12. System.out.println(ss);
  13. }
  14. }
  15. class Singer{
  16. String name;
  17. int age;
  18. @Override
  19. public String toString() {
  20. return "Singer{" +
  21. "name='" + name + '\'' +
  22. ", age=" + age +
  23. '}';
  24. }
  25. public Singer(String name, Integer age) {
  26. this.name = name;
  27. this.age = age;
  28. }
  29. }

Set集合和List的泛型是一样的,强制规定集合中元素的类型

  1. Set<String> set = new HashSet<>();
  2. set.add("apple");
  3. set.add("banana");
  4. set.add("pear");
  5. set.add("orange");
  6. for (String s : set) {
  7. System.out.println(s);
  8. }
  9. List<String> list = new ArrayList<>();
  10. list.add("apple"); // size=1
  11. list.add("pear"); // size=2
  12. list.add("apple"); // 允许重复添加元素,size=3
  13. System.out.println(list.size());

Map集合的泛型比较特殊. 因为这个鬼东西里面有key和value.两项内容.

  1. Map<String, String> map = new HashMap<String, String>();
  2. map.put("萝莉", "小蔡");
  3. map.put("御姐", "苏打强");
  4. // map.put(1, 2) ; // 报错, 只能是字符串
  5. String v = map.get("萝莉");
  6. System.out.println(v);

定义泛型类

image.png

泛型方法

  • 在调用该方法时决定传入什么类型参数

image.png

  1. public class GenericDemo1 {
  2. public static void main(String[] args) {
  3. //调用普通方法
  4. Generic g1 = new Generic();
  5. g1.show("hello");
  6. g1.show(111);
  7. g1.show(true);
  8. //泛型类的方法调用
  9. Generic2<String> g21 = new Generic2<String>();
  10. g21.show("hello");
  11. Generic2<Integer> g22 = new Generic2<Integer>();
  12. g22.show(123);
  13. Generic2<Boolean> g23 = new Generic2<Boolean>();
  14. g23.show(true);
  15. //泛型方法调用(终极版)
  16. Generic3 g3 = new Generic3();
  17. g3.show("hello");
  18. g3.show(111);
  19. g3.show(true);
  20. g3.show(12.34);
  21. }
  22. }
  23. class Generic{
  24. //普通方法
  25. public void show(String s){
  26. System.out.println(s);
  27. }
  28. public void show(Integer s){
  29. System.out.println(s);
  30. }
  31. public void show(Boolean s){
  32. System.out.println(s);
  33. }
  34. }
  35. //泛型类改进
  36. class Generic2<T>{
  37. public void show(T t){
  38. System.out.println(t);
  39. }
  40. }
  41. //泛型方法改进
  42. class Generic3{
  43. public <T> void show(T t){
  44. System.out.println(t);
  45. }
  46. }

泛型接口

image.png
image.png
具体可参考Collection接口及其实现类 ArrayList

  1. Interface Collection<E> //接口定义的是泛型
  2. public interface List<E> //List接口继承的仍然是泛型
  3. extends Collection<E>
  4. public class ArrayList<E> //最终实现类
  5. extends AbstractList<E>
  6. implements List<E>, RandomAccess, Cloneable, Serializable
  7. //方法仍然是泛型
  8. void add(int index, E element) //将指定元素插入此列表中的指定位置。
  9. boolean add(E e) //将指定的元素追加到此列表的末尾。

类型通配符

image.png
image.png

可变参数

image.png

image.png

第一个确定的参数必须放置前面,后面不确定个数的参数封装到a集合里了

可变参数的使用

image.png
image.png