Java集合——Map、Set和List总结


文章目录


一、Collection

Collection 的常用方法

  • public boolean add(E e):把给定的对象添加到当前集合中 。
  • public void clear():清空集合中所有的元素。
  • public boolean remove(E e):把给定的对象在当前集合中删除。
  • public boolean contains(E e):判断当前集合中是否包含给定的对象。
  • public boolean isEmpty():判断当前集合是否为空。
  • public int size():返回集合中元素的个数。
  • public Object[] toArray():转换为数组。
    当需要数组转换为集合时,使用 Arrays.asList(list)

Collection 的遍历方式

  • 迭代器 iterator():获取当前集合迭代对象,然后调用方法
java 复制代码
  1、hasNext():当前位置是否有数据
  2、next():返回当前位置,并向后移动

   Collection<Integer> list = new ArrayList<>();
   list.add(1);
   Iterator<Integer> iterator = list.iterator();
   while (iterator.hasNext()) {
      System.out.println(iterator.next());
   }

迭代器遍历注意事项

1、迭代器不可多次获取当前元素 ,否则会报错,因为每一次获取都会向后移动,你一次判断多次获取,则会越界抛出异常NoSuchElementException

2、迭代器不支持集合自身方法remove(),但支持迭代器对象自身的remove()方法(集合自身的移除方法会导致一些其他元素没有遍历到,到)

java 复制代码
        Collection<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        Iterator<Integer> iterator = list.iterator();
        while (iterator.hasNext()) {
            if (iterator.next() == 2){
               // list.remove(1);  此代码报错,不支持
                iterator.remove();
            }
        }
        System.out.println(list); 输出 [1]

  • 增强for
    for(数据类型 变量名 :数组或集合){} :支持集合和数组遍历
    查看反编译文件,发现增强for(数据类型 变量名 :数组或集合){} 底层是基于iterator迭代器实现的

  • Lambda表达式
    Lambda表达式是简化函数式接口的内部类方法(函数式接口是指接口,仅且只有一个抽象类方法,被@FunctionalInterface修饰)
    forEach(匿名内部类的Lambda的简化表达式)

Collection不支持普通for()遍历


二、Map、Set、List的不同


  • Collection是单列集合顶级父类接口
  • Map是双列集合顶级父类接口
Map Set List
HashMap HashSet ArrayList
TreeMap TreeSet LinkedList
LinkedHashMap LinkedHaspSet ---
键值对存储数据 存取无序 不重复 无索引 存取有序 可重复 有索引

三、List

  • List:有序、可重复、有索引
  • 常用特有方法

add(int index , E e)

remove(int index)

set(int index , E e)

get(int index)

  • 支持的遍历方式:迭代器遍历、增强for遍历、Lambda遍历、普通for遍历

1、ArrayList

  • ArrayList是List接口实现类,存取有序、可以存储重复元素、可以使用下标操作元素,因为底层是基于数组实现的,在内存中是连续的
  • 支持重复数据的插入
  • 适合快速查询,但是不适合中间插入和删除操作
  • ArrayList实例化后,当你插入第一个数据开始,它的数组大小会变为10 ;当你插入的数据超过这个数组大小,ArrayList会动态的对数组实现扩容:新数组大小 = 旧数组大小 × 1.5
java 复制代码
默认大小
private static final int DEFAULT_CAPACITY = 10;
....
扩容方法
private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1); 扩容至原来的1.5倍
    if (newCapacity - minCapacity < 0)  新的容量小于指定容量的最小值
        newCapacity = minCapacity;      扩容至指定容量的最小值(第一次就是10)
    if (newCapacity - MAX_ARRAY_SIZE > 0) 
        newCapacity = hugeCapacity(minCapacity); 
    elementData = Arrays.copyOf(elementData, newCapacity);  将数组复制到一个新数组中,长度为 newCapacity
}

>> 相当于除2,而 << 相当于乘2

  • 常用方法
方法名 作用
list.add("a") 直接添加数据
list.add(1, "b") 根据索引位置,添加数据
list.contains("a") 判断是否包含数据
list.get(1) 根据索引位置获取数据
list.indexOf("a") 根据数据本身返回索引位置
list.set(1, "c") 根据索引修改数据
list.remove("a") 根据数据本身直接移除数据
list.remove(1) 根据索引移除数据

2、LinkedList

  • LinkedList 实现接口 List,存取有序、支持索引操作,底层由双向链表实现,只能从一端开始遍历,查询效率低,在内存中不是连续的
  • 因为链表结构,LinkedList 更适合删除插入操作,只需要修改前结点和后一个结点引用指向即可
  • 可以插入重复数据

基于添加add(),分析一下源码

1、定义一个集合,并添加数据,看看内部实现

java 复制代码
    public static void main(String args[]) {
        LinkedList<Object> list = new LinkedList<>();
        list.add("a");
        list.add(1,"b");
        System.out.println(list);
        
    }

2、查看根据索引添加数据的内部实现

java 复制代码
    public void add(int index, E element) {
        checkPositionIndex(index);  //检查索引是否越界

        if (index == size) //添加位置在最后一个节点
            linkLast(element); 
        else
            linkBefore(element, node(index));   //任意结点前插入数据
    }
  • 1、如果索引越界则抛出异常 IndexOutOfBoundsException
  • 2、索引不越界时,判断是否添加位置是否在最后一个,是则添加,不是则调用 linkBefore() 方法

3、我们看 linkBefore() 中的**node(index)**方法,

java 复制代码
    Node<E> node(int index) {
        // assert isElementIndex(index);
        if (index < (size >> 1)) {  //索引在链表左边
            Node<E> x = first;    //获取第一个结点
            for (int i = 0; i < index; i++)  //遍历,并返回index处结点
                x = x.next;
            return x;
        } else {  //索引在链表右边
            Node<E> x = last;
            for (int i = size - 1; i > index; i--) //遍历,并返回index处结点
                x = x.prev;
            return x;
        }
    }

看到返回值和内部代码实现,可知主要作用即返回指定元素索引处的节点。现在返回 linkBefore(element, node(index))

java 复制代码
    void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
        final Node<E> pred = succ.prev;  获取索引处前一结点
        final Node<E> newNode = new Node<>(pred, e, succ); 新建一个新的结点
        succ.prev = newNode;  索引处的前指针指向新结点
        if (pred == null)  如果前结点为空,则新结点变为头节点
            first = newNode;
        else
            pred.next = newNode;  否则,前结点的向后指针指向新结点
        size++;  长度加一
        modCount++; 表示修改次数加一
    }

看了上面代码,大致应该知道添加内部代码实现流程,其他的方法就不一一列举,有兴趣去看看源码即可


四、Map

  • key不支持重复,value可以重复,如果key重复则value会被覆盖
  • 常用方法:

(1)put(K key, V value)

(2)get(Object key)

(3)size()

(4)clear()

(5)isEmpty ()

(6)remove(Object key)

(7)values():获取全部值

(8)keySet() :获取全部键

(9)containsKey():是否包含键

(10)containsValue():是否包含值

(11)putAll():把一个map添加进另一个map

(12)entrySet():获取全部集合内所有对象数据

  • 遍历方式:

    1、键遍历获取值:可以根据keySet()获取全部键,使用增强for()遍历
    2、键值对遍历获取值:可以根据entrySet()获取一个set集合数据,再使用增强for()遍历,getKey()可以获取键,getValue()获取值
    3、 Lambda遍历:底层其实就是键值对遍历
    map.forEach((k, v) -> {System.out.println(v);});


1、HashMap

1、 HashMap 底层由哈希表(数组、链表、红黑树)实现

2、HashMap 初始默认大小是16,负载因子是0.75 ,当填充元素达到扩容要求时,HashMap会自动扩容,每次扩容是旧数组的两倍

3、HashMap实现接口Map,所有是无序的、不支持重复、无索引

HashMap细节:

1、HashMap添加数据,如果此时数组大小正好插满了12(16×0.75)个时,如果当前发生冲突则数组扩容,如果没有发生冲突则不扩容

2、当数组大小大于或者等于64时,才会把链表大于8的转换为红黑树


2、LinkedHashMap

LinkedHashMap:有序、不重复、无索引,底层由哈希表(数组、链表、红黑树)实现,并且维护了一个双向链表机制


3、TreeMap

按照key的大小升序排序,不重复、无索引,底层基于红黑树实现


五、Set

Set:无序、不可重复、无索引

  • 常用方法:几乎都是Collection的方法

  • HashSet:存取无序、不可重复、无索引操作,底层由哈希表(数组、链表、红黑树)实现
  • 如果自定义对象,需要重写hashCod()和equal()方法,实现不可重复

  • LinkedHashSet:存取有序、不可重复、无索引操作,底层由哈希表(数组、链表、红黑树)实现,并且维护了一个双向链表机制

  • TreeSet:内部升序排序、不可重复、无索引操作,基于红黑树实现
  1. 数值包装类型和字符串这两种对象可以升序排序

  2. 其他对象不可以排序,但是可以自定义排序规则

  3. 排序规则

  • 实现接口 Comparable,自定义compareTo()方法
  • 调用有参构造器,设置Comparator对象,实现compare()方法
  • 如果两则都实现了,则选择就近原则,选择Comparator对象比较

相关推荐
wm104310 分钟前
java web springboot
java·spring boot·后端
smile-yan12 分钟前
Provides transitive vulnerable dependency maven 提示依赖存在漏洞问题的解决方法
java·maven
老马啸西风13 分钟前
NLP 中文拼写检测纠正论文-01-介绍了SIGHAN 2015 包括任务描述,数据准备, 绩效指标和评估结果
java
Earnest~16 分钟前
Maven极简安装&配置-241223
java·maven
皮蛋很白19 分钟前
Maven 环境变量 MAVEN_HOME 和 M2_HOME 区别以及 IDEA 修改 Maven repository 路径全局
java·maven·intellij-idea
青年有志21 分钟前
JavaWeb(一) | 基本概念(web服务器、Tomcat、HTTP、Maven)、Servlet 简介
java·web
上海研博数据25 分钟前
flink+kafka实现流数据处理学习
java
KpLn_HJL26 分钟前
leetcode - 2139. Minimum Moves to Reach Target Score
java·数据结构·leetcode
小扳2 小时前
微服务篇-深入了解 MinIO 文件服务器(你还在使用阿里云 0SS 对象存储图片服务?教你使用 MinIO 文件服务器:实现从部署到具体使用)
java·服务器·分布式·微服务·云原生·架构
龙少95432 小时前
【深入理解@EnableCaching】
java·后端·spring