Java中的容器及其区别

|Collection 
|  ├List 
|  │-├LinkedList 
|  │-├ArrayList 
|  │-└Vector 
|  │ └Stack 
|  ├Set 
|  │├HashSet 
|  │├TreeSet 
|  │└LinkedSet 

|Map 
  ├Hashtable 
  ├HashMap 
  └WeakHashMap

1、Java中的容器

Java容器类类库的用途是“持有对象”,并将其划分为两个不同的概念:

1)Collection:一个独立元素的序列,这些元素都服从一条或者多条规则。 List必须按照插入的顺序保存元素,而set不能有重复的元素。Queue按照排队规则来确定对象产生的顺序(通常与它们被插入的顺序相同)。 
2)Map:一组成对的键值对对象,允许使用键来查找值

Collection集合

Collection是最基本的集合接口,List和Set是Collection集合的两个主要的子接口。

List接口

List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。 实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。

LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的get,remove,insert方法在 LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。注意:LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:List list = Collections.synchronizedList(new LinkedList(…));

ArrayList实现了可变大小的数组。它实现了List接口,允许所有元素,包括null。ArrayList没有同步。size,isEmpty,get,set方法运行时间为常数。但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。和LinkedList一样,ArrayList也是非同步的(unsynchronized)。一般情况下使用这两个就可以了,因为非同步,所以效率比较高。 
如果涉及到堆栈,队列等操作,应该考虑用List(如LinkedList和ArrayList),对于需要快速插入,删除元素,应该使用LinkedList,如果需要快速随机访问元素,应该使用ArrayList。

Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和ArrayList创建的 Iterator是同一接口,但是,因为Vector是同步的,当一个 Iterator被创建而且正在被使用,另一个线程改变了Vector的状态(例如,添加或删除了一些元素),这时调用Iterator的方法时将抛出ConcurrentModificationException,因此必须捕获该异常。

Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop方 法,还有 peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。

Set接口

Set是一种不包含重复元素的Collection,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。 Set的构造函数有一个约束条件,传入的Collection参数不能包含重复的元素。 Set容器类主要有HashSet和TreeSet等。

HashSet :Java.util.HashSet类实现了Java.util.Set接口。 它不允许出现重复元素;不保证集合中元素的顺序 ;允许包含值为null的元素,但最多只能有一个null元素。

TreeSet是将元素存储在红-黑树结构中,所以存储的结果是有顺序的(所以如果想要存储的集合有顺序那么选择TreeSet)。

Map集合接口

Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个 value。Map接口提供3种集合的视图:Map的内容可以被当作一组key集合,或者一组value集合,或者一组key-value映射。

Hashtable:Hashtable实现Map接口,实现一个key-value映射的哈希表。任何非空(non-null)的对象都可作为key或者value。由于作为key的对象将通过计算其散列函数来确定与之对应的value的位置,因此任何作为key的对象都必须实现hashCode和equals方法。

HashMap :HashMap和Hashtable类似,不同之处在于HashMap是非同步的,并且允许null,即null value和null key,但是将HashMap视为Collection时(values()方法可返回Collection),其迭代子操作时间开销和HashMap的容量成比例。因此,如果迭代操作的性能相当重要的话,不要将HashMap的初始化容量设得过高,或者load factor过低。

-JDK1.0引入了第一个关联的集合类HashTable,它是线程安全的。 HashTable的所有方法都是同步的。 
-JDK2.0引入了HashMap,它提供了一个不同步的基类和一个同步的包装器synchronizedMap。synchronizedMap被称为 有条件的线程安全类。 
-JDK5.0util.concurrent包中引入对Map线程安全的实现ConcurrentHashMap,比起synchronizedMap,它提供了更高的灵活性,同时可以并发地进行读和写操作。

HashTableHashMap的区别主要有两点

1、Hashtable 中的方法是同步的,而HashMap中的方法是非同步的。在多线程并发的环境下,可以直接使用Hashtable,但是要使用HashMap的话就要自己增加同步处理了。

2、Hashtable中,key和value都不允许出现null值。在HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,即可以表示 HashMap中没有该键,也可以表示该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。

一、线程安全(Thread-safe)的集合对象:

  • Vector 线程安全
  • Stack线程安全(继承Vector
  • HashTable 线程安全
  • StringBuffer 线程安全
  • ConcurrentHashMap 线程安全
  • CopyOnWriteArrayList 线程安全

二、非线程安全的集合对象:

  • ArrayList
  • LinkedList
  • HashMap
  • HashSet
  • TreeMap
  • TreeSet
  • StringBulider

2、Collection和Collections的区别

java.util.Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List与Set。

Collections是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。此类不能实例化,就像一个工具类,服务于Java的Collection框架。

Collections工具类中经常用到的方法:

(1)public static void sort(List list);--根据元素的自然顺序对指定列表按升序排序。

(2)public static void reverse(List list);--逆序反转,反转指定列表中元素的顺序。

(3)public static void shuffle(List list);--使用默认随机源随机更改指定列表的元素顺序。

(4)public static void copy(List deslist,List sourcelist);--将所有元素从一个列表(sourcelist)复制到另一个列表(deslist)。

(5)public static ArrayList list(Enumeration e);--返回一个数组列表,按照枚举结构中元素的原有顺序包含其中的所有元素。

(6)public static int frequency(Collection c,Object o);--返回指定collection中等于指定对象的元素数。

(7)public static T max(Collection c);--按照元素的自然顺序,返回指定collection中的最大元素。

(8)public static T min(Collection c);--按照元素的自然顺序,返回指定collection中的最小元素。

(9)public static void rotate(List list,int distance);--根据指定的距离循环移动指定列表的元素。

3、List、Set、Map之间的区别

List、Set、Map之间的区别主要体现在元素的重复性、有序性和是否允许为空值三方面。

1) 元素重复性:

① List允许有重复的元素。任何数量的重复元素都可以在不影响现有重复元素的值及其索引的情况下插入到List集合中;

② Set集合不允许元素重复。Set以及所有实现了Set接口的类都不允许重复值的插入,若多次插入同一个元素时,在该集合中只显示一个;

③ Map以键值对的形式对元素进行存储。Map不允许有重复键,但允许有不同键对应的重复的值;

2) 元素的有序性:

① List及其所有实现类保持了每个元素的插入顺序;

② Set中的元素都是无序的,但是某些Set的实现类以某种特殊形式对其中的元素进行排序,如:LinkedHashSet按照元素的插入顺序进行排序;

③ Map跟Set一样对元素进行无序存储,但其某些实现类对元素进行了排序,如:TreeMap根据键对其中的元素进行升序排序;

3) 元素是否为空值:

① List允许任意数量的空值;

② Set最多允许一个空值的出现;[ 当向Set集合中添加多个null值时,在该Set集合中只会显示一个null元素]

③  Map只允许出现一个空键,但允许出现任意数量的空值;

总结:List中的元素:有序、可重复、可为空;

Set中的元素:无序、不重复、只允许有一个空元素;

Map中的元素:无序、键不可重复,值可重复、只允许有一个空键,有多个空值

什么场景下使用listsetmap

1、如果经常使用索引来对容器中的元素进行访问,那么List是正确的选择。如果已经知道索引的话,那么List的实现类(如ArrayList)可以提供更快速的访问,如果经常添加或删除元素,那么LinkedList是最好的选择。

2、如果想要容器中的元素能够按照它们插入的次序进行有序存储,那么就选择List,因为List是一个有序容器,它按照插入顺序进行存储。

3、如果想要保证插入元素的唯一性,即不允许有重复的元素,那么可以选择一个Set的实现类,比如HashSet、LinkedHashSet或者TreeSet。所有Set的实现类都遵循了统一约束比如唯一性,另外还提供了额外的特性,比如TreeSet还是一个SortedSet,所有存储于TreeSet中的元素可以使用Java里的Comparator或者Comparable进行排序。LinkedHashSet也按照元素的插入顺序对它们进行存储。

4、如果想要以键和值的形式进行数据存储,那么Map是正确的选择。

3.1、ArrayList、LinkedList和Vector的区别

ArrayList:基于数组实现的,数组长度大于实际存储的元素个数。允许使用索引查询元素,所以查询速度较快。但是插入或删除数据时,需要移动插入或删除元素所在索引的所有后续元素,因此,插入或删除数据比较慢。另外,ArrayList不是线程安全的。

Vector:与ArrayList几乎相同,主要区别在于Vector是线程安全的(方法使用synchronized关键字修饰),而ArrayList不是线程安全的。另外,在扩容时,ArrayList增加50%,而Vector会扩容1倍。

LinkedList:使用双向链表实现存储,按索引查询数据需要进行向前或向后遍历,所以查询速度较慢。但是插入数据时只需要记录本项前后项即可,因此插入数据较快。另外,LinkedList不是线程安全的,LinkedList也不需要扩容。

线程安全的List:Vector、SynchronizedList和CopyOnWriteArrayList。

Vector是线程安全的List集合,不过他的性能是最差的,所有的方法都是加了synchronized来同步,从而保证线程安全。如下所示:

public synchronized boolean add(E e) {modCount++;ensureCapacityHelper(elementCount + 1);elementData[elementCount++] = e;return true;
}public synchronized E get(int index) {if (index >= elementCount)throw new ArrayIndexOutOfBoundsException(index);return elementData(index);
}public synchronized E remove(int index) {modCount++;if (index >= elementCount)throw new ArrayIndexOutOfBoundsException(index);E oldValue = elementData(index);int numMoved = elementCount - index - 1;if (numMoved > 0)System.arraycopy(elementData, index+1, elementData, index,numMoved);elementData[--elementCount] = null; // Let gc do its workreturn oldValue;
}

SynchronizedList是Collections类的静态内部类,它能把所有 List 接口的实现类转换成线程安全的List,比 Vector 有更好的扩展性和兼容性。它的所有方法都是带同步对象锁的,和 Vector 一样,它不是性能最优的。如下所示:

public E get(int index) {synchronized (mutex) {return list.get(index);}
}public E set(int index, E element) {synchronized (mutex) {return list.set(index, element);}
}public void add(int index, E element) {synchronized (mutex) {list.add(index, element);}
}public E remove(int index) {synchronized (mutex) {return list.remove(index);}
}

CopyOnWriteArrayList是java1.5以后才加入的新类,从命名可以理解为复制再写入的List。

它的添加或删除操作是加锁的(ReentrantLock ,非synchronized同步锁),读操作没有加锁。添加或者删除元素时,先加锁,再进行复制替换操作,最后再释放锁。 它的优势在于,读操作是不加锁的。这样做的好处是,在高并发情况下,读取元素时就不用加锁,写数据时才加锁,大大提升了读取性能。缺点是浪费空间,每次添加、删除元素就需要复制一份新数组。

public class CopyOnWriteArrayList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable {private static final long serialVersionUID = 8673264195747942595L;/** The lock protecting all mutators */final transient ReentrantLock lock = new ReentrantLock();/** The array, accessed only via getArray/setArray. */private transient volatile Object[] array;/*** Gets the array.  Non-private so as to also be accessible* from CopyOnWriteArraySet class.*/final Object[] getArray() {return array;}/*** Sets the array.*/final void setArray(Object[] a) {array = a;}//新增
public boolean add(E e) {final ReentrantLock lock = this.lock;lock.lock();try {Object[] elements = getArray();int len = elements.length;Object[] newElements = Arrays.copyOf(elements, len + 1);newElements[len] = e;setArray(newElements);return true;} finally {lock.unlock();}
}
//查询
public E get(int index) {return get(getArray(), index);
}
private E get(Object[] a, int index) {return (E) a[index];
}
//删除
public E remove(int index) {final ReentrantLock lock = this.lock;lock.lock();try {Object[] elements = getArray();int len = elements.length;E oldValue = get(elements, index);int numMoved = len - index - 1;if (numMoved == 0)setArray(Arrays.copyOf(elements, len - 1));else {Object[] newElements = new Object[len - 1];System.arraycopy(elements, 0, newElements, 0, index);System.arraycopy(elements, index + 1, newElements, index,numMoved);setArray(newElements);}return oldValue;} finally {lock.unlock();}
}

remove(int index)的作用就是将volatile数组中第index个元素删除。它的实现方式是,如果被删除的是最后一个元素,则直接通过Arrays.copyOf()进行处理,而不需要新建数组。否则,新建数组,然后将volatile数组中被删除元素之外的其它元素拷贝到新数组中;最后,将新数组赋值给volatile数组。

3.2、HashMap、Hashtable和TreeMap的区别

HashMap和HashTable都实现了Map接口。

HashMap允许有空键和空值,而HashTable不允许有空键或空值。HashTable中的方法是被synchronized关键字修饰的,所以HashTable是线程安全的。而HashMap不是线程安全的,HashMap是HashTable的轻量级实现(非线程安全的实现),所以效率会高于HashTable。HashMap去掉了HashTable的contains方法,改成containsValue和containsKey方法。

在多个线程访问HashTable时,不需要自己为它的方法实现同步,而HashMap就必须为之提供同步。

HashMap是基于散列表(哈希表)实现的,TreeMap是基于红黑树实现的。

HashMap中存储的元素是无序的,TreeMap中存储的元素是有序的(如果需要按某种顺序存储键值对数据,可以使用TreeMap)。

HashMap是通过key的hashCode方法对数据进行查询的,Treemap适用于按自然顺序或自定义顺序遍历键(key)。HashMap和TreeMap都是非线程安全的。

3.3、HashSet、TreeSet和LinkedHashSet的区别

Set接口继承了Collection接口。Set集合不包含重复的元素,这是使用Set的主要原因。有三种常见的Set实现——HashSet, TreeSet和LinkedHashSet。

HashSet是基于哈希表(散列表)实现的(基于HashMap实现的),其中存储的元素是无序的。

TreeSet是基于红黑树实现的,其中存储的元素是有序的。

LinkedHashSet也是基于哈希表实现的,同时维护了一个双链表来记录插入数据的顺序。

总体而言,如果需要一个访问快速的Set,应该使用HashSet;如果需要一个排序的Set,应该使用TreeSet;当需要记录插入数据的顺序时,应该使用LinkedHashSet。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/59011.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

论文阅读- --DeepI2P:通过深度分类进行图像到点云配准

目前存在的问题&#xff1a; 单模态配准具有局限性&#xff0c;多模态研究很少跨模态图像到点云配准问题是求解相机坐标系与点云之间的旋转矩阵R ∈ SO(3)和平移向量t ∈ R3。 这个问题很困难&#xff0c;因为由于缺乏点到像素的对应关系&#xff0c;无法使用 ICP、PnP 和捆绑调…

大厂面试真题-如果使用guava limiter实现实例级别的缓存

Guava库中的RateLimiter和Cache是两个不同的组件&#xff0c;分别用于控制访问频率和实现缓存功能。RateLimiter用于流量控制&#xff0c;确保系统在处理请求时不会超过指定的速率&#xff0c;而Cache则用于存储数据以加快访问速度。 由于RateLimiter本身并不直接支持实现缓存…

Air780E如何发送SMS?一文详解!

今天一起来学习使用合宙低功耗4G模组Air780E发送SMS短消息&#xff1a; 一、SMS简介 SMS&#xff08;短消息服务&#xff0c;ShortMessageService&#xff09;功能主要用于在蜂窝网络中传输短消息。 在4G网络中&#xff0c;短信可以在数据传输的同时进行&#xff0c;不会因数…

【TC3xx芯片】TC3xx芯片PFlash的ECC校验问题补充

目录 前言 正文 1.PFlash的ECC监控l 1.1. PFlash的监控使能 1.2. PFlash的故障注入 2.DFlash的ECC监控 2.1. DF0 ECC Read Register 2.2. DF0 ECC Status Register 2.3. DF0 ECC Control Register 2.4. DF0 ECC Write Register 2.5. DF0 User Mode Control 3.总结 …

Centos 7系统一键安装宝塔教程

服务器推荐青鸟云服务器&#xff0c;2H2G低至16元/月 官网地址&#xff1a; 所有产品_香港轻量云 2核 2G-A型_青鸟云 推荐Finalshell软件连接至服务器&#xff0c;下载地址&#xff1a; https://dl.hostbuf.com/finalshell3/finalshell_windows_x64.exe 下载完成后连接服务…

知识吾爱纯净版小程序系统 leibiao SQL注入漏洞复现(XVE-2024-30663)

0x01 产品简介 知识吾爱纯净版小程序系统是一款基于微信小程序平台开发的知识付费应用,旨在帮助用户快速建立自己的知识付费平台,实现支付变现和流量主收益。它提供了简洁明了的用户界面和良好的用户体验,同时注重用户隐私保护,确保用户信息的安全存储和传输。 0x02 漏洞…

HTML前端页面设计静态网站

浅浅分享一下前端作业&#xff0c;大佬轻喷~ <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>一个网…

各大自媒体平台的感受 —— 小红书

如果你有些内容&#xff0c;想发布到自媒体平台上&#xff0c;目前有很多平台可用选择。 发布自媒体在获得关注的同时&#xff0c;同时也会期望有些收益。 我们的内容也发布了不少时间了&#xff0c;那就分别扒下各大自媒体平台的吃相&#xff0c;这篇文章说的是小红书。 收…

openEuler下配置openGauss环境图解

一、在openEuler中创建用户&#xff0c;并授予权限 # 创建用户 sudo adduser omm# 授予权限 chown omm /opt# 切换用户 su - omm 二、在openGauss官网找到openGauss极简版的软件包 openGauss软件 | openGauss下载 | openGauss软件包 | openGauss社区 右键立即下载&#xff0…

RabbitMQ 的集群

大家好&#xff0c;我是锋哥。今天分享关于【RabbitMQ 的集群】面试题&#xff1f;希望对大家有帮助&#xff1b; RabbitMQ 的集群 RabbitMQ 是一种流行的开源消息代理&#xff0c;广泛用于构建分布式系统中的消息队列。随着应用程序规模的扩大&#xff0c;单一的 RabbitMQ 实…

[ 应急响应基础篇 ] Windows 写入任务计划程序详解--任务计划程序窗口写入 命令行写入

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…

flinksql-Queries查询相关实战

分组聚合 --分组集 --GROUPING SETS() 允许你定义特定的分组方式&#xff0c;这样你可以选择只对感兴趣的分组进行计算。 --通过手动指定不同的分组组合&#xff0c;你能够灵活地控制数据的聚合结果。 --与 ROLLUP 和 CUBE 不同&#xff0c;GROUPING SETS 不会自动生成所有子集…

泄密与间谍:网络安全与国家安全的紧密联系

在当今数字化时代&#xff0c;网络安全已成为国家安全的重要组成部分。随着信息技术的迅猛发展&#xff0c;网络空间的安全问题愈发突出&#xff0c;泄密和间谍活动对国家安全构成了严峻挑战。本文将探讨泄密与间谍活动的影响&#xff0c;以及为何没有网络安全就没有国家安全。…

使用Git LFS管理大型文件

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 使用Git LFS管理大型文件 引言 Git LFS 简介 安装 Git LFS 安装 Git 安装 Git LFS 配置 Git LFS 初始化 Git 仓库 指定需要使用…

C语言300行-投篮

&#xff03;include "stdio。h" &#xff03;ifdef __APPLE__ #include 〈GLUT/glut。h〉 &#xff03;else &#xff03;include <GL/glut.h> #endif #include 〈stdlib.h> #include <stdio。h〉 #include <math.h〉 #include 〈windows.h〉…

深度学习-梯度消失/爆炸产生的原因、解决方法

在深度学习模型中&#xff0c;梯度消失和梯度爆炸现象是限制深层神经网络有效训练的主要问题之一&#xff0c;这两个现象从本质上来说是由链式求导过程中梯度的缩小或增大引起的。特别是在深层网络中&#xff0c;若初始梯度在反向传播过程中逐层被放大或缩小&#xff0c;最后导…

Linux运行Java程序,并按天输出日志

我们写好的Java程序&#xff08;非web项目&#xff09;&#xff0c;直接在服务器中敲入命令 java -jar app_name.jar就可以运行&#xff0c;如果想让程序一直在服务器中运行&#xff0c;则需要nohup命令。如下&#xff1a; nohup java -jar app_name.jar 如果需要将程序输出的日…

【Clikhouse 探秘】ClickHouse 物化视图:加速大数据分析的新利器

&#x1f449;博主介绍&#xff1a; 博主从事应用安全和大数据领域&#xff0c;有8年研发经验&#xff0c;5年面试官经验&#xff0c;Java技术专家&#xff0c;WEB架构师&#xff0c;阿里云专家博主&#xff0c;华为云云享专家&#xff0c;51CTO 专家博主 ⛪️ 个人社区&#x…

electron + vue 打包完成后,运行提示 electrion-updater 不存在

electron vue 打包完成后&#xff0c;运行提示 electrion-updater 不存在 检查配置&#xff1a; electrion-updater 放在 dependencies 下你的项目 package.json 与 electron 的 package.json 是否共用同一个&#xff0c;如果不是&#xff0c; electrion-updater 放在 elect…

【Android】Java开发语言规范

Java语言规范 命名风格 **类名&#xff1a;**使用 UpperCamelCase 风格&#xff0c;必须遵从驼峰形式&#xff0c;但以下情形例外&#xff1a;DO / BO / DTO / VO / AO&#xff0c;所有单词的首字母大写**方法名、参数名、成员变量、局部变量&#xff1a;**统一使用 lowerCam…