Java集合类List、Set、Map总结

image-20240109001008082

集合类

我们口中所说的集合类的类型主要为三种:list(列表),set(集合),map(映射)

集合类是通过两个接口来实现的

1.Collection接口,list和set实现了这个接口

2.Map接口,map实现了这个接口
    
Collection为单列集合   Map为双列集合(键值对)

image-20240108234825551

常见的数据结构

我们常见的数据结构有三种

    1.数组结构

    2.链表结构

    3.哈希表结构

数组结构
概念
存储区间连续、内存占用严重、空间复杂度大、大小固定,不易扩展

    优点:
            随机读取和修改效率高,因为数组是连续的,随机访问性强,查找速度快

    缺点:
            插入和删除效率低,因为插入数据,这个位置后面的数据在内存中都要往后移动,且大小固定,不易动态扩展

链表结构
概念
存储区间离散、占用内存宽松、空间复杂度小、大小不固定,易于扩展

    优点:
            插入删除速度快,内存利用效率高,没有固定大小,扩展灵活

    缺点:
            查询较复杂,不能随机查找,每次都是从第一个开始遍历,查询效率低

哈希表结构
概念
结合数组结构和链表结构的优点,从而实现了查询修改效率高,插入和删除效率也高的一种数据结构

            HashMap就是这样一种数据结构

image-20240108235230374

    为何随机增删查询效率都很高
            因为增删是在链表上进行的,查询是在数组上进行的

List

1.1、特点:有序、有下标、元素可以重复

1.2、常用操作方法:add(E e)、remove(int index)、get(int index)、iterator()

1.3、实现类:ArrayList、Vector、LinkedList

1.4、实现类特点:

image-20240108235410870

ArrayList

概念

​ 底层数据结构为数组array,查询速度快,增删改慢,因为是一种类似数组的形式进行存储,所以随机访问的速度极快

常用方法
public class Test01 {
    public static void main(String[] args) {
        ArrayList<String> arrayList = new ArrayList<>();                    //创建一个arrayList对象
        ArrayList<String> arrayList1 = new ArrayList<>(10);     //创建一个长度为10的arrayList
        arrayList.add("123");                                               //向arrayList集合中添加一个元素,可以有选择的返回一个boolean类型
        boolean add = arrayList.add("456");                                 //返回boolean类型
        String s = arrayList.get(1);                                        //返回下标为1的元素(下标从0开始)
        String set = arrayList.set(1, "789");                               //将下标为i的元素替换为"789"并返回被替换的元素
        int size = arrayList.size();                                        //返回元素个数
        String remove1 = arrayList.remove(0);                         //删除下标为0的元素(下标从0开始),返回被删除的元素
        boolean remove = arrayList.remove("123");                        //删除集合中第一次出现的指定元素,返回boolean类型
        ArrayList<Integer> integerArrayList = new ArrayList<>();
        ArrayList<Integer> integerArrayList1 = new ArrayList<>();
        integerArrayList.add(123);
        integerArrayList.add(123);
        integerArrayList.add(456);
        integerArrayList1.add(123);
        System.out.println(integerArrayList);
        integerArrayList.removeAll(integerArrayList1);          //从集合integerArrayList中删除integerArrayList1的所有元素,不是只删除第一个,是删除所有的
        System.out.println(integerArrayList);
        arrayList.clear();                                                  //清除集合中的元素
        boolean empty = arrayList.isEmpty();                                //判断集合是否为空
        boolean contains = arrayList.contains("123");                       //判断集合中是否有“123”这个元素,返回boolean类型
        arrayList.add("123");
        arrayList.add("456");
        Iterator<String> iterator = arrayList.iterator();                   //返回集合首地址的迭代器,迭代器指向的是下标为0的前一个地址(?)
        boolean b = iterator.hasNext();                                     //判断iterator是否存在下一个元素,返回boolean
        String next = iterator.next();                                      //先将指针移动到下一个位置,然后再获取值
        while(iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

这里需要注意,迭代器iterator返回的是ArrayList下标为0的元素地址的前一个

Vector

概念

​ 底层的数据结构是是****数组array****,与ArrayList相同,查询速度快,增删改慢

常用方法
public class Test01 {
    public static void main(String[] args) {
        Vector<String> Vector = new Vector<>();                    //创建一个Vector对象
        Vector<String> Vector1 = new Vector<>(10);     //创建一个长度为10的Vector
        Vector.add("123");                                               //向Vector集合中添加一个元素,可以有选择的返回一个boolean类型
        boolean add = Vector.add("456");                                 //返回boolean类型
        String s = Vector.get(1);                                        //返回下标为1的元素(下标从0开始)
        String set = Vector.set(1, "789");                               //将下标为i的元素替换为"789"并返回被替换的元素
        int size = Vector.size();                                        //返回元素个数
        String remove1 = Vector.remove(0);                         //删除下标为0的元素(下标从0开始),返回被删除的元素
        boolean remove = Vector.remove("123");                        //删除集合中第一次出现的指定元素,返回boolean类型
        Vector.clear();                                                  //清除集合中的元素
        boolean empty = Vector.isEmpty();                                //判断集合是否为空
        boolean contains = Vector.contains("123");                       //判断集合中是否有“123”这个元素,返回boolean类型
        Vector.add("123");
        Vector.add("456");
        Iterator<String> iterator = Vector.iterator();                   //返回集合首地址的迭代器,迭代器指向的是下标为0的前一个地址(?)
        boolean b = iterator.hasNext();                                     //判断iterator是否存在下一个元素,返回boolean
        String next = iterator.next();                                      //先将指针移动到下一个位置,然后再获取值
        while(iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

与ArrayList相同

ArrayList和Vector的区别

1.在线程同步的方面来讲
ArrayList线程不同步,Vector线程安全,因为每个方法上都加上了synchronized,但是Vector的效率小于ArrayList

            Vector安全但是效率低,因为加锁和放锁的过程中,在系统中是有开销的

image-20240108235709211

LinkedList

概念

​ 底层数据接口使用****链表****,增删改速度快,查询稍慢

​ LinkedList是一个双向链表,没有初始化大小,也没有扩容的机制,就是一直在前面或者后面新增就好。

常用方法
public class Test01 {
    public static void main(String[] args) {
        LinkedList<String> LinkedList = new LinkedList<>();                    //创建一个LinkedList对象,无法初始化大小
        LinkedList.add("123");                                               //向LinkedList链表中添加一个元素,可以有选择的返回一个boolean类型
        boolean add = LinkedList.add("456");                                 //返回boolean类型
        LinkedList.addFirst("123");                                       //在头部插入
        LinkedList.addLast("123");                                        //在尾部插入
        String s = LinkedList.get(1);                                        //返回下标为1的元素(下标从0开始)
        String first = LinkedList.getFirst();                                //获取第一个下标的元素
        String last = LinkedList.getLast();                                  //获取最后一个下标的元素
        String set = LinkedList.set(1, "789");                               //将下标为i的元素替换为"789"并返回被替换的元素
        int size = LinkedList.size();                                        //返回元素个数
        String remove1 = LinkedList.remove(0);                         //删除下标为0的元素(下标从0开始),返回被删除的元素
        boolean remove = LinkedList.remove("123");                        //删除链表中第一次出现的指定元素,返回boolean类型
        String s1 = LinkedList.removeFirst();                                //删除链表中的第一个元素
        String s2 = LinkedList.removeLast();                                 //删除链表的最后一个元素
        boolean b1 = LinkedList.removeFirstOccurrence("123");             //删除链表中第一次出现"123"所在位置的元素
        boolean b2 = LinkedList.removeLastOccurrence("123");              //删除链表中最后一次出现"123"所在位置的元素
        LinkedList.clear();                                                  //清除链表中的元素
        boolean empty = LinkedList.isEmpty();                                //判断链表是否为空
        boolean contains = LinkedList.contains("123");                       //判断链表中是否有“123”这个元素,返回boolean类型
        LinkedList.add("123");
        LinkedList.add("456");
        Iterator<String> iterator = LinkedList.iterator();                   //返回链表首地址的迭代器,迭代器指向的是下标为0的前一个地址(?)
        boolean b = iterator.hasNext();                                     //判断iterator是否存在下一个元素,返回boolean
        String next = iterator.next();                                      //先将指针移动到下一个位置,然后再获取值
        while(iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

Set

1、特点:无序、无下标、元素不可以重复, (Set只能通过迭代器获得所有元素,然后逐个遍历)

2、常用操作方法:add(E e)、remove(Object o)、iterator()

3、Set接口有三个主要的实现类:HashSet、LinkedHashSet、TreeSet

4、实现类特点:

image-20240108235957037

set的实现类(HashSet,LinkedHashSet,TreeSet)都是线程不安全的, 解决方法

class E{
    public static void main(String[] args) {
        Set<Integer> set = new HashSet<>();
        Set<Integer> integers = Collections.synchronizedSet(set);       //这里是Collections,不是Collection,一个是类,一个是接口
    }
}

HashSet

概念

​ Set最常用的实现类,底层数据结构运用Hash(散列/哈希)算法,底层也是用数组实现

常用方法
public class Test02 {
    public static void main(String[] args) {
        HashSet<String> HashSet = new HashSet<>();                        //创建一个HashSet对象,无法初始化大小
        HashSet.add("123");                                               //向HashSet链表中添加一个元素,可以有选择的返回一个boolean类型
        boolean add = HashSet.add("456");                                 //返回boolean类型
        int size = HashSet.size();                                        //返回元素个数
        boolean remove1 = HashSet.remove(0);                           //删除下标为0的元素(下标从0开始),返回被删除的元素
        boolean remove = HashSet.remove("123");                        //删除链表中第一次出现的指定元素,返回boolean类型
        HashSet.clear();                                                  //清除链表中的元素
        boolean empty = HashSet.isEmpty();                                //判断链表是否为空
        boolean contains = HashSet.contains("123");                       //判断链表中是否有“123”这个元素,返回boolean类型
        HashSet.add("123");
        HashSet.add("456");
        Iterator<String> iterator = HashSet.iterator();                   //返回链表首地址的迭代器,迭代器指向的是下标为0的前一个地址(?)
        boolean b = iterator.hasNext();                                   //判断iterator是否存在下一个元素,返回boolean
        String next = iterator.next();                                    //先将指针移动到下一个位置,然后再获取值
        while(iterator.hasNext()) {                                       //迭代器遍历
            System.out.println(iterator.next());
        }
        for (String str: HashSet) {                                       //for循环遍历
            System.out.println(str);
        }
    }
}

LinkedHashSet

概念

​ 继承和HashSet类,底层也是哈希表结构,但是为了保持数据先后添加的顺序,所以又加了链表结构,因为多加了一层数据结构,所以效率低,不建议使用

常用方法
public class Test02 {
    public static void main(String[] args) {
        LinkedHashSet<String> LinkedHashSet = new LinkedHashSet<>();                        //创建一个LinkedHashSet对象,无法初始化大小
        LinkedHashSet.add("123");                                               //向LinkedHashSet链表中添加一个元素,可以有选择的返回一个boolean类型
        boolean add = LinkedHashSet.add("456");                                 //返回boolean类型
        int size = LinkedHashSet.size();                                        //返回元素个数
        boolean remove1 = LinkedHashSet.remove(0);                           //删除下标为0的元素(下标从0开始),返回被删除的元素
        boolean remove = LinkedHashSet.remove("123");                        //删除链表中第一次出现的指定元素,返回boolean类型
        LinkedHashSet.clear();                                                  //清除链表中的元素
        boolean empty = LinkedHashSet.isEmpty();                                //判断链表是否为空
        boolean contains = LinkedHashSet.contains("123");                       //判断链表中是否有“123”这个元素,返回boolean类型
        LinkedHashSet.add("123");
        LinkedHashSet.add("456");
        Iterator<String> iterator = LinkedHashSet.iterator();                   //返回链表首地址的迭代器,迭代器指向的是下标为0的前一个地址(?)
        boolean b = iterator.hasNext();                                   //判断iterator是否存在下一个元素,返回boolean
        String next = iterator.next();                                    //先将指针移动到下一个位置,然后再获取值
        while(iterator.hasNext()) {                                       //迭代器遍历
            System.out.println(iterator.next());
        }
        for (String str: LinkedHashSet) {                                       //for循环遍历
            System.out.println(str);
        }
    }
}

HashSet和LinkedHashSet的区别

1.在HashSet插入中,不保留顺序,这意味着元素的插入顺序不需要与元素的检索顺序相同。

​ 在LinkedHashSet中,保留插入顺序,这意味着元素的插入顺序必须与元素的检索顺序相同。

2.HashSet的效率高于LinkedHashSet

TreeSet

概念

​ Set接口的实现类,实现了SortSet接口,底层采用****红黑树****算法,TreeSet只能存储相同类型的对象的引用

常用方法
public class Test02 {
    public static void main(String[] args) {
        TreeSet<String> TreeSet = new TreeSet<>();                        //创建一个TreeSet对象,无法初始化大小
        TreeSet.add("123");                                               //向TreeSet链表中添加一个元素,可以有选择的返回一个boolean类型
        boolean add = TreeSet.add("456");                                 //返回boolean类型
        int size = TreeSet.size();                                        //返回元素个数
        boolean remove1 = TreeSet.remove(0);                           //删除下标为0的元素(下标从0开始),返回被删除的元素
        boolean remove = TreeSet.remove("123");                        //删除链表中第一次出现的指定元素,返回boolean类型
        TreeSet.clear();                                                  //清除链表中的元素
        boolean empty = TreeSet.isEmpty();                                //判断链表是否为空
        boolean contains = TreeSet.contains("123");                       //判断链表中是否有“123”这个元素,返回boolean类型
        TreeSet.add("123");
        TreeSet.add("456");
        Iterator<String> iterator = TreeSet.iterator();                   //返回链表首地址的迭代器,迭代器指向的是下标为0的前一个地址(?)
        boolean b = iterator.hasNext();                                   //判断iterator是否存在下一个元素,返回boolean
        String next = iterator.next();                                    //先将指针移动到下一个位置,然后再获取值
        while(iterator.hasNext()) {                                       //迭代器遍历
            System.out.println(iterator.next());
        }
        for (String str: TreeSet) {                                       //for循环遍历
            System.out.println(str);
        }
    }
}

List和Set的区别

对于ArrayList和HashSet来说

​ 1.是否有序
​ ArrayList是有序的

​ HashSet是无序的

​ 2.是否重复
​ ArrayList是可重复的

​ HashSet是不可重复的

​ 3.null元素数量
​ ArrayList可以有多个null元素

​ HashSet只能有一个null元素

4.获取元素方式
ArrayList可以使用迭代器取出所有元素,也可以通过get(index)获取制定下表的元素

Set只能通过迭代器获得所有元素,然后逐个遍历

Map

1、特点:

  • 用于存储任意键值对(key-value)
  • 键:无序、无下标、不允许重复(唯一)
  • 值:无序、无下标、允许重复

2、常用方法:put(K key, V value)、get(Object key)、keySet()、remove(K key)、entrySet()等

3、Map接口有三个主要的实现类:HashMap、TreeMap、HashTable

4、实现类的特点:

image-20240109000515949

HashMap

概念

​ 哈希表的实现原理,先拿一个数组表示位桶,每个位桶在用链表存储数据,当数据超过阈值8,且数组长度超过64的时候,会将数据转化为红黑树来存储数据,当数据个数小于6的时候,红黑树会退化为链表

image-20240109000535557

所以HashMap底层的数据结构是数组+链表+红黑树

    为什么长度为8转化为红黑树,不是一开始就是红黑树或者链表呢?
    首先我们知道,链表的平均查找的时间复杂度是O(n),红黑树有和链表不一样的查找性能,由于红黑树有自平衡的特点,所以始终可以将查找的时间复杂度控制在O(logn)

    至于为什么不一开始就是用红黑树,是因为

            对于红黑树来说,虽然查询成本低,但是新增的成本高

            对于链表来说,虽然查询成本高,但是新增的成本低

    最初链表还不是很长,所以O(n)和O(logn)的区别不大,如果长度过长,这种区别就回有多体现所以为了提高查找性能,要把链表转化为红黑树
常用方法
public class Test03 {
    public static void main(String[] args) {
        HashMap<String, Integer> hashMap = new HashMap();                           //新建一个HashMap
        hashMap.put("123",1);                                                       //将值存放到HashMap中
        Integer integer = hashMap.get("123");                                       //根据键获取值
        int size = hashMap.size();                                                  //获取HashMap大小
        hashMap.clear();                                                            //清空HashMap
        boolean empty = hashMap.isEmpty();                                          //判断是否为空
        Integer remove = hashMap.remove("123");                                 //根据键删除值,并返回
        Collection<Integer> collection;
        collection = hashMap.values();                                              //以Collection的形式返回HashMap集合中所有value组成的值
        System.out.println(collection);
        Set<String> strings = hashMap.keySet();                                     //以Set的形式返回键的集合
        Iterator<String> iterator1 = strings.iterator();                            //与keySet合用,获得迭代器
        Set<Map.Entry<String, Integer>> entries = hashMap.entrySet();               //将HashMap中的每个键值转化为Entry对象并返回Set集合
        Iterator<Map.Entry<String, Integer>> iterator = entries.iterator();         //与entrySet合用,返回迭代器
        while(iterator.hasNext()){
            Map.Entry<String, Integer> next = iterator.next();
            System.out.println(next.getKey());                                      //获得键
            System.out.println(next.getValue());                                    //获得值
        }
    }
}

TreeMap

概念

​ 实现了SortedMap接口,是一个有序的集合,底层是红黑树结构,每一个key-value都作为一个红黑树的节点

常用方法
public class Test03 {
    public static void main(String[] args) {
        TreeMap<String, Integer> TreeMap = new TreeMap();                           //新建一个TreeMap
        TreeMap.put("123",1);                                                       //将值存放到TreeMap中
        Integer integer = TreeMap.get("123");                                       //根据键获取值
        int size = TreeMap.size();                                                  //获取TreeMap大小
        TreeMap.clear();                                                            //清空TreeMap
        boolean empty = TreeMap.isEmpty();                                          //判断是否为空
        Integer remove = TreeMap.remove("123");                                 //根据键删除值,并返回
        Collection<Integer> collection;
        collection = TreeMap.values();                                              //以Collection的形式返回TreeMap集合中所有value组成的值
        System.out.println(collection);
        Set<String> strings = TreeMap.keySet();                                     //以Set的形式返回键的集合
        Iterator<String> iterator1 = strings.iterator();                            //与keySet合用,获得迭代器
        Set<Map.Entry<String, Integer>> entries = TreeMap.entrySet();               //将TreeMap中的每个键值转化为Entry对象并返回Set集合
        Iterator<Map.Entry<String, Integer>> iterator = entries.iterator();         //与entrySet合用,返回迭代器
        while(iterator.hasNext()){
            Map.Entry<String, Integer> next = iterator.next();
            System.out.println(next.getKey());                                      //获得键
            System.out.println(next.getValue());                                    //获得值
        }
    }
}

HashMap和TreeMap的区别

1.排序方式不同
HashMap是通过hashcode对内容进行快速查找的,HashMap中的元素是没有顺序的

​ TreeMap所有元素是由某一固定顺序的,如果需要的到一个有序结构,就应该使用TreeMap

​ 2.线程安全性相同
​ HashMap和TreeMap都是线程不安全的

​ 3.继承的父类不同
​ HashMap继承AbstractMap类,覆盖了hashcode和equals方法

​ ThreeMap继承SortedMap类,保持键的有序顺序

​ 4.底层数据结构不同
​ HashMap基于hash表实现,底层数据结构为数组+链表+红黑树

​ TreeMap基于红黑树实现,底层数据结构为红黑树

HashTable

概念

​ 继承了Map接口,和HashMap类似

HashMap和HashTable的区别

    1.继承的父类不同
            HashMap继承AbstractMap

            Hashtable继承Dicitionary(已废弃?)

    2.提供的接口不同
            HashTable比HashMap多提供elments和contians方法

    3.对Null key和Null value的支持不同
            HashTable既不支持Null key也不支持Null value

            HashMap中,Null可以作为key,这样的键只有一个

    4.线程安全性不同
            HashTable线程安全,但是效率低

            HashMap线程不安全,但是效率高

    5.初始容量大小和扩充容量大小不同
            HashTable默认初始化大小为11,每次扩充容量变为原来的2n+1

            HashMap默认初始化大小为16,每次扩充容量变为2倍

    6.计算hash值的方法不同
            HashTable直接使用对象的hashCode,计算时需要进行除法运算

            HashMap为了提高计算效率,将哈希表的大小固定为2的幂,不用做除法,只需要位运算即可

            HashMap虽然效率高,但是Hash冲突也增加了

参考

https://blog.csdn.net/Elephant_King/article/details/122350354

https://www.cnblogs.com/mikellee/p/14006963.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值