从底层设计分析List和Set的区别

本文详细介绍了Java集合框架中的List、Set和Map接口及其具体实现类的特点和应用场景,包括ArrayList、HashSet、HashMap等,并对比了它们之间的差异。

0. 集合关系类图

面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式。

数组虽然也可以存储对象,但长度是固定的;集合长度是可变的,数组中可以存储基本数据类型,集合只能存储对象。

集合类的特点:集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
Set接口

Set:元素是无序(存入和取出的顺序不一定一致),元素不可以重复。
  ——HashSet:底层数据结构是哈希表。是线程不安全的。不同步。
    HashSet是如何保证元素唯一性的呢?
    是通过元素的两个方法,hashCode和equals来完成。
    如果元素的HashCode值相同,才会判断equals是否为true。
    如果元素的hashcode值不同,不会调用equals。

注意,对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashcode和equals方法。

——TreeSet:

有序的存放:TreeSet  线程不安全,可以对Set集合中的元素进行排序

通过compareTo或者compare方法来保证元素的唯一性,元素以二叉树的形式存放。
    
集合的输出

——4种常见的输出方式

——Iterator: 迭代输出,使用最多的输出方式

——ListIterator: Iterator的子接口,专门用于输出List中的内容

——Enumeration

——foreach

在迭代时,不可以通过集合对象的方法操作集合中的元素,因为会发生ConcurrentModificationException异常。所以,在迭代器时,只能用迭代器的放过操作元素,可是Iterator方法是有限的,只能对元素进行判断,取出,删除的操作,如果想要其他的操作如添加,修改等,就需要使用其子接口,ListIterator。该接口只能通过List集合的listIterator方法获取。

在这里插入图片描述
Map接口的常用子类

Map
  ——HashMap:底层是哈希表数据结构,允许使用 null 值和 null 键,该集合是不同步的。将hashtable替代,jdk1.2.效率高。
  ——TreeMap:底层是二叉树数据结构。线程不同步。可以用于给map集合中的键进行排序。

集合工具类

Collections:集合框架的工具类。里面定义的都是静态方法。

Collections和Collection有什么区别?
    Collection是集合框架中的一个顶层接口,它里面定义了单列集合的共性方法。
      它有两个常用的子接口,
      ——List:对元素都有定义索引。有序的。可以重复元素。
      ——Set:不可以重复元素。无序。

Collections是集合框架中的一个工具类。该类中的方法都是静态的。
      提供的方法中有可以对list集合进行排序,二分查找等方法。
      通常常用的集合都是线程不安全的。因为要提高效率。
      如果多线程操作这些集合时,可以通过该工具类中的同步方法,将线程不安全的集合,转换成安全的。

总结:

List:add/remove/get/set。

1,ArrayList:其实就是数组,容量一大,频繁增删就是噩梦,适合随机查找;

2,LinkedList:增加了push/[pop|remove|pull],其实都是removeFirst;

3,Vector:历史遗留产物,同步版的ArrayList,代码和ArrayList太像;

4,Stack:继承自Vector。Java里其实没有纯粹的Stack,可以自己实现,用组合的方式,封装一下LinkedList即可;

5,Queue:本来是单独的一类,不过在SUN的JDK里就是用LinkedList来提供这个功能的,主要方法是offer/pull/peek,因此归到这里呢。

Set:add/remove。可以用迭代器或者转换成list。

1,HashSet:内部采用HashMap实现的;

2,LinkedHashSet:采用LinkedHashMap实现;

3,TreeSet:TreeMap。

Map:put/get/remove。

1,HashMap/HashTable:散列表,和ArrayList一样采用数组实现,超过初始容量会对性能有损耗;

2,LinkedHashMap:继承自HashMap,但通过重写嵌套类HashMap.Entry实现了链表结构,同样有容量的问题;

3,Properties:是继承的HashTable。

顺便说一下Arrays.asList,这个方法的实现依赖一个嵌套类,这个嵌套类也叫ArrayList!

1. List

List是有序并且允许重复
ArrayList 可变长度的动态数组 (它其实就是一个Object[] 的数组)
在这里插入图片描述
可以使用readObjec() 和 writeObject() 去序列化
在这里插入图片描述
ArrayList 有三种构造方式

在这里插入图片描述
new 一个ArrayList的时候是不会去初始化数组的,当调用add操作的时候,才会去初始化数组,如果没有传初始化因子的时候,默认数组大小是10;
在这里插入图片描述
ArrayList 扩容的操作
在这里插入图片描述
接着进行元素的拷贝

remove()方法
在这里插入图片描述

2. Set

set不允许重复且无序

  • HashSet 是对 HashMap的包装(特性跟HashMap差不多)
  • TreeSet 是对TreeMap的包装(特性跟TreeMap差不多)
<think>好的,用户现在问的是ListSet底层结构区别。之前他们问过关于Java继承中访问修饰符的问题,现在转向集合框架,可能需要了解这两种常用集合的底层实现,以便在实际开发中选择合适的结构。 首先,我需要回忆Java中ListSet的基本概念。List是有序、可重复的集合,而Set是无序、不可重复的。用户可能已经知道这些,但想深入底层结构。所以,我需要详细解释它们不同的实现类及其数据结构。 接下来,应该分点讨论。List的常见实现有ArrayList、LinkedListVector。ArrayList基于动态数组,LinkedList基于双向链表,Vector也是数组但线程安全。需要说明它们的增删改查的时间复杂度,比如ArrayList的随机访问快,但插入删除慢;LinkedList相反。 然后是Set的实现,如HashSet、LinkedHashSetTreeSet。HashSet基于哈希表(HashMap),LinkedHashSet在哈希表基础上维护链表保持插入顺序,TreeSet基于红黑树实现有序。要强调HashSet的O(1)时间复杂度,TreeSet的O(log n)排序特性。 还需要比较它们的底层数据结构差异,比如List多用数组或链表,而Set用哈希表或树结构。同时,线程安全方面,Vector是旧的实现,现在推荐用CopyOnWriteArrayList,而Set的线程安全版本如ConcurrentSkipListSet。 用户可能的应用场景:比如需要快速访问用ArrayList,频繁插入删除用LinkedList;去重用HashSet,需要排序用TreeSet。同时,线程安全的需求也是考虑点。 要确保回答结构清晰,可能用分点方式,结合代码示例表格对比,帮助用户理解不同实现之间的区别。还要注意提到版本变化,比如Java 8后HashMap的红黑树优化,避免过时信息。 最后,总结两者根本区别List维护元素顺序索引,允许重复;Set确保唯一性,部分实现维护顺序。这样的结构能帮助用户更好地记忆应用。</think>**ListSet底层结构对比(基于Java集合框架)** $$数据结构对比表$$ \begin{array}{|c|c|c|c|} \hline \text{类型} & \text{典型实现类} & \text{底层结构} & \text{时间复杂度} \\ \hline \text{List} & ArrayList & \text{动态数组} & O(1)\text{随机访问}, O(n)\text{插入删除} \\ & LinkedList & \text{双向链表} & O(n)\text{访问}, O(1)\text{头尾操作} \\ & Vector & \text{同步数组} & \text{类似ArrayList但线程安全} \\ \hline \text{Set} & HashSet & \text{哈希表(HashMap实例)} & O(1)\text{平均查找时间} \\ & LinkedHashSet & \text{哈希表+双向链表} & \text{保持插入顺序} \\ & TreeSet & \text{红黑树} & O(\log n)\text{有序操作} \\ \hline \end{array} $$ **一、List核心实现解析** 1. **ArrayList(动态数组)** - 初始容量10,扩容公式:$$newCapacity = oldCapacity + (oldCapacity >> 1)$$ - 随机访问效率最高(通过索引直接定位) - 示例内存布局: ```java // JDK17+ 后优化为紧凑型存储 class ArrayList<E> { transient Object[] elementData; // 实际存储数组 private int size; // 当前元素数量 } ``` 2. **LinkedList(双向链表)** - 节点结构: ```java class Node<E> { E item; Node<E> prev; Node<E> next; } ``` - 插入删除只需修改指针,但访问需要遍历 **二、Set核心实现解析** 1. **HashSet(哈希表)** - 实际使用HashMap存储元素(值存于Key) - 哈希冲突解决方案:链表→红黑树(JDK8+) - 当链表长度≥8且数组长度≥64时转换 2. **TreeSet(红黑树)** - 基于TreeMap实现,元素必须实现Comparable接口 - 树结构保证元素有序性(自然排序或Comparator) **三、关键差异对比** ```java // 结构验证示例 List<Integer> list = new ArrayList<>(); list.add(1); list.add(1); // 允许重复 System.out.println(list.get(0)); // 支持索引访问 Set<Integer> set = new HashSet<>(); set.add(1); set.add(1); // 自动去重 // set.get(0); // 编译错误,无索引访问 ``` **四、性能选择指南** 1. **高频查询场景**:优先ArrayList(随机访问O(1)) 2. **频繁增删场景**:考虑LinkedList(头尾操作O(1)) 3. **去重需求**:HashSet(最快去重O(1)) 4. **排序需求**:TreeSet(自动排序O(log n)) **五、线程安全扩展** - List线程安全方案: ```java List<String> syncList = Collections.synchronizedList(new ArrayList<>()); // 或使用CopyOnWriteArrayList(写时复制) ``` - Set线程安全方案: ```java Set<String> syncSet = Collections.synchronizedSet(new HashSet<>()); // 或使用ConcurrentSkipListSet(跳表实现) ``` **底层结构演进**:JDK8后HashMap的链表长度超过阈值时会转换为红黑树,这使得HashSet在最坏情况下的查找性能从O(n)提升到O(log n),这是List结构所不具备的优化特性。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值