本文欢迎转载,转载前请联系作者,经允许后方可转载。转载后请注明出处,谢谢! http://blog.csdn.net/colton_null 作者:喝酒不骑马 Colton_Null from CSDN


如果一个程序只有包含固定数量的且其生命周期都是已知的对象,那么这是一个非常简单的程序。

一、引言

我们在编程的时候,有时候在程序运行中需要根据当时的情况去创建对象,在此之前可能不知道对象的数量或者确切的类型。例如,我们在根据某些条件去查询数据库后,返回的条数不一定。那么这个时候,就需要用到一个东西(我们暂且称之为容器),用它来装载数量不固定的、类型不固定的对象。

数组的存在,可以解决其中一部分问题。数组是保存一组对象最有效的方式,如果你想保存一组基本数据类型,官方也是推荐使用这个方式去存储数据。但是数据的弊端是,它具有固定的大小。也就是说,在我们编写代码创建数组的时候,必须要给定数据的大小。给定后,这个数据的大小就不能被改变了。所以它麻烦在于我们有时候事先不知道需要存放都少对象(例如前面起到的查库,返回的条数不确定),而且一旦数组大小定小了,数组的存放个数还会受到限制。

于是,Java为我们提供了一套完整且好用的容器,来解决上述问题,我们称之为“集合”。

二、集合简介

集合类存放在java.util包中。主要的类型一共有四种:List(列表)、Set(集)、Queue(队列)、Map(映射)。其中主要有两大接口,分别是Collection和Map。其中List、Set、Queue实现了Collection接口。

偷一张图哈

这张图可以说非常经典了。

Tips:实线边框的是实现类,折线边框的是抽象类,而点线边框的是接口。带有空心箭头的点线表示一个特定的类实现了一个接口,实心箭头表示某个类可以生成箭头所指向类的对象。比如,Collection可以生成Iterator。

图中标粗的类(HashSet、ArrayList、LinkedList、HashMap)是比较常用的集合。

那么这些集合(容器)都是干什么用的呢?他们各自又有什么特性呢?

三、集合基本概念

首先在搞清楚常用集合之前,先说说有关集合的基本概念。

前面我们说到,在程序运行过程中,对于不确定数量、不确定类型的对象的存储,我们可以用集合来解决。也就是说,我们可以通过用这些集合,来方便的存储我们想要的对象。当然了,集合对对象的存储,并不是真正把对象塞到容器里来,那是通过什么方式存储对象的呢?这个我们后面再谈。

根据两大集合接口Collection和Map,我们可以把集合也分为两种不同的概念来探讨。

1.Collection。

它是一个序列,可以想象学校里学生们站排这个场景。每个学生就是一个对象,这个排就是一个Collection集合。不同的实现类,对于它所存储的对象的规则要求也不同。List、Queue须按照插入的顺序存储元素,而Set则不能有重复元素。这里简单说一下Queue,它实际上是对队列这种数据结构的一种实现,典型特性就是先进先出(FIFO)。Queue接口提供了offer(Object e)、peek()、poll()等方法对有关队列概念的操作进行实现。

2.Map。

它是一组成对的“键值对”对象,可以通过键来查找值。这就像班级的座位表,任课老师来班级上课,通过座位表上“第三排第五座”就能找到对应的学生“马叨叨”。最神奇的是,我们不光可以用类似“第三排第五座”这样的文字(String)来寻找值,我们也可用通过对象来寻找值。也就是说,Map中的键值都可以是对象。我们称这种对应关系为“映射表”。

四、常用集合总结

1.List

a.ArrayList

它在随机访问元素,即用角标查找数据的时候比较快,但是在元素的插入和删除操作上速度慢一些。

b.LinkedList

而LinkedList在元素插入删除操作上速度较快,但是在随机访问的操作上相对较慢。

对此,我编写了一段程序来测试这个说法。测试思路:分别用插入十万条数据到ArrayList和LinkedList中,以及十万条数据的随机读取,计算每次任务执行的时间差。

public class ListDemo {// 列表长度定义private static final int LIST_SIZE = 100000;public static void main(String[] args) {List arrayList = new ArrayList(LIST_SIZE);List linkedList = new LinkedList();// 测试arraylist增加数据时间long timestart1 = System.currentTimeMillis();System.out.println(timestart1 + ":arrayList开始插入数据");for(int i = 0; i < LIST_SIZE; i++) {// 每次都将随机数插入到列表的第0位arrayList.add(0, Math.random());}long timeend1 = System.currentTimeMillis();System.out.println(timeend1 + ":arrayList结束插入数据");System.out.println("时间差(ms):" + (timeend1 - timestart1) + "\n");// 测试linkedlist增加数据时间long timestart2 = System.currentTimeMillis();System.out.println(timestart2 + ":linkedlist开始插入数据");for(int i = 0; i < LIST_SIZE; i++) {// 每次都将随机数插入到列表的第0位linkedList.add(0, Math.random());}long timeend2 = System.currentTimeMillis();System.out.println(timeend2 + ":linkedlist结束插入数据");System.out.println("时间差(ms):" + (timeend2 - timestart2) + "\n");// 测试arrayList读取数据时间long timestart3 = System.currentTimeMillis();System.out.println(timestart3 + ":arraylist开始读取数据");for(int i = 0; i < LIST_SIZE; i++) {arrayList.get((int) Math.random() * LIST_SIZE);}long timeend3 = System.currentTimeMillis();System.out.println(timeend3 + ":arraylist结束读取数据");System.out.println("时间差(ms):" + (timeend3 - timestart3) + "\n");// 测试linkedList读取数据时间long timestart4 = System.currentTimeMillis();System.out.println(timestart4 + ":linkedlist开始读取数据");for(int i = 0; i < LIST_SIZE; i++) {linkedList.get((int) Math.random() * LIST_SIZE);}long timeend4 = System.currentTimeMillis();System.out.println(timeend4 + ":linkedlist结束读取数据");System.out.println("时间差(ms):" + (timeend4 - timestart4) + "\n");}
}

最后的测试结果是如下图所示:

从图中我们可以看出,在元素插入的效率上,LinkedList要远快于ArrayList;在元素随机读取的效率上,ArrayList偏快一些。

那么为什么会这样呢?

实际上,ArrayList是实现了基于动态数组的数据结构,而LinkedList基于链表的数据结构。对于随机访问get和set,ArrayList用数组角标定位元素,而LinkedList则要移动指针才能定位元素。 前者的时间复杂度为O(1),后者为O(n)。

但对于插入和删除操作add和remove,LinkedList就占有一定的优势了,因为ArrayList要移动数据,而LinkedList修改指针指向就ok了。所以前者的增删操作时间复杂度为O(n),后者为O(1)。

所以,我们要根据不同的场景,选择合适的List来完成我们的需要。当我们需要频繁随机读取数据时,首选ArrayList。当我们需要在列表中间频繁插入、移除数据时,首选LinkedList。

另外,LinkedList还添加了可以使其用作栈、队列或双向队列的方法。比如getFirst()、element()、peek()等。这些方法在Java API中都有详细的介绍。

2.Set

Set具有和Collection完全一样的接口,不像List。Set和List最大的区别在于,Set是唯一的、无序的。“唯一的”表示Set不保存重复的原色,“无序的”表示Set中元素的存储是没有顺序的,不过如果真想要保持元素的顺序可以用TreeSet。所以对于Set,最常被使用的简单应用就是测试归属性,即判断某个元素是不是在Set集合里。

对于Set,一般最常使用的就是HashSet,它专门对快速查找操作进行了优化。因为它的底层数据结构为散列表(即哈希表),有关散列的内容,我会在另外一篇文章中详细介绍。

3.Queue

通常情况下,LinkedList可以被用作Queue的一种实现,因为它实现了Queue的接口。不过还有一个类,叫PriorityQueue,它是一个比较标准的队列实现类,但不是绝对标准。因为PriorityQueue保存队列元素的顺序并不是按加入队列的顺序,而是按队列元素的大小进行重新排序。因此当调用peek()、pull()方法来取出队列中的元素时,并不是取出最先进入队列的元素,而是取出队列中最小的元素。这其实违背了先进先出(FIFO)规则。

4.Map

最后,终于到大家都熟悉的Map了。

a.HashMap

HashMap是最为常见的Map了,一般初学者学习的时候都是从HashMap开始,然后有些人就一直使用HashMap不知道其它MapleStory了……HashMap的是根据键的HashCode值来存储数据,根据键可以直接获取它的值,访问速度极快,但数据的存储是无序的。其底层数据结构在Java 1.8后为数组+链表+红黑树(之前是数据+链表,1.8后对结构进行了优化,在链表数量大于8后,用红黑树存储数据,体现就是查询速度更快了,有关这块的内容我也会用单独的篇幅来跟大家一起探讨)。

HashMap允许key和value都为null。

在线程安全方面,HashMap是线程不安全的。如果需要线程安全的Map,可以使用ConcurrentHashMap。

b.LinkedHashMap

LinkedHashMap保存了元素插入Map时的顺序。通常情况下,LinkedHashMap的遍历要慢于HashMap。当然也有特殊情况,比如在HashMap容量巨大但是存储数据较少的时候,遍历会慢于LinkedHashMap。因为LinkedHashMap遍历只和数据数量有关,与容量无关;而HashMap的遍历和它的容量有关。

c.TreeMap

TreeMap实现了SortMap接口,所以它可以根据键进行排序。它默认的排序规则是升序排序,当然开发者也可以指定排序比较器,修改排序规则。

有人可能会提到HashTable。HashTable与HashMap类似,不过它现在已经过时了,变成了遗留类而已。要说它的区别就是,它不允许key和value为null。另外它是个线程安全的Map。不过现在如果想要HashMap线程安全的话,建议使用ConcurrentHashMap。因为HashTable已经被淘汰了,当数据增加到一定程度的时候,效率太低。

—————手动分割线—————

最后说一下前面丢出的一个问题。之前我们提到,集合对对象的存储,并不是把对象塞到容器中,那它是通过什么方式存储对象呢?实际上,集合中存放的是对象的引用,学过C语言同学可能会更好理解,这个引用可以理解为就是记录对象存放地址的指针。根据引用,我们就能找到对象在内存中的存放位置,从而获得对象。而并非在内存中把对象实际装载到某个容器中。

五、总结

  1. Map保存的是相关联的键值对,而Collection保存的是单一的元素。
  2. List和数组的区别在于,List能够自动扩充容量。
  3. 对于Collection集合,需要唯一性我们要想到Set,根据排序与否选择TreeSet和HashSet。如果不需要唯一性则要想要List,如果查询多则使用ArrayList,增删多聚选择LinkedList。
  4. 对于Queue、Stack等数据结构的实现,用LinkedList即可。
  5. 对于Map集合。一般使用HashMap,如果需要排序则使用TreeMap,如果需要保持元素插入顺序则使用LinkedHashMap,如果需要线程安全的Map则使用ConcurrentHashMap。
  6. 过时的类就不要使用了,比如HashTable、Vector、Stack等。

站在前人的肩膀上前行,感谢以下博客及文献的支持。

Java中ArrayList和LinkedList区别 时间复杂度 与空间复杂度
java集合之Set与List总结
Java中的集合Queue、LinkedList、PriorityQueue(四)
《Java编程思想(第四版) 机械工业出版社》

Java-Collection集合和Map集合总结相关推荐

  1. Collection集合和Map集合循环遍历三种方法

    Collection集合的四种遍历方式: 1.迭代器 public static void main(String[] args) {List<String> list = new Arr ...

  2. 1-17 Set集合和Map集合

    Hash表 Hash,一般翻译做"散列",也有直接音译为"哈希"的,它是基于快速存取的角度设计的,也是一种典型的**"空间换时间"**的做法 ...

  3. Set集合和Map集合

    Set接口和Map接口 回顾 1 集合概念,用来存储一组数据的容器.和数组类似,数组是长度固定的,集合长度可以变化.数组能存储基本类型和引用类型,集合只能存储引用类型. 2 Collection接口, ...

  4. JavaSE_day14Map集合和Map集合的实现类

    Map集合:存在键值关系的集合,称为Map集合 之前谈到的Set,Collection集合他是一个单列的集合,你可以根据某一个对象的属性来找出某个想要的元素,那么今天所说的Map集合他是一个双列集合, ...

  5. 如何判断List 集合和Map 集合是否为空

    1:废话少说,直接上代码 //使用CollectionUtils Spring 包下的工具类List<Object> list = new ArrayList<>();if ( ...

  6. Map集合和List集合总结

    Map集合和List集合哪个效率更高 List接口 List集合是一个元素有序(存储有序).可重复的集合,集合中的每个元素都有对应的索引,以便于查询和修改,List集合是允许存储null值的. Lis ...

  7. Day_8/08(List集合和Set集合)

    目录 一.List集合 1.ArrayList集合 2.LinkedList集合 1.增加元素 2.查询元素 3.修改元素 4.删除元素 二.Set集合 1.HashSet集合 2.LinkedHas ...

  8. 判断集合和map是否为空

    之前一直以为判断集合和map都是用CollectionUtils即可,实际上不然. 1.CollectionUtils判断集合(collection/list/set)是否为空 CollectionU ...

  9. java对list中map集合中某个字段排序

    java对list中map集合中某个字段排序 适用于字符串排序,数字对比不可用,以下示例,当把sort的值改为74,66,142时排序不对看示例2 示例1 List<Map<String, ...

最新文章

  1. 干货分享:自动驾驶核心技术进展之车用毫米波雷达
  2. Java中的作用域有哪些
  3. 致北漂——你来北京不是为了配合出演苦情戏
  4. ASP.NET中 Repeater嵌套
  5. HTTP权威指南记录 ---- HTTP概述
  6. 朴素贝叶斯--实战分析
  7. 并发量,QPS,TPS,看这一篇就够了
  8. 组播协议——IGMP v2报文头介绍
  9. 协同办公 团队协作网站推荐(一)
  10. Oracle 11gR2 中 示例用户 安装说明
  11. VC2013生成的程序兼容WindowsXP操作系统
  12. java8判断对象是否为空新写法
  13. Android Protect-0.重新打包和签名
  14. android textview表情,android如何在textview或edittext上添加表情
  15. 项目落地 - 智慧海绵城市(物联网技术应用)
  16. 充电电池的充放电电流-0.2C、1C、2C的含义
  17. 互联网测试必须要知道的App稳定性测试
  18. 英雄会归来,做个人物小点评。
  19. 全干工程师神器 - Jmeter 06 - Jmeter后置处理器
  20. android 学习笔记③

热门文章

  1. numpy block创建分块矩阵
  2. KNIME 安装配置
  3. vue实现登录时的图片验证码(纯前端)
  4. 网络基础:(二)路由选择基础与静态路由
  5. 解决Mac无法关闭80端口,无法将hosts域名指向本地80端口
  6. html链接加入qq群,获取QQ群加群链接
  7. 2019牛客暑期多校训练营(第七场)-B Irreducible Polynomial(多项式因式分解)
  8. Visual Studio 2019的下载及安装
  9. 计算机专业硕士毕业论文,硕士毕业论文(计算机专业、自动化专业).doc
  10. js 中英文混合字符串 截取固定长度字节 方法封装