JAVA集合详解

1.集合和数组的区别

1.数组长度是固定的,集合长度是可变的

2.数组可以是基本数据类型也可以是引用数据类型,集合只能是引用数据类型

3.数组只能存储同一种数据类型,集合可以存储不同的数据类型

2.Collection集合中常见的方法

boolean  add(E,e)  在集合末尾添加元素

boolean  remove(Object o)  若集合中的值有与o相等的值,删除该元素,并返回true

bolean  contains(Object o)  判集合中是否保函某元素

boolean  isEmpty 判断集合是否为空

boolean addAll(Collection  c) 将一个类集c中的元素添加到另一个类集

int  size()  返回集合中的元素个数

object[ ] toArray( )返回一个包含集合中所有元素的数组,类型为object

Intreator  interator( ) 迭代器 ,集合中专用的遍历方式

clear()   清除集合中所有元素

3.常用集合的分类

1.Collection接口下的集合(单列集合)

List集合接口:元素按照进入先后有序保存,可重复

        ArrayList:底层数据结构是数组,查询快,增删慢,线程不安全,效率高,可以存储重复元素

        LinkedList 底层数据结构是链表,查询慢,增删快,线程不安全,效率高,可以存储重复元素

        Vector:底层数据结构是数组,查询快,增删慢,线程安全,效率低,可以存储重复元素

常用方法:

        void  add(int  index,object  obj)  在指定位置添加元素

        Object  remove  (int  index) 根据指定索引删除元素,并把删除的元素返回

      Object  set(int  index,object  obj)  把指定索引位置的元素修改为指定的值返回修改前的值

        Object  get(int  index) 获取指定索引位置的元素

        

Set集合 接口: 仅接收一次,不可重复,并做内部排序

   HashSet 使用hash表(数组)存储元素元素无序且唯一,线程不安全,效率高,可以存            储null元素,唯一性是靠所存储元素类型是否重写hashCode()和equals()方法来保证的

      实现唯一性的过程:

            存储元素会使用Hash算法生成一个HashCode值,和所存储元素的HashCode值比较

             如果不相等,则存储的两个对象一定不相等,此时存储当前的新的hashCode值处的               元素对象

              如果HashCode值相等,也不一定相等,会调用equals()方法判断两个对象的内容是                否相等 如果内容相等,那么就是同一个对象,无需存储,如果比较的内容不相等,                 那么就是不同的对象,就该存储,采用哈希的解决地址冲突算法,在当前   hashCode值处类似一个新的链表, 在同一个hashCode值的后面存储存储不同的对象,这样就保证了元素的唯一性。

     LinkedHashSet :底层数据结构采用链表和哈希表共同实现。链表保证了元素的顺序与存储顺序一致,哈希表保证了元素的唯一性,线程不安全,效率高。

    TreeSet 底层数据结构采用二叉树来实现,元素唯一且已经排好序;唯一性同样需要重写hashCode和equals()方法,二叉树结构保证了元素的有序性

List和Set总结:

   List特点:元素有放入顺序,元素可重复可以动态增长,查找元素效率高,插入删除元                           素效率低

   Set特点:元素无放入顺序,元素不可重复,重复元素会覆盖掉,检索元素效率低下,删除                      和插入效率高,插入和删除不会引起元素位置改变

Map集合(双列集合)

    Map用于保存具有映射关系的数据key和value,都可以使任何引用类型的数据,但key不能重复。所以通过指定的key就可以取出对应的value

Map集合常用方法:

boolean  containsKey(Object  key) 查询Map集合中是否包含指定的Key,如果包含就返回true

boolean  containsValue(Object value)查询Map集合中是否包含指定值,如果包含就返回true

boolean  isEmpty()查询map集合是否为空,为空返回true

boolean  put(Object key, Object value)添加一个键值对,如果已有key值就覆盖掉

boolean  remove(Object  key)删除指定key对应的键值对,返回可以关联的值,没有就为null

Object  get(Object value) 返回指定Key所对应的value,如果不包含就为null

Int  size() 返回该map里的键值对个数

Set  Key( ) 返回map中所有key组成的set集合

void  putAll( Map  m)将指定map集合中的键值对复制到map集合

HashMap和HashTable的区别

1.线程安全:
    HashMap 是非线程安全的,HashTable 是线程安全的;HashTable 内部的方法基本都经过       synchronized 修饰
2.效率:
    因为线程安全的问题,HashMap 要比 HashTable 效率高(HashTable 基本被淘汰,保证线      程安全的话就使用 ConcurrentHashMap)
3.对Null key 和Null value的支持:
    HashMap 中,null 可以作为键,这样的键只有一个,可以有一个或多个键所对应的值为 null
     HashTable 中 put 进的键值只要有一个 null,直接抛NullPointerException
4.初始容量大小和每次扩充容量大小的不同:
    HashMap 默认的初始化大小为16。之后每次扩充,容量变为原来的2倍
    Hashtable 默认的初始大小为11,之后每次扩充,容量变为原来的2n+1.
5.底层数据结构:
    java7 是数组+链表(头插法)    java8是数组+链表+红黑树(当链表长度大于8进行树化)

 HashMap的工作原理

      HashMap是基于hashing的原理,使用put(key, value)存储对象到HashMap中,使用get(key)从HashMap中获取对象

 Hashmap中put()方法工作原理:

       java7

        1.put方法会去判断hashmap集合是否为空或者长度是否为0,如果是则调用                             inflateTable()方法对hashmap进行初始化

        2.判断key是否为空,为空则遍历以table下表0为首的链表,寻找是否存在key==null的键值对,若存在就覆盖旧值并返回旧值,若不存在就调用addEntry()进行插入

        3.若key不为空,根据key计算数组索引值,循环链表,判断key是否存在,存在就覆盖旧值并返回

        4.addEntry()先判断是否扩容,如果要扩容就进行扩容,不用扩容就生成Entry对象,使用头插法添加到当前链表中

        

     java8

        1.put()方法判断是否为空或者长度是否为0,如果是就进行初始化,调用resize进行扩容

        2.put方法根据key通过hash算法得到数组下标

        3.如果数组下标元素为空,将key和value封装成node<k,v>直接放入

        4.如果下标元素不为空:

                a.判断数组上该位置key是否相同,相同就进行覆盖并返回旧值

                b.不相同就判断该节点上node的类型,判断是红黑树还是链表

                        c.如果是红黑树,将key和value封装成红黑树节点添加到树中,添加过程会判                         断是否存在当前key,存在就更新value

                        d.如果是链表节点,将key和value封装成一个链表node通过尾插法插入到链表的最后位置,因为是尾插法,所以要遍历链表,遍历过程中判断是否存在当前key,如果存在就更新value,遍历完后将新链表node插入到链表中,插入后,如果链表姐节点大于等于8并且数组长度大于64则会将链表转化为红黑树

java7和Java8中hashmap中put方法区别

初始化方式:

        java7采用InflateTable()

        java采用resize()

底层数据结构:

        java7采用数组加链表,无hash冲突放数组,有冲突存单链表

        java8采用数组+链表+红黑树,无hash冲突放数组,有冲突&链表长度<8,存单链表

        链表长度>8&数组长度>64存红黑树

插入数据方式:

        java7头插法

        java8尾插法

Hashmap中get()方法原理

JAVA7

    1.get方法先判断key是否为空,为空就调用getForNullKey方法

          a.首先判断Hashmap是否为空,为空就直接返回null

          b.遍历以table下标0为头节点的链表,寻找key=null对应的目标节点,找到就直接返回目标节点对应的值,找不到就返回null

     2.如果key不为空就调用getEntry()方法

          a.首先判断Hashmap是否为空,为空就直接返回null

          b.根据key计算hash值,根据hash值计算对应的索引下标,遍历以该数组下标为头节点的链表的所有节点,寻找该key对应的目标节点,找到就直接返回,找不到就直接返回null

JAVA8      getNode方法

        1.首先根据key计算hash值得到在数组中的索引位置,判断node数组以及传入对象对应的索引位置的桶是否为空,为空就返回null

        2.不为空就判断传入对象生成的hash值和key值与该桶第一个元素的是否相同,是就返回该桶第一个元素,不相同就继续判断该桶是否是红黑树

                a.是就遍历红黑树,判断是否有key相同的目标节点,存在就返回目标节点,不存在就返回null值

                b.不是红黑树就是链表,遍历链表,判断是否有key和hash值相同的目标节点,存在就返回目标节点,不存在就返回null值

                

Hashmap的扩容机制

JAVA7

        扩容条件:元素的个数>=阈值并且当前数组节点不为空

        1.先生成长度是老数组2倍的新数组

        2.遍历老数组中桶的每个元素

        3.根据key计算新数组的索引下标

        4.按照索引将元素添加到新数组

        5.所有元素转移完后,将新数组赋值给Hashmap的table属性

注:java7在多线程环境下扩容会产生链表成环的问题,JAVA8采用尾插法&链表重新链接解决了此问题

JAVA8

         1.先生成长度是老数组2倍的新数组

         2.遍历老数组中桶的每个元素

         3.如果桶节点没有形成链表,计算出新数组的索引位置,直接转移到新数组

         4.已经形成链表,将链表重新链接,按照低位区和高位区重新分配到新数组

         5.如果桶节点已经树化,

                调用split方法将红黑树重新分为低位区和高位区2个链表

                判断低位区和高位区链表的长度,链表长度小于6则会进行取消树化的处理,否则就会将新生成的链表树化

         6.所有元素转移完后,将新数组赋值给Hashmap的table属性

ConcurrentHashmap

1.什么是ConcurrentHashmap?

        ConcurrentHashmap是属于JUC包下的一个集合类,可以实现线程安全

2.数据结构:和Hashmap基本类似,数组+链表+红黑树

区别:

ConcurrentHashmap内部在数据写入时加入了同步机制(分段锁)保证线程安全,读是无锁

ConcurrentHashmap的扩容是多并发执行的,扩容效率更高,使用于多并发场景

3.安全并发访问:

JAVA7利用 Unsafe操作+ReentrantLock+分段思想

1.采用分段锁技术将哈希表分成多个段,每个段有一个可重入锁,可以在多个线程访问时,只需要锁住需要操作的那个表,提高并发性能

注:这种方式可以减少锁竞争,在高并发下,仍然会出现竞争

JAVA8利用 Unsafe操作+Synchronized关键字+分段思想

1.使用CAS+synchronized来保证线程安全,简单的读写操作,用CAS操作+自旋锁来修改,复杂的流程则使用synchronized来同步修改,可以避免分段锁机制下的锁的粒度太大,以及高并发场景下线程数量过多导致的锁竞争问题,提高并发性能

Hashmap为什么线程不安全?

    Hashmap是基于哈希表实现的,通过哈希算法保证元素的一致性,但在多线程下可能会出现以下问题:

1.多线程下扩容死循环:

        JAVA7中采用的是头插法,在多线程的环境下进行扩容可能导致环形链表的出现,形成死循环,JAVA8采用尾插法,扩容时会保证链表元素的顺序

2.多线程下的put可能会导致元素丢失:

        多线程同时执行put操作,若计算出来的索引位置相同,回导致前面的key被后面的key覆盖的问题,导致元素的丢失

3.put和get并发时可能导致get为null:

        线程1执行put时因为元素个数超阈值进行扩容,线程2此时执行get可能导致这个问题

Hashset和Hashmap的区别

1.Hashmap实现了map接口               Hashset实现了set接口

                  仅存储键值对                                仅存储对象

                  调用put方法添加元素                   调用add方法添加元素

                 使用key计算hashcode                 使用成员对象计算hsahcode,用equals来判断相                                                                          等性

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值