前言

对于每个从事Java开发的程序员来讲,说到集合并不会陌生,但是要把Java中的集合都罗列出来并说出用法并非那么容易,因为在平常的Java开发过程中基本上只会用到其中的几个。那么Java的集合到底有哪些呢?下面让我们一起来探索一下。

一、集合框架体系图

集合分为两大接口,分别是Collection和Map。Collection有三个子接口,分别是Set、List、Queue,这三个子接口下面又有一些抽象类以及各个抽象类下面的具体实现类。那么这些集合都有什么特点呢?下面我们以Set、List、Map这三大块中几个常用集合来说明。

二、Set

  • HashSet
  • 无序、唯一。依靠hashCode()和equals()来保证唯一
  • HashSet能存null值,这也是它的一个特点
  • 从下图HashSet的几个构造函数中我们不难看出它的底层是一个HashMap
public HashSet() { map = new HashMap<>();}public HashSet(Collection extends E> c) { map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16)); addAll(c);

}

public HashSet(int initialCapacity, float loadFactor) { map = new HashMap<>(initialCapacity, loadFactor);

}

public HashSet(int initialCapacity) { map = new HashMap<>(initialCapacity);}
  • TreeSet
  • 树形集合,唯一,有序
  • 继承于AbstractSet,并实现了NavigableSet
  • 有自然排序、自定义比较器排序两种排序方式
  • 自然排序
  • 1、被排序的类需要实现Comparable接口
  • 2、重写其中的compareTo方法
  • 自定义比较器排序
  • 1、这种方法需要一个新的类实现Comparator接口并重写其中的compare方法
  • LinkedHashSet
  • 有序、唯一
  • 非线程安全
  • 底层是ListHashMap,所以既有链表的特点(有序),也有哈希表的特点(唯一)

三、List

List结构图

  • ArrayList
  • 有序、非唯一。比较常用的集合类
  • 非线程安全
  • 从下图ArrayList的几个构造函数中我们不难看出它的底层是一个动态数组。它具有查询快,效率高,增删慢,每个元素之间不能有间隔的特点
public ArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); }}public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}public ArrayList(Collection extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; }}
  • LinkedList
  • 有序,非唯一
  • 底层数据结构是链表,查询比ArrayList慢,增删比ArrayList快,效率高
  • 非线程安全

四、Map

Map结构图

  • HashMap
  • 无序、键唯一、键值允许为null。这是比较常用的集合类之一
  • HashMap的工作原理:
  • 使用put存储对象到HashMap中
  • 当我们给put()方法传递键和值时
  • 先对键调用hashCode()方法
  • 返回的hashCode用于找到bucket位置来储存Entry对象
  • HashMap是在bucket中储存键对象和值对象,作为Map.Entry
  • 使用get(key)从HashMap中获取对象
  • rehashing(扩容)的时机和细节
  • HashMap的扩容机制是什么?什么时候会resize?
  • 向容器里添加元素时,会触发判断
  • 当前容器的元素个数,大于HashMap的阈值(当前数组的长度乘以加载因子的值),就会自动扩容
  • resize即扩容,重新计算容量,方法是使用一个新的数组代替已有的容量小的数组
  • HashMap默认大小:
  • 容量:指的是哈希表中桶的数量
  • 初始容量:16
  • 初始最大容量: 1 << 30(1<<30 表示1左移30位,每左移一位乘以2,所以就是1*2*30=1073741824)
  • 初始加载因子:0.75f
  • 阈值:容量 * 加载因子
  • HashMap执行put方法时,两个对象的hashcode相同会发生什么?
  • 因为hashcode相同,所以它们的bucket位置相同,哈希碰撞会发生
  • 因为HashMap使用链表存储对象,这个Entry(包含有键值对的Map.Entry对象)会存储在链表中
  • 因为HashMap在链表中存储的是键值对,使用get方法时,找到bucket位置之后,虽然存在同样的hashcode,但会调用keys.equals()方法去找到链表中正确的key节点,最终找到要找的值对象
  • HashMap不足之处
  • 高并发环境下数据容易出现错乱
  • 在JDK1.8之前的版本中,HashMap采用数组+链表实现,用链表处理冲突,同一hash值的链表都存储在一个链表里。但是当链表中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,HashMap采用数组+链表+R-B Tree(红黑树)实现,当链表长度超过阈值(8)时,将链表转换为R-B Tree(红黑树),这样大大减少了查找时间。结构示意图如下

结构示意图1

结构示意图2

结构示意图3

  • LinkedHashMap
  • 有序、键唯一,键值允许为null
  • 从下图LinkedHashMap的部分源码中可以看出,LinkedHashMap继承自HashMap,所以HashMap的特性它都有,不同的是LinkedHashMap是有序的,而HashMap是无序的
public class LinkedHashMap extends HashMap implements Map
  • HashTable
  • 数组 + 链表形式存储
  • 线程安全
  • 无论是key还是value都不允许有null值的存在,在HashTable中调用Put方法时,如果key为null,直接抛出NullPointerException异常
  • 遍历使用的是Enumeration列举
  • 同步阻塞型、性能开销较大
  • TreeMap
  • 无序、键唯一
  • TreeMap 是一个有序的key-value集合,非同步,基于R-B Tree(红黑树)实现,每一个key-value节点作为R-B Tree(红黑树)的一个节点
  • 有自然排序和比较器排序,这两种排序方式在TreeSet已经说明了,在这就不展开来讲了
  • ConcurrentHashMap
  • 线程安全。在并发编程中,ConcurrentHashMap是一个经常被使用的数据结构,相比于HashTable以及Collections.synchronizedMap(),ConcurrentHashMap在线程安全的基础上提供了更好的写并发能力,但同时降低了对读一致性的要求
  • ConcurrentHashMap为了提高本身的并发能力,在内部采用了一个叫做Segment的结构,一个Segment其实就是一个类Hash Table的结构,Segment内部维护了一个链表数组,用下面这一幅图来看下ConcurrentHashMap的内部结构
  • 从上图我们可以看出,ConcurrentHashMap定位一个元素的过程需要进行两次Hash操作,第一次Hash定位到Segment,第二次Hash定位到元素所在的链表的头部,因此,这一种结构的带来的副作用是Hash的过程要比普通的HashMap要长,但是带来的好处是写操作的时候可以只对元素所在的Segment进行加锁即可,不会影响到其他的Segment。
  • 从代码层面上我们来看下是怎么实现的,以get方法为例。从下面的代码中,我们可以看出segmentFor这个函数用于确定操作应该在哪一个segment中进行,这个函数用了位操作来确定Segment,根据传入的hash值向右无符号右移segmentShift位,然后和segmentMask进行与操作,可以得出以下结论:假设Segment的数量是2的n次方,根据元素的hash值的高n位就可以确定元素到底在哪一个Segment中。
public V get(Object key) {  int hash = hash(key.hashCode());  return segmentFor(hash) .get(key, hash);}final Segment segmentFor(int hash) {  return segments[(hash >>> segmentShift)  & segmentMask];}V get(Object key, int hash) { if (count != 0) { // read-volatile HashEntrye = getFirst(hash); while (e != null) { if (e.hash == hash && key.equals(e.key)) { V v = e.value; if (v != null) return v; return readValueUnderLock(e);  } e = e.next; } } return null;}

五、尾言

Java的集合类还是蛮多的,除了上述讲的这些,还有一些比较不常用或者已经被弃用的集合类没有做扩展,感兴趣的小伙伴们可以找Java Api文档来看,希望本文能给想学Java或者刚学Java的小伙伴们提供一点小小的启发~

想了解更多私信加关注回复学习 免费领取最新学习资料

java map排序_探索Java常用集合相关推荐

  1. java map迭代_在Java中对Map进行迭代

    #概述 本文,我们将了解一下在Java中迭代Map各种不同方法. 简单来说,我们可以使用keySet().valueSet()或entrySet()来提取Map的内容.因为这些都是Sets,所以类似的 ...

  2. java map替换_在java的Map集合中,怎样更改value的值

    展开全部 map集合没有专门更改value的方法,更改value的方法就是636f70793231313335323631343130323136353331333436316363map.put(k ...

  3. java 头尾 队列_探索JAVA并发 - 并发容器全家福

    14个并发容器,你用过几个? 不考虑多线程并发的情况下,容器类一般使用ArrayList.HashMap等线程不安全的类,效率更高.在并发场景下,常会用到ConcurrentHashMap.Array ...

  4. java linkedlist排序_用Java对用户定义对象的LinkedList进行排序

    要使用Java对列表进行排序,可以使用sort(List list) 方法.此方法可以对所有元素必须实现Comparable接口的列表进行排序. 在下面的示例中,House类是用户定义的.为了使其具有 ...

  5. java 查找排序_数据结构(Java)——查找和排序(1)

    1.查找的定义 查找是这样一个过程,即在某个项目组中寻找某一指定目标元素,或者确定该组中并不存在该目标元素. 对其进行查找的项目的组有时也成为查找池. 两种常见的查找方式:线性查找和二分查找. 为了能 ...

  6. java平均分排序_编写java程序,输入10个成绩,计算最高分,最低分,平均分,并按从小到大排序,最后统计高于平均分的人有多少?...

    展开全部 代码如下:import java.util.ArrayList; import java.util.List; import java.util.Scanner; public class  ...

  7. java字母排序_【Java】实现按中文首字母排序 | 学步园

    要实现"按中文首字母排序"操作,可以使用java.util包下的Arrays类的sort()函数. Arrays类包含用来操作数组(比如排序和搜索)的各种方法. 比如对于排序操作的 ...

  8. java 外部排序_完整java实现外部排序

    外部排序指的是大文件的排序,即待排序的记录存储在外存储器上,待排序的文件无法一次装入内存,需要在内存和外部存储器之间进行多次数据交换,以达到排序整个文件的目的.选自百度百科. 第一步:      首先 ...

  9. java map映射_【Java】Map 映射接口 概述

    Map 映射接口 概述 Map是一个双列数据,存储K-V类型的数据 JDK1.2 - HashMap 是目前Map的主要实现类 JDK1.2 线程不安全的,效率高,可存储null的key和value ...

  10. java map 查找_在Java TreeMap中查找元素位置

    我正在使用StrMap TreeMap< String,String>的TreeMap,并使用它来实现词典. 然后我有一个文件的集合,并且想要在字典定义的向量空间(单词的空格)中创建每个文 ...

最新文章

  1. 顶尖程序员不同于常人的 5 个区别
  2. 07构建个人博客网站
  3. CTFshow 文件上传 web153
  4. 面试 -- 多线程( 一) -- 基础
  5. linux下如何使用有道词典
  6. 杨清彦:《像三国》游戏3D动效制作经验分享
  7. Django-Model中的ForeignKey,ManyToManyField与OneToOneField
  8. 动态规划训练5 [回文词]
  9. 桶排序/Bucket Sort
  10. 数学6年级测试软件,苏教版数学六年级下册2018年小学六年级毕业测试试卷(无答案).doc...
  11. 来自雨林木风的Linux发行版: Ylmf Linux
  12. Linux基础:systemctl和journalctl常用命令
  13. 【CDN学习笔记5】源站IP变更后导致图片显示不出来的案例
  14. 随机生成小球(HTML5)
  15. Python计算圆周长和面积
  16. html页面禁止竖屏,关于移动端页面强制竖屏的方法
  17. OpenGL学习笔记:颜色
  18. 程序员如何避免「温水煮码农」
  19. 优质文章为什么对网站推广这么重要
  20. Fanuc发那科法兰克数据采集实战c#——CNC数控系统数据采集、西门子免授权数据采集方案

热门文章

  1. java io 常用类库_JDK 中需要掌握的常用类库
  2. Ubuntu 下的根目录为
  3. 如何以管理员身份运行电脑
  4. day23 02 组合(继续人狗大战游戏)
  5. python 使用.bat文件自动执行
  6. ORACLE错误6650
  7. vim源码编译启用python
  8. HDU 2296 Ring -----------AC自动机,其实我想说的是怎么快速打印字典序最小的路径...
  9. LTT (Linux Trace Toolkit) 简介
  10. 微软的_tell函数实现