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