Java集合

Java集合

前言

文本源自微博客 (www.microblog.store),且已获授权.

一、接口继承关系和实现

集合类存放于Java.util包中,主要有3种:set(集)、list(列表包含Queue)和map(映射)

  1. Collection: Collection是集合List、Set、Queue的最基本的接口
  2. Iterator:迭代器,可以通过迭代器遍历集合中的数据
  3. Map:是映射表的基础接口

二、List

Java的List是非常常用的数据类型。List是有序的Collection。JavaList一共有三个实现类,分别是:ArrayListVectorLinkedList

2.1 ArrayList(数组)

ArrayList是最常用的List实现类,内部是通过数组来实现的,它允许对元素进行快速随机访问。数组的缺点是每隔元素之间不能有间隙,当数组大小不满足时需要增加存储能力,就要将已有数组的数据复制到新的存储空间中。当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动,代价比较高。因此,它适合随机查找和遍历,不适合插入和删除

2.2 Vector(数组实现、线程同步)

Vector和ArrayList一样,也是通过数组实现的,不通的是它支持线程的同步,即某一时刻只有一个线程能够写Vector,可以避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此访问速度比ArrayList慢。

2.3 LinkList(链表)

LinkedList是用链表结构存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度比较慢。另外,他还提供了List接口中没有定义的方法,专门用于操作表头和表尾元素,可以当做堆栈队列双向队列使用。

三、Set

Set注重独一无二的性质,该体系集合用于存储无序(存入和取出的顺序不一定相同),值不能重复。对象的相等性本质是对象的hashCode值java是依赖对象的内存地址计算出来的)判断的,如果想要让两个不同的对象视为相等,必须重写Object的hashCode方法和equals方法。

3.1 HashSet(Hash表)

哈希表存放的是哈希值。HashSet存储元素的顺序并不是按照存入时的顺序(和List显然不同)而是按照哈希值来存的所以取数据也是按照哈希值来取的。元素的哈希值是通过元素的hashcode方法来获取的,HashSet首先判断两个元素的哈希值,如果哈希值一样,接着会比较equals方法,如果equals结果为true,HashSet就视为同一个元素,如果equals为false,就不是同一个元素。

哈希值相同equals为false的元素如何存储呢?就是在同样的哈希值下顺延(可以认为哈希值相同的元素放在同一个哈希桶中)。也就是哈希一样的存一列。如下图1表示hashCode值不相同的情况;图2表示hashCode值相同但是equals不同的情况:

HashSet通过hashCode值来确定元素在内存中的位置。一个hashCode位置上可以存储多个元素

3.2 TreeSet(二叉树)

  1. TreeSet()是使用二叉树的原理对新add()的对象按照指定的顺序来排序(升序、降序),每增加一个车对象都会进行排序,将对象插入的二叉树指定的位置
  2. Integer和String对象都可以进行默认的TreeSet排序,而自定义的对象是不可以的,自定义的类必须实现*Comparable*接口,并且覆写相应的compareTo()函数,才可以正常使用
  3. 在覆写compare()函数时,要返回响应的值才使TreeSet按照一定的规则来排序
  4. 比较对象指定对象的顺序。如果该对象小于等于大于指定对象,则分别返回负整数、正整数

有两种方法可以对自定义对象进行排序:

  1. 重写compareTo()方法

    java 复制代码
    @Data
    @Accessors(chain = true)
    public class Article implements Comparable<Article> {
    
        private static final long serialVersionUID = 1L;
    
        private int id;
        
        private String userId;
        
         /**
         * 重写compareTo方法,实现排序
         */
        @Override
        public int compareTo(Article article) {
            return Integer.compare(this.id,article.getId());
        }
    }
  2. 使用匿名函数对TreeSet实现:

    java 复制代码
    //自定义排序规则(逆序,通过交换p1、p2的位置来实现顺序或者逆序)
    TreeSet<Article> set = new TreeSet<>((p1,p2)->Integer.compare(p2.getId(), p1.getId()));

3.3 LinkHashSet(HashSet+LinkedHashMap)

对于LinkHashSet而言,它继承于HashSet、又基于LinkedHashMap来实现的。LinkHashSet底层使用LinkedHashMap来保存所有元素,它继承于HashSet,其所有的方法操作上又于HashSet相同,因此,LinkedHasSet的实现上非常简单,只提供了四个构造方法,并通过传递一个标识参数,调用父类的构造器,底层构造一个LinkedHashMap来实现,在相关操作上与父类HashSet相同,直接调用父类HashSet的方法即可。

四、Map

4.1 HashMap(数组+链表+红黑树)

HashMap根据 键的hashCode存储数据,大多数情况下,可以直接定位到它的值,因而具有很快的访问速度,但是遍历顺序却是不确定的。HashMap最多只允许一条记录的键为null,允许多条记录的值为null。HashMap非线程安全,即任一时刻可以有多个线程同时写HashMap。可能会导致数据的不一致。如果需要满足线程安全,可以使用Collection是的synchronizedMap方法使HashMap具有线程安全的能力,或者使用ConcurrentHashMap。我们用下图来介绍HashMap的结构。

4.1.1 Java7实现

大方向上,HashMap里面是一个数组,然后数组中每个元素是一个单向链表。上图中,每个绿色的实体是嵌套类Entry的实例,Entry包含四个属性:keyvaluehash值、和用于单向链表的next

  1. capacity:当前数组容量,始终保持 2n,可以扩容,扩容后数组大小为当前的2倍
  2. loadFactor:负载因子,默认为0.75
  3. threshold:扩容的阈值,等于 capacity * loadFactory
4.1.2 Java8实现

Java8对HashMap进行了一些修改,最大的不同就是利用了红黑树,所以其由 数组+链表+红黑树组成。

根据Java7 HashMap的介绍,我们知道,查找的时候,根据hash值我们能够快速定位到数组的具体下标,但是之后的话,需要顺着链表一个个比较下去才能找到我们需要的,时间复杂度取决于链表长度,为O(n)。 为了降低这部分开销,在Java8中,当链表中的元素超过8个以后,会将链表转换为红黑树,在这些位置进行查找的时候,可以降低时间复杂度为 O(logN)。

4.2 ConcurrentHashMap

4.2.1 Segment段

ConcurrentHashMap和HashMap的思路是差不多的,但是因为它支持并发操作,所以要复杂一些。整个ConcurrentHashMap由一个个Segment组成,segment代表"部分"或"一段"的意思。所以很多地方都将其描述成分段锁。下文中,使用"槽"来代替一个segment。

4.2.2 线程安全(Segment继承ReentrantLock加锁)

简单理解就是,ConcurrentHashMap是一个Segment数组Segment砼工程继承ReentrantLock来进行加锁,所以每次需要加锁的操作锁住的是一个Segment,这样只要保证每个Segment是线程安全的,也就实现了线程安全。

4.2.3 并行度(默认16)

concurrencyLevel: 并行级别、并发数、Segment数,怎么翻译不重要,理解他。默认是16,也就是说,ConcurrentHashMap有16个Segments,所以理论上,这个时候,最多可同时支持16个线程并发写,只要他们的操作分别分布在不通的Segment上。这个值可以在初始化的时候设置为其他,但是一旦初始化以后,它是不可以扩容的。再具体到每个Segment内部,其实每个Segment很像之前介绍的HashMap,不过它要保证线程安全,所以处理起来要麻烦些。

4.2.4 Java8实现(引入了红黑树)

Java8对ConcurrentHashMap进行了比较大的改动,java8也引入了红黑树。

4.3 HashTable(线程安全)

HashTable是遗留类,很多映射的常用功能与HashMap类似,不通的是HashTable继承自Dictionary类,并且是线程安全的,任一时间只有一个线程能写HashTable,并发性不如ConcurrentHashMap,因为ConcurrentHashMap引入了分段锁。HashTable不建议在新代码中使用,不需要线程安全的场合可以用HashMap替换,需要线程安全的场合可以用ConcurrentHashMap替换。

4.4 TreeMap(可排序)

TreeMap实现了SortedMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排列,也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。

如果使用排序的映射,建议使用TreeMap;在使用TreeMap时,key必须实现 Comparable接口或者在构造TreeMap时传入自定义 的Comparable,否则在运行时会抛出java.lang.ClassCastException类型的异常.

4.5 LinkHashMap(记录插入顺序)

LinkHashMap是HashMap的一个子类,保存了记录的插入顺序,在用Iterator遍历LinkHashMap时,先得到的记录肯定是先插入的,也可以在构造时带参数,按照访问持续排列。

相关推荐
code_std19 小时前
保存文件到指定位置,读取/删除指定文件夹中文件
java·spring boot·后端
小许学java19 小时前
Spring事务和事务传播机制
java·数据库·spring·事务
大学生资源网19 小时前
基于Javaweb技术的宠物用品商城的设计与实现(源码+文档)
java·mysql·毕业设计·源码·springboot
汤姆yu19 小时前
基于springboot的热门文创内容推荐分享系统
java·spring boot·后端
星光一影19 小时前
教育培训机构消课管理系统智慧校园艺术舞蹈美术艺术培训班扣课时教务管理系统
java·spring boot·mysql·vue·mybatis·uniapp
lkbhua莱克瓦2419 小时前
MySQL介绍
java·开发语言·数据库·笔记·mysql
武昌库里写JAVA19 小时前
在iview中使用upload组件上传文件之前先做其他的处理
java·vue.js·spring boot·后端·sql
董世昌4119 小时前
什么是事件冒泡?如何阻止事件冒泡和浏览器默认事件?
java·前端
好度19 小时前
配置java标准环境?(详细教程)
java·开发语言
teacher伟大光荣且正确20 小时前
关于Qt QReadWriteLock(读写锁) 以及 QSettings 使用的问题
java·数据库·qt