欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

java中集合(LinkedList、HashSet、HashMap、HashTable、Collection、Collections)

程序员文章站 2023-11-29 17:37:22
什么是集合集合类存放于java.util包中。集合类型主要有3种: set(集) 、list(列表) 和map(映射)。集合存放的都是对象的引用,而非对象本身。所以我们称集合中的对象就是集合中对象的引用。 简单来讲:集合就是一个放数据的容器,准确的说是放数据对象弓|用的容器。基本方法:int size() 获取元素个数boolean isEmpty0 是否个数为0boolean contains(Object element) 是否包含指元索boolean add(...

什么是集合

  1. 集合类存放于java.util包中。
  2. 集合类型主要有3种: set(集) 、list(列表) 和map(映射)。
  3. 集合存放的都是对象的引用,而非对象本身。所以我们称集合中的对象就是集合中对象的引用。
  • 简单来讲:集合就是一个放数据的容器,准确的说是放数据对象弓|用的容器。

基本方法:

  • int size() 获取元素个数

  • boolean isEmpty0 是否个数为0

  • boolean contains(Object element) 是否包含指元索

  • boolean add(E element) 添加元素,成功时返回true

  • boolean remove(Object element) 删除元素,成功时返回true

  • Iterator iterator() 获取迭代器

  • boolean addAl(Collection< extends E> )
    添加集合c中所有的元素到本集合中,如果集合有改变就返回true

  • boolean removeAll(Collection<> ) 删除本集合中和c集合中一致的元素,如果集合有改变就返回true

  • boolean retainAll(Collection<> ) 保留本集合中C集合中两者共有的,如果集合有改变就返回true

  • void clear()删除所有元素

    ●Object[] toArray0
    ●返回一个包含集合中所有元素的数组
    ● T toArray(TI a)
    ●返回一个包含集合中所有元素的数组,运行时根据集合元愫的类型指定数组的类型

java中集合(LinkedList、HashSet、HashMap、HashTable、Collection、Collections)

  • List:有可重复集合;
  • Set:无不重复集合;
  • Map:键值对存储,key必须唯一,只有一个key可以为null,value可以有多 个null,使用key来搜索value;

1.LinkedList

LinkedList也是List接口的实现类,和ArrayList功能类似,但LinkedList新增几个功能 ,如addFirst(),addLast(),getFirst(),getLast()等

数据结构:双向链表

链式存储,在内存中不是连续存储的,每个元素存储包括三部分,前一个元素的内存地址、数据部分、后一个元素内存地址,因此在进行元素的新增和删除时效率比较高,但查询时比较慢
链式存储方式与数组的连续存储方式相比,其实对内存的利用率更高

2.HashSet

HashSet是Set接口的实现类,最大的特点就是内部元素无序、并且不能重复(重复添加的话会默认覆盖上一个相同的元素),默认初始容量16

HashSet数据结构:哈希表(数组+链表)

  • HashSet存储原理:

1.Set set=new HashSet();
2.set.add(object);

存储过程为:
定义x=object.hashCode,得到obj对象的哈希码x,再对x进行hash散列运算,对数组长度进行求余,假如长度为20,则返回一个0-19之间的值,然后这个值就是存在HashSet数组中的下标。如果下标位置没有对象,则把object加到该位置;如果已有对象,则用equals判断两对象是否相等,相等则舍弃object,不相等,则把object以链表的方式加在该对象下面

原则1:让equals相等的对象返回相同的hashCode(为了过滤掉相等的元素)
原则2:尽量保证equals不相同的对象返回不同的hashCode(为了添加不同的元素)
HashSet的add()方法底层使用的是HashMap的存储方式,看源码可以知道

  • add()方法

HashSet检查重复
当HashSet添加元素时,HashSet会先计算元素的HashCode值是否在集合中已存在,若不存在,则直接加入,若HashCode值已存在,
则使用equals方法判断HashCode相同的两个元素的值是否相等,如果值相等则元素加入失败,也就是不能有复元素;
①如果两个对象相等,则HashCode- 定相等
②两个对象相等,则equals方法返回为true
③如果两个对象的HashCode相等,,对象不一定相等

3.HashMap

map是存放键值对的。
HashMap是Map接口的实现类,也是采用了hash算法。

其实向HashSet中放值,就是向HashMap中放值

Key不可以一样,如果key一样,在内存中存储的位置相同,后一个会把前一个key覆盖,value可以一样key和value均可以为null,HashMap的存储键的过程和HashSet一样,不过HashMap在根据key获取value时的原理如下

前面也是根据对象获取哈希码,进行哈希运算求下标(哈希码%容量),如果下标位置只有一对key-value,就直接取得value,如果有多个key-value键值对,然后再根据key获取value。

HashMap的数据结构
数组+单链表(数组中的每个元素都是单链表的头节点)
使用链表的原因是解决哈希冲突的(即不同的key映射到了数组的同一位置处,就将其放入单链表中)

HashMap是线程不安全的
HashTable是HashMap的兄弟。HashMap跟HashTable用法基本一样

它们两个区别就是HashTable是线程安全的

线程安全:
10个人同时操作HashTable,只有一个人操作,剩下的九个人等待。操作完毕后,剩下的9个人中的一个人操作HashTable,剩下8个人等待

线程不安全:
10个人同时操作

例子: 第一个人拿原始值, 如10
第二个人修改原始值 10-》20

如果二个人修改了原始值,会造成后面的人再拿数据时,会拿到第二个人修改后的数据(脏数据)

HashMap与Hashtable

  1. 线程安全,HashMap是线程不安全的,Hashtable是线程安全的Hashtable的每个方法都被synchronized修饰,每个方法都是同步的,但是效率低下,因此HashMap是效率比较高,但是线程不安全,Hashtable目前已被淘汰,可使用ConcurrentHashMap替代它;

  2. HashMap可以有nul键,
    但是在Hashtable不能有nul做为键,否则会报NPE即NullPointException空指针异常;

  3. 初始容量与扩容大小,创建时如果不指定容量大小,则会使用默认大小,Hashtable默认初始容量 大小为11,扩容为2n+1,HashMap默认初始容量大小为16,扩容为2n,若给定初始容量大小Hashtable则直接使用给定的值,HashMap将其扩充为2的幂次方大 小,也就是HashMap总是使用2的幂作为哈希表的大小;

  4. 底层数据结构, JDK1.8之 后HashMap中当链表的长度大于阈值(默认8) ,则将链表转化为红黑树,以减少搜索时间;

HashMap底层实现:
JDK1.8之前使用的是数组加链表结合在-起的链表散列, HashMap通过key的HashCode经过扰动函数处理过后得到Hash值,然后通过(n-1)&hash判断当前元素存放的位置(这里的n指的是数组的长度),如果当前位置存在元素,就判断该元素与添加的元素的hash值以及key是否相同,如果相同,直接覆盖,不相同就通过拉链法解决冲突。扰动函数指HashMap的hash方法, 使用hash方法是为了防止实现比较差的hashCode0方法为了减少碰撞;

HashMap的长度:
为了能让HashMap存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀。我们上面也讲到了过了, Hash 值的范围值-2147483648到2147483647.前后加起来大概40亿的映射空间,只要哈希函数映射得比较均匀松散,一般应用是很难 出现碰撞的。

但问题是一个40亿长度的数组,内存是放不下的。所以这个散列值是不能直接拿来用的。之前还要先做对数组的长度取模运算,得到
的余数才能用来要存放的位置也就是对应的数组下标。这个数组下标的计算方法是“(n- 1) & hash"。(n代表数组长度) 。这也就解
释了HashMap的长度为什么是2的幂次方。

这个算法应该如何设计呢?

我们首先可能会想到采用%取余的操作来实现。但是,重点釕:”取余(%)操作中如果除数是2的幂次则等价于与其除数减- 的与(&)操作(也就是说hash%length= =hash&(length-1)的前提是length是2的n次方; )。”.并且采用二进制位操作&,相对于%能够提高运算效率,这就解释了HashMap的长度为什么是2的幂次方。

因为n永远是2的次幂,所以n-1通过二进制表示,永远都是尾端以连续1的形式表示(00001111, 0000011)当(n- 1)和hash做与运算时,会保留hash中后x位的1,
例如00001111 & 10000011 = 00000011 这样做有2个好处:

①&运算速度快,至少比%取模运算块
②能保证索引值肯定在capacity中,不会超出数组长度
③(n- 1) & hash,当n为2次幂时,会满足一个公式: (n- 1) & hash= hash % n

4.HashTable

HashTable和HashMap功能类似,都是用来保存键值对,但两者之间又有区别

区别:

  1. HashTable中不允许保存null的,而HashMap可以保存空的null和value
  2. HashTable线程安全,HashMap线程不安全
  3. Hashtable继承自Dictionary类,而HashMap继承自AbstractMap类。但二者都实现了Map接口

5.Collection

  • Collection接口的子接口:List (序列) ,Queue,Set接口集合中不能存放基本数据类型数据,只能存放对象的引用;
  • collection是list和set接口的父接口,是一个集合接口

6.Collections

Collections 是一个包装类。它包含有各种有关集合操作的静态多态方法。此类不能实例化,就像一个工具类
常用方法如下:

  • Collections.sort()

  • Collections.reverse()

  • Collections.binarySearch()方法

为二分法查找,所以数组必须是有序的或者是用sort()方法排序之后的binarySearch(Object[], Object key)
方法的object[]参数是要查找的数组,key参数为要查找的key值。

Collection和Collections有什么区别?

Collection是集合体系的最顶层,包含了集合体系的共性,
Collections是一个工具类,方法都是用于操作Collection。

7.ArrayList

ArrayList是List接口的实现类,一种大小可变数组,随着元素的增多,容量会自动扩充,默认初始容量值是10,也可以自己指定初始容量

采用的数据结构:数组
(线性表:数组、链表、队列、栈
非线性表:二叉树、堆、图等)

  • ArrayList优点:

查询速度快
ArrayList缺点:
新增和删除元素比较慢

  • 查询速度快的原因:

ArrayList底层是数组实现的,根据下标查询,不需要比较,查询方式为,首地址+(元素长度*下标),基于这个位置读取相应的字节数,所以非常快;

  • 新增和删除慢的原因:

增删会带来元素的移动,增加数据会向后移动,删除数据会向前移动,所以影响效率

适用场景:
如果应用程序对数据有较多的随机访问使用ArrayList较好

ArrayList与LinkedList

  1. 两者都是线程不安全的集合;

  2. 底层数据结构的不同,ArrayList使用Object[]数组存储,LinkedList使用双向链表存储,JDK1.6之前为循环链表,JDK1.7取消了循环链表;

  3. 操作优劣,ArrayList使用数组存储,因此查询速度快,插入,删除速度慢,因为需要移动元素的位置,主要是指从指定位置插入删除速度,从末尾添加元素时间复杂度为O(1),LinkedList使用链表存储,因此添加,删除速度快,无需移动元素即可完成,而查询速度慢,每次查询某个元素都需要从头部节点或尾部节点开始遍历,直到查找到指定元素;

  4. 内存空间占用比较,ArrayList使用数组存储,列表后面总是需要预留一部分空间,造成空间浪费,LinkedList的每个元素所占用的空间总是要比ArrayList每个元素的空间大,因为它需要存储前一个节点的位置和后一个节点的位置,从而占用了较多的空间;

ArrayList与Vector

ArrayList与Vector-样都是使用Object]数组作为底层数据结构,但是ArrayList的方法不是同步的,因此是线程不安全的集合,而Vector的每个方法都是同步的,因此是线程安全的,但是当有多个线程同时访问Vector时,只能有一个线程访问它,其他线程只能等待它完成,因此非常耗时;因此当操作不需要保证线程安全时使用ArrayList比较合适;

八、总结

①List
Arayt-i-d-------- Object数组实现
Vector----- >Object数组实现
Linked-is------->双向链表,JDK1.6之 前为循环链表

②Set
HashSet(无序,唯一)------> 基于HashMap实现
LinkedHash--------->基于HashSet,其内部是基于LinkedHashMap实现
TreeSet(有序,唯- ---->红黑树(自平衡的排序二叉树)

③Map
HashMap
JDK1.7---->由数组+链表组成; .
JDK1-.8—>阈值大于8s时,链表转变成红黑树,减少搜索时间
LinkedHashMap
继承自HashMap,在HashMap 基础上,增加一条双向链表
Hashtable---->数组+链表
TreeMp------>红黑树(自平衡的排序二叉树)

HashMap
JDK1-.7—>由数组+链表组成;
JDK1.8—>阈值大于8s时,链表转变成红黑树,减少搜索时间

LinkedHashMap
继承自HashMap,在HashMap 基础上,增加一双向链表
Hashtabe---->数组+链表
TreeM------->红黑树(自平衡的排序二叉树)

本文地址:https://blog.csdn.net/weixin_44641846/article/details/107077908