chengaofeng
发布于 2024-09-16 / 9 阅读
0
0

Java常见的集合与应用

什么是集合?

  1. Java集合类是一种特别有用的工具类,可用于存储数量不等的对象

  2. Java集合包含List、Set、Map、Queue四种体系

  3. Java集合类主要由两个接口派生而出:Collection和Map

四种存储结构

  1. List代表有序、可重复集合

  2. Queue代表队列特性

  3. Set代表无序、不可重复集合

  4. Map代表存储映射关系的集合

Collection接口及实现

Map接口及实现

List集合体系及应用

  1. List集合代表一个元素有序、可重复的集合,集合中每个元素都有其对应的顺序索引

  2. List集合允许使用重复元素,通过索引访问指定位置的元素

  3. List集合默认按元素的添加顺序设置元素的索引

List接口与ArrayList实现类

ArrayList的特点

  1. ArrayList基于数组实现的List类,是Java数组的有效替代品

  2. ArrayList会自动对容量进行扩容,多数情况下无须指定最大长度

  3. ArrayList的数据在内存中是连续紧密存储的,基于数据访问速度快

  4. 类似javascript中的Array

package cn.chengaofeng;

import java.util.ArrayList;

import org.junit.Test;

public class ArrayListTest {
    @Test
    public void testCreateArrayList() {
        ArrayList<Number> arrayList = new ArrayList<>();
        arrayList.add(1);
        arrayList.add(2);
        arrayList.add(3);
        assert arrayList.size() == 3;
        assert arrayList.get(0).equals(1);
        assert arrayList.get(1).equals(2);
        assert arrayList.get(2).equals(3);
    }

    @Test
    public void testUpdateArrayList() {
        ArrayList<Number> arrayList = new ArrayList<>();
        arrayList.add(1);
        arrayList.add(2);
        arrayList.add(3);
        // 通过索引添加元素
        arrayList.add(1, 4);
        assert arrayList.size() == 4;
        assert arrayList.get(0).equals(1);
        assert arrayList.get(1).equals(4);
        assert arrayList.get(2).equals(2);
        assert arrayList.get(3).equals(3);
        // 通过set更新元素
        arrayList.set(2, 5);
        assert arrayList.get(2).equals(5);
        // 通过remove删除元素
        arrayList.remove(1);
        assert arrayList.size() == 3;
    }
}

LinkedList特征与方法应用

  1. LinkedList同时实现了List与Deque两个接口

  2. LinkedList在保障有序、允许重复的前提下,也可以作为队列在队首、队尾快速追加数据

  3. LinkedList的数据在内存中是分散存储的,基于链表,拥有良好的数据插入速度,但数据访问速度低于ArrayList

package cn.chengaofeng;

import java.util.LinkedList;

import org.junit.Test;

public class LinkedListTest {
    @Test
    public void testLinkedList() {
        LinkedList<String> linkedList = new LinkedList<>();
        // 跟ArrayList一样,LinkedList也实现了List接口,所以可以使用List接口中的方法
        linkedList.add("a");
        linkedList.add("b");
        linkedList.add("c");
        assert linkedList.size() == 3;
        assert linkedList.get(0).equals("a");
        assert linkedList.get(1).equals("b");
        assert linkedList.get(2).equals("c");
        // LinkedList还实现了Deque(双端队列)接口,所以可以使用Deque接口中的方法
        linkedList.addFirst("d");
        linkedList.addLast("e");
        assert linkedList.size() == 5;
        assert linkedList.getFirst().equals("d");
        assert linkedList.getLast().equals("e");
    }
}

ArrayList与LinkedList存储结构

三种集合的遍历方式

  1. for循环遍历

  2. forEach方法遍历

  3. Iterator迭代器遍历

推荐阅读:如何理解Java的迭代器?

package cn.chengaofeng;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.junit.Test;

public class ListLoopTest {
    @Test
    public void testListLoop() {
        List<String> listLoop = new ArrayList<>();
        listLoop.add("a");
        listLoop.add("b");
        listLoop.add("c");
        for (String s : listLoop) {
            System.out.println(s);
        }

        listLoop.forEach(s -> System.out.println(s));

        Iterator<String> iterator = listLoop.iterator();
        // 使用迭代器遍历集合

        while (iterator.hasNext()) {
            String element = iterator.next();
            System.out.println(element);
        }
    }
}

Set集合体系及应用

  1. Set集合代表一个元素无序、不可重复的集合

  2. Set集合与List集合使用方法基本相同,只是处理行为略有不同

  3. Set集合常用的实现类是:HashSet与TreeSet

package cn.chengaofeng;

import java.util.HashSet;

import org.junit.Test;

public class HashSetTest {
    @Test
    public void testCreateHashSet() {
        // HashSet是Set接口的一个实现类,所以可以使用Set接口中的方法
        HashSet<String> hashSet = new HashSet<>();
        hashSet.add("a");
        hashSet.add("b");
        hashSet.add("c");
        assert hashSet.size() == 3;
        assert hashSet.contains("a");
        assert hashSet.contains("b");
        assert hashSet.contains("c");
        hashSet.add("a");
        assert hashSet.size() == 3;
        // 无序的,无法使用get方法获取指定索引的元素
    }
}

Set集合如何确保数据的唯一性?

Set集合在新增数据时先判断数据的hashCode()是否已存在,若hashCode()在Set集合存在再调用equals()进行值比较;hashCode()与equals()都存在的情况下,Set集合才认为数据已存在,不予新增。

为什么要用对象的hashCode()直接用equals()判断不行吗?

出于执行效率考虑,hashCode()返回的整数结果决定了Set集合中的存放位置,hashCode()计算速度很快,但可能出现哈希碰撞;equals()则对值进行比较,处理速度相对较慢。

HashSet存储原理

  1. HashSet是Set接口的典型实现,大多数时候使用Set集合时就是使用这个实现类

  2. HashSet按Hash算法来决定集合元素的顺序,具有很好的查找性能

  3. 当向HashSet集合中存入一个元素时,根据该对象的hashCode值决定该对象在HashSet中的存储位置

Hash是什么?

Hash,一般翻译做散列、杂凑,或音译为哈希,是把任意长度的数据通过散列算法变换成固定的输出,该输出就是散列值。

LinkedHashSet

  1. LinkedHashSet是HashSet的子类,除HashSet的特性外,它同时使用链表维护元素的次序,可以保障按插入顺序提取数据

  2. LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet的性能

  3. 迭代访问Set里的全部元素时将有很的性能,因为它以链表来维护内部顺序

package cn.chengaofeng;

import java.util.LinkedHashSet;

import org.junit.Test;

public class LinkedHashSetTest {
    @Test
    public void testCreateHashSet() {
        LinkedHashSet<String> hashSet = new LinkedHashSet<>();
        hashSet.add("a");
        hashSet.add("b");
        hashSet.add("c");
        assert hashSet.size() == 3;
        System.out.println(hashSet);
        // 输出结果:[a, b, c] 有序的,按照添加的顺序输出
    }
}

TreeSet存储原理

  1. TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态

  2. TreeSet采用红黑树的数据结构来存储集合元素

  3. TreeSet默认采用自然排序对元素升序排列,也可以实现Comparable接口自定义排序方式

package cn.chengaofeng;

import java.util.TreeSet;

import org.junit.Test;

public class TreeSetTest {
    class IntegerComparator implements java.util.Comparator<Integer> {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    }

    @Test
    public void testCreateHashSet() {
        TreeSet<Integer> set = new TreeSet<>(new IntegerComparator());
        set.add(1);
        set.add(2);
        set.add(3);
        assert set.size() == 3;
        assert set.contains(1);
        System.out.println(set);
    }
}

Map映射体系及其应用

Map映射特点

  1. Map用于保存具有映射关系的数据,每组映射都是Key与Value组合而成

  2. Key与Value可以是任何引用类型数据,但是Key通常是String

  3. Map中的Key不允许重复,重复为同一个Key设置Value,后者Value会覆盖前者Value

HashMap和LinkedHashMap

Java先有Map后有Set,HashSet从HashMap精简而来

package cn.chengaofeng;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import org.junit.Test;

public class HashMapTest {
    @Test
    public void createHashMap() {
        HashMap<String, String> hashMap = new HashMap<>();
        hashMap.put("a", "1");
        hashMap.put("b", "2");
        hashMap.put("c", "3");
        assert hashMap.size() == 3;
        assert hashMap.get("a").equals("1");
        assert hashMap.get("b").equals("2");
        assert hashMap.get("c").equals("3");
        System.err.println(hashMap);
        assert hashMap.containsKey("a") == true;

        // 不确定值的类型
        HashMap<String, Object> student = new HashMap<>();
        student.put("name", "张三");
        student.put("age", 18);
        int age = (int) student.get("age");
        assert age == 18;
    }

    @Test
    public void createLinkedHashMap() {
        // 可以保证按照添加的顺序输出
        LinkedHashMap<String, Object> linkedHashMap = new LinkedHashMap<>();
        linkedHashMap.put("name", "张三");
        linkedHashMap.put("age", 18);
        System.out.println(linkedHashMap);
    }

    // 开发中我们会直接使用Map接口,而不是HashMap或LinkedHashMap
    @Test
    public void createMap() {
        // 可以保证按照添加的顺序输出
        Map<String, Object> linkedHashMap = new LinkedHashMap<>();
        linkedHashMap.put("name", "张三");
        linkedHashMap.put("age", 18);
        System.out.println(linkedHashMap);
    }
}

TreeMap

  1. TreeMap存储key-value对时,需要根据key对节点进行排序

  2. TreeMap支持两种Key排序:自然排序和定制排序

  3. 与TreeSet相同,TreeMap也是基于红黑树结构对数据进行排序

package cn.chengaofeng;

import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;

import org.junit.Test;

public class TreeMapTest {
    class RecordComparator implements Comparator<String> {

        @Override
        public int compare(String o1, String o2) {
            return o1.compareTo(o2);
        }

    }

    @Test
    public void createTreeMap() {
        Map<String, Object> record = new TreeMap<>(new RecordComparator());
        record.put("name", "张三");
        record.put("c", "cccc");
        record.put("age", 18);
        record.put("b", "bbbb");
        System.out.println(record);
    }
}

Map的遍历方式

package cn.chengaofeng;

import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import org.junit.Test;

public class TreeMapTest {
    @Test
    public void forEachMap() {
        Map<String, Object> record = new TreeMap<>(new RecordComparator());
        record.put("name", "张三");
        record.put("c", "cccc");
        record.put("age", 18);
        record.put("b", "bbbb");

        Set<String> set = record.keySet();

        for (String k : set) {
            System.out.println(k + " : " + record.get(k));
        }

        // 推荐使用
        record.forEach((k, v) -> {
            System.out.println(k + " : " + v);
        });

        Iterator<Map.Entry<String, Object>> it = record.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<String, Object> entry = it.next();
            System.out.println(entry.getKey() + " : " + entry.getValue());
        }
    }
}

应用Collections实现集合排序

Collections实现集合排序

package cn.chengaofeng;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.junit.Test;

public class ListSorterTest {

    @Test
    public void sortList() {
        List<Integer> list = new ArrayList<>();
        list.add(3);
        list.add(1);
        list.add(2);
        list.add(4);
        list.add(5);
        list.add(6);
        assert list.get(0) == 3;
        assert list.get(1) == 1;
        System.out.println(list);
        Collections.sort(list); // 会修改原list
        System.out.println(list);
    }

    class RecordComparator implements Comparator<Integer> {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o1 - o2;
        }
    }

    @Test
    public void sortList2() {
        List<Integer> list = new ArrayList<>();
        list.add(3);
        list.add(1);
        list.add(2);
        list.add(4);
        list.add(5);
        list.add(6);
        assert list.get(0) == 3;
        assert list.get(1) == 1;
        System.out.println(list);
        Collections.sort(list, new RecordComparator()); // 会修改原list
        System.out.println(list);
    }
}

自定义类型,如何通过集合排序?

package cn.chengaofeng;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.junit.Test;

public class CustomSortTest {
    class User {
        public int id;
        public String name;
        public int age;

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

    class UserComparator implements java.util.Comparator<User> {
        @Override
        public int compare(User o1, User o2) {
            // 字符串用 o1.name.compareTo(o2.name)
            return o1.id - o2.id;
        }
    }

    @Test
    public void sortList() {
        List<User> list = new ArrayList<>();
        list.add(new User(1, "张三", 18));
        list.add(new User(5, "孙七", 22));
        list.add(new User(2, "李四", 20));
        list.add(new User(4, "赵六", 21));
        list.add(new User(6, "周八", 23));
        list.add(new User(3, "王五", 19));

        assert list.get(0).id == 1;
        assert list.get(1).id == 5;
        list.forEach(u -> System.out.println(u.id));
        Collections.sort(list, new UserComparator()); // 会修改原list
        list.forEach(u -> System.out.println(u.id));
    }
}

总结

Java集合概述

  • 集合类型:List、Set、Map、Queue。

  • 核心接口:Collection和Map。

List接口

  • 特点:有序、可重复、允许使用索引。

  • 实现类

    • ArrayList:基于动态数组实现,支持快速随机访问。

    • LinkedList:基于双向链表实现,支持快速插入和删除。

Set接口

  • 特点:无序、不可重复。

  • 实现类

    • HashSet:基于哈希表实现,不保证元素顺序。

    • LinkedHashSet:基于哈希表和链表实现,维护插入顺序。

    • TreeSet:基于红黑树实现,元素处于排序状态。

Map接口

  • 特点:存储键值对,键唯一。

  • 实现类

    • HashMap:基于哈希表实现,不保证映射顺序。

    • LinkedHashMap:基于哈希表和链表实现,维护插入顺序。

    • TreeMap:基于红黑树实现,键处于排序状态。

集合遍历方式

  • for循环:直接遍历集合。

  • forEach:使用Lambda表达式遍历。

  • Iterator:使用迭代器遍历。

集合排序

  • Collections.sort():对List集合进行排序。

  • Comparator:自定义比较器,用于自定义排序规则。

集合操作

  • 添加add()

  • 删除remove()

  • 访问get()(List)、contains()containsKey()

  • 更新set()(List)、put()(Map)

集合特性

  • 线程安全:某些集合如VectorHashtable是线程安全的,但性能较低。Collections.synchronizedList()Collections.synchronizedMap()可以提供线程安全的包装。

  • 并发集合ConcurrentHashMapConcurrentLinkedQueue等提供了线程安全的并发访问。

记忆模型

  • List:想象成书架,每本书(元素)都有固定位置(索引)。

  • Set:想象成没有重复的艺术品收藏,每件艺术品(元素)都是独一无二的。

  • Map:想象成图书馆的目录系统,每个目录条目(键)都指向特定的书籍(值)。


评论