UM2L14——集合类

Java中各集合类之间的关系

Java 中的集合类就类似 C# 中的 List​ 和 Dictionary 的存在
主要用于批量存储对象,并且可以动态添加,动态删除

Java 的集合类继承关系如下:

​Collection(接口):

  • ​Set(接口)

    • ​HashSet
    • ​TreeSet
  • ​List(接口)

    • ​ArrayList
    • ​LinkedList

​Map(接口):

  • ​HashMap
  • ​TreeMap

Collection 接口中的方法

  1. ​add(E e) 添加元素
  2. ​remove(Object o) 移除元素
  3. ​clear() 清空元素
  4. ​isEmpty() 是否为空
  5. ​iterator() 获取迭代器,可以用于遍历
  6. ​size() 集合中元素个数
  7. ​contains() 判断元素是否存在
  8. ​toArray() 将容器中元素转为数组

List 接口中的方法

由于 Java 不存在索引器,因此,需要使用 get​ 和 set 来通过索引获取和修改元素

  1. ​get(int index) 获取指定位置元素
  2. ​set(int index, Object obj) 修改集合中指定位置的元素

ArrayList 和 LinkedList 类

Java 中的 ArrayList​ 和 LinkedList​ 类似于 C# 中的 List​ 和 LinkedList
根据自己的实际情况选择使用即可

两者方法上的使用完全一致,因为他们继承相同的接口,区别:

  • ​ArrayList 本质是数组,是顺序存储
  • ​LinkedList 本质是链表,是链式存储

两者性能上的区别:

  • ​LinkedList​ 在插入删除时效率高于 ArrayList
  • ​ArrayList​ 在访问集合中指定位置对象时效率高于 LinkedList

常用方法

  1. 初始化

    两种List​都非常类似C#中的List

    1
    2
    3
    ArrayList<String> list = new ArrayList<>();
    ArrayList<Integer> list2 = new ArrayList<>();
    LinkedList<String> linkedList = new LinkedList<>();
  2. 增 add()

    1
    2
    3
    4
    5
    6
    list.add("1");
    list.add("2");
    list.add("3");
    list2.add(1);
    list2.add(2);
    list2.add(3);
  3. 删 remove()​ clear()

    ​ArrayList​ 和 LinkedList​ 使用remove​来移除某个元素可以通过索引,也可以通过值来移除
    但是,如果我们的容器当中存储的是****​int​​​​​类型 在移除的时候,我们只能通过索引移除而无法通过指定值来移除某个元素

    还有一个Clear方法来清空列表

    1
    2
    3
    4
    list.remove("3");
    list.remove(1);
    list2.remove(1);
    list.clear();
  4. 查 contains()​ get()

    1
    2
    3
    4
    5
    System.out.println(list2.get(0));
    System.out.println(list2.get(1));

    if (list2.contains(3))
    System.out.println("存在2元素");

    输出:

    1
    2
    3
    1
    3
    存在2元素
  5. 改 set()

    1
    2
    list2.set(0, 99);
    System.out.println(list2.get(0));

    输出:

    1
    99
  6. 判断是否为空 isEmpty()

    1
    2
    3
    4
    5
    if (list.isEmpty())
    System.out.println("列表为空1");
    list.clear();
    if (list.isEmpty())
    System.out.println("列表为空2");

    输出:

    1
    列表为空2
  7. 遍历 iterator()

    对ArrayList​ 和 LinkedList的遍历有三种方法:

    1. ​for循环遍历索引
    2. ​for循环遍历元素
    3. 迭代器遍历
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //for循环遍历索引
    for (int i = 0; i < list2.size(); i++) {
    System.out.println(list2.get(i));
    }
    //for循环遍历元素
    for (Integer i : list2) {
    System.out.println(i);
    }
    //迭代器遍历
    Iterator<Integer> it = list2.iterator();
    while (it.hasNext()) {
    System.out.println(it.next());
    }

    输出:

    1
    2
    3
    4
    5
    6
    99
    3
    99
    3
    99
    3
  8. 转数组 toArray()

    如果直接调用toArray()​而不传入参数,返回的是Object[]​(即使使用了泛型去声明列表,返回的还是Object[]​)
    可以提前声明对应类型的数组,将其传入到toArray()内,该数组就会接收列表转数组的结果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    System.out.println("**********");
    Object[] objs = list2.toArray();
    for (Object x : objs) {
    System.out.println(x);
    }

    System.out.println("**********");
    Integer[] ints = new Integer[2];
    list2.toArray(ints);
    for (int x : ints) {
    System.out.println(x);
    }

    输出:

    1
    2
    3
    4
    5
    6
    **********
    99
    3
    **********
    99
    3

HashSet 和 TreeSet 类

Set相关集合的特点是:

  1. 不会存储重复元素,如果我们有去重需求可以选择他们
  2. 只能遍历获取所有元素,不能单独获取中间的某个元素
  3. ​TreeSet会自动排序,可以取出(弹出)首尾
  • 相同点:他们都不允许存储重复的元素,如果传入重复的只会记录一个

    他们相对上节课的 ArrayList​ 和 LinkedList​ 最大的使用区别就是他们可以去除重复的元素
    都不能像 ArrayList​ 和 LinkedList 通过索引获取或修改元素

  • 不同点:他们的底层数据结构不同,HashSet​ 底层结构是哈希表,TreeSet 底层结构是树

    ​TreeSet​ 是有序排列的(会自动将加入元素进行排序),HashSet​ 不一定有序
    ​TreeSet​ 相对 HashSet​ 由于多继承了 SortedSet 接口,所以有更多的方法可以使用

注意:TreeSet​具有排序功能
元素是数字时,按照大小升序排列,元素是字符串时,按照字符的编码值升序排列
元素是自定义类时,可以继承Comparable​接口 重载其中的方法
特别是compareTo方法,它用于制定排序规则

常用方法

​TreeSet​ 和 HashSet​ 都没有改的功能,因为他们没有继承list接口

  1. 初始化

    1
    2
    HashSet<Integer> hashSet = new HashSet<>();
    TreeSet<Integer> treeSet = new TreeSet<>();
  2. 增 add()

    1
    2
    3
    4
    treeSet.add(3);
    treeSet.add(2);
    treeSet.add(1);
    treeSet.add(0);
  3. 删 remove()

    这两个容器都没有通过索引去移除的重载,因此只能通过值移除某个元素

    1
    treeSet.remove(0);
  4. 查 contains()

    1
    2
    3
    4
    if (treeSet.contains(1))
    System.out.println("存在1元素");
    if (treeSet.contains(0))
    System.out.println("存在0元素");

    输出:

    1
    存在1元素
  5. 遍历 iterator()

    只有两种遍历: for循环遍历元素和迭代器

    值得一提的是,TreeSet​ 是有序排列的(会自动将加入元素进行排序),HashSet 不一定有序

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    treeSet.add(3);
    treeSet.add(2);
    treeSet.add(1);treeSet.add(1);treeSet.add(1);treeSet.add(1);

    for (int x : treeSet) {
    System.out.println(x);
    }
    Iterator<Integer> it = treeSet.iterator();
    while (it.hasNext()) {
    System.out.println(it.next());
    }

    输出:

    1
    2
    3
    4
    5
    6
    1
    2
    3
    1
    2
    3

    通过遍历结果可以发现,set集合会自动去重

TreeSet独有的方法

  1. ​first() 返回当前第一个元素

    1
    2
    //treeSet: 1,2,3
    System.out.println(treeSet.first());
  2. ​pollFirst() 取出当前第一个元素

    取出和获取是不一样的,取出是获取该元素的同时让该元素被移除出该set

    1
    2
    3
    4
    5
    6
    //treeSet: 1,2,3
    System.out.println(treeSet.pollFirst());
    System.out.println("**********");
    for (int x : treeSet) {
    System.out.println(x);
    }
  3. ​last() 返回最后一个元素

    1
    2
    //treeSet: 2,3
    System.out.println(treeSet.last());

    输出:

    1
    3
  4. ​pollLast() 取出当前最后一个元素

    1
    2
    3
    4
    5
    6
    //treeSet: 2,3
    System.out.println(treeSet.pollLast());
    System.out.println("**********");
    for (int x : treeSet) {
    System.out.println(x);
    }

    输出:

    1
    2
    3
    3
    **********
    2
  5. ​headSet(E obj)​ 返回一个新Set集合,新集合是 截取 传入的值对应的元素 前面的所有的元素(不包括传入的值)

    1
    2
    3
    4
    5
    6
    //treeSet: 1,2,3,4
    SortedSet<Integer> newSet = treeSet.headSet(3);
    //newSet: 1,2
    for (int x : newSet) {
    System.out.println(x);
    }

    输出:

    1
    2
    1
    2
  6. ​subSet(E b, E e)​ 返回一个新Set​集合,新集合是 截取 传入的值 b​ 和 e 对应的元素之间的所有对象,左包含,右不包含

    1
    2
    3
    4
    5
    //treeSet: 1,2,3,4
    SortedSet<Integer> newSet2 = treeSet.subSet(1, 4);
    for (int x : newSet2) {
    System.out.println(x);
    }

    输出:

    1
    2
    2
    3
  7. ​tailSet(E obj)​ 返回一个新Set集合,新集合是传入对象(包含)之后的所有对象

    1
    2
    3
    4
    5
    //treeSet: 1,2,3,4
    SortedSet<Integer> newSet3 = treeSet.tailSet(2);
    for (int x : newSet3) {
    System.out.println(x);
    }

    输出:

    1
    2
    3
    2
    3
    4

Map接口

Map集合类似C#中的Dictionary​字典,以键值对形式存储数据
我们在使用时,建议主要使用HashMap​,它的效率高于TreeMap

  1. ​put(K key, V value) 添加键值对
  2. ​containsKey(Object key) 判断是否存在键
  3. ​containsValue(Object value) 判断是否存在值
  4. ​get(Object key)​ 如果存在key​,则返回对应值,否则返回null
  5. ​keySet()​ 返回该集合中的所有key​对象形成的Set集合
  6. ​values()​ 返回该集合中所有value​对象形成的Collection集合
  7. ​size() 键值对 对数
  8. ​isEmpty() 是否为空
  9. ​remove(Object key) 根据键移除
  10. ​clear() 清空容器

HashMap和TreeMap之间的区别

  • 相同点:他们都是以键值对形式存储数据,方法使用基本相同

  • 不同点:

    1. ​HashMap​允许有null​键和null​值(但是必须保持键的唯一性)
      ​TreeMap不允许键为空
    2. ​TreeMap​中的映射关系具有一定的顺序,它会帮助我们进行排序
      因此在添加、删除、定位映射关系时,效率较HashMap差
    3. ​HashMap​数据结构基于哈希表,TreeMap数据结构基于树

在实际使用时,建议都使用HashMap​,除非需要排序的Map​时才用TreeMap

HashMap和TreeMap中的常用方法

  1. 初始化

    1
    HashMap<Integer, String> hashMap = new HashMap<>();
  2. 增 put()

    1
    2
    3
    hashMap.put(1, "123");
    hashMap.put(2, "234");
    hashMap.put(3, "345");
  3. 删 remove()

    1
    hashMap.remove(3);
  4. 查 get()​ containsKey()

    需要传入键来获取值,如果不存在这个键,则返回null

    1
    2
    3
    4
    5
    6
    System.out.println(hashMap.get(1));
    System.out.println(hashMap.get(3));
    if (hashMap.containsKey(1))
    System.out.println("存在键1");
    if (hashMap.containsValue("123"))
    System.out.println("存在值\"123\"");

    输出:

    1
    2
    3
    4
    5

    123
    null
    存在键1
    存在值"123"
  5. 改 put()​ containsKey()

    修改HashMap​或TreeMap​的值时,直接使用put​来修改值就可以了
    一般如果是明确要修改某个存在的键对应的值,而非添加键值对,就需要配合containsKey方法来修改

    1
    2
    3
    if (hashMap.containsKey(1))
    hashMap.put(1, "555");
    System.out.println(hashMap.get(1));
  6. 遍历 keySet()​ values()

    遍历可以遍历键,也可以遍历值

    1
    2
    3
    4
    5
    6
    for (Integer i : hashMap.keySet()) {
    System.out.println(hashMap.get(i));
    }
    for (String str : hashMap.values()) {
    System.out.println(str);
    }

    输出:

    1
    2
    3
    4
    555
    234
    555
    234