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