java基础-集合

常用的集合类有哪些?


Map接口和Collection接口是所有集合框架的父接口:

Collection接口的子接口包括:Set接口和List接口
Map接口的实现类主要有:HashMap、TreeMap、Hashtable、ConcurrentHashMap以及Properties等
Set接口的实现类主要有:HashSet、TreeSet、LinkedHashSet等
List接口的实现类主要有:ArrayList、LinkedList、Stack以及Vector等

集合和数组的区别

  • 数组是固定长度的;集合可变长度的。

  • 数组可以存储基本数据类型,也可以存储引用数据类型;集合只能存储引用数据类型。

  • 数组存储的元素必须是同一个数据类型;集合存储的对象可以是不同数据类型。

为什么要使用集合?

存储数据的时候, 数据多时变量就不方便了, 不是一个明智的选择,那么数组呢?数组也不是个好的选择,因为数组在创建的时候需要指定长度,在使用的过程中长度不变, 所有我们要寻求一个拥有可变长度的容器来存储这些数据。

还好Java提供了各种各样的容器,每个容器的长度都是可变的,我们只管往里面存,空间如果不够,它自己会调整。

所以有再多的数据我们也不怕,也不用看着下标去存,容器也提供了大量的对应方法,当看到提供的方法的时候就知道是什么操作了....比如:add,remove,size,isEmpty.....

为什么要使用集合?

答:因为方便和高效

哪些集合类是线程安全的?
vector:就比arraylist多了个同步化机制(线程安全),因为效率较低,现在已经不太建议使用。在web应用中,特别是前台页面,往往效率(页面响应速度)是优先考虑的。
statck:堆栈类,先进后出。
hashtable:就比hashmap多了个线程安全。
enumeration:枚举,相当于迭代器。
Java集合的快速失败机制 “fail-fast”?
是java集合的一种错误检测机制,当多个线程对集合进行结构上的改变的操作时,有可能会产生 fail-fast 机制。

例如:假设存在两个线程(线程1、线程2),线程1通过Iterator在遍历集合A中的元素,在某个时候线程2修改了集合A的结构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这个时候程序就会抛出 ConcurrentModificationException 异常,从而产生fail-fast机制。

原因:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变modCount的值。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;否则抛出异常,终止遍历。

解决办法:

在遍历过程中,所有涉及到改变modCount值得地方全部加上synchronized。

使用CopyOnWriteArrayList来替换ArrayList

怎么确保一个集合不能被修改?
可以使用 Collections. unmodifiableCollection(Collection c) 方法来创建一个只读集合,这样改变集合的任何操作都会抛出 Java. lang. UnsupportedOperationException 异常。

示例代码如下:

List<String> list = new ArrayList<>();
list. add("x");
Collection<String> clist = Collections. unmodifiableCollection(list);
clist. add("y"); // 运行时此行报错
System. out. println(list. size());


 

---------------------------------------------------------------------------------------------------------------------------------

集合框架中两大体系 Collection和 Map

Collection 集合体系

Map 键值对 集合体系

---------------------------------------------------------------------------------------------------------------------------------

Collection做为集合框架的顶层设计,它里面抽象出了17个公共方法;

这些方法分三类:1.修改操作; 2.查询操作;  3.批量操作;

add(E e)

向集合中添加单个数据,成功返回true,失败返回false

addAll(Collection<? extends E> c)

向集合中添加批量数据,成功返回true,失败返回false

所添加的数据类型跟定义的数据类型保持一致(常见的是一个对象,或者子对象,除此以外其他类型都无法存入)

    批量数据一般来自另一个集合,平时开发的时候用的比较少

---------------------------------------------------------------------------------------------------------------------------------

集合删除数据的方法有几种?

一共有四种:

1.删除单个数据remove方法

 2.删除批量数据removeAll方法

3.删除符合条件的数据 removeIf方法(jdk1.8)

4.删除所有数据clear方法直接清空集合

remove:数据是通过迭代器来删除的,它根据要删除的数据是否为null,分为两种情况,Remove方法每调用一次,只删除一个匹配项;当要删除的数据不为null的时候,匹配删除对应的项;

 --------------------------------------------------------------------------------------------------------------------------------

removeAll:删除指定数组的数据

 retainAll: 指定要保留的数据的集合,跟removeAll是相反的原理

removeIf: 遍历集合,依次检查迭代器的返回数据,以查看它是否满足规则,满足规则用remove方法删除;

clear:遍历集合依次调用迭代器的remove方法,删除集合中的每一项数据;

---------------------------------------------------------------------------------------------------------------------------------

ArrayList:

array数组的意思,list列表的意思;数组列表

存入的顺序和取出的顺序一致:

优点:查询快,只要知道数据的下标查询速度非常快,arraylist是所有容器中查询速度最快的一个;

缺点:增删慢,因为数组满了后要扩容,扩容要新创建一个是原来1.5倍的新数组,然后把之前的元素copy到新数组中来;这样一来数据越多越慢;

大小Size是元素的个数;容量(capacity)就是数组的长度;

三个构造方法:

无惨构造方法的初始容量是10;第二个构造方法可以指定容量;第三个可以将另一个集合数组中的元素插入到ArrayList集合中数组的初始容量就是集合的大小;

常用方法:

  ArrayList不仅是list下的一员,又做为collection旗下的一员,所以不但拥有collection里面的所有方法,还拥有list里面的所有的方法;

ArrayList常用的方法有10个

---------------------------------------------------------------------------------------------------------------------------------

LinkedList:

linked链式的意思,list列表;链式列表,基于链表实现的,而且是双向的,所以又称“双向链表”

前后没有节点的时候

前后有节点的,可以从尾部和头部添加节点

  1. 有序,元素存入和去除的顺序一致
  2. 可重复,里面可以存储重复的元素
  3. 可为null,它里面可以存储null元素

优点:增删快,增加的时候只需要记住前后节点就可以了

缺点:查询慢,每次查询都要从头部或者尾部按顺序查找

既实现了list接口,可实现了Deque接口,而list和queue又集成了collection,所以拥有的方法特别多

它常用的方法有9个:

Linkedlist删除数据:

        删除数据一般是找到匹配项以后,使用迭代器的remove方法进行删除

 ArrayList和LinkedList 区别:

综上所述:ArrayList多用于查询较多的应用场景种,linkedlist多用于频繁增删的场景中

---------------------------------------------------------------------------------------------------------------------------------

HashMap:   hash散列,哈希,map映射;哈希map
key-velue的形式存储数据的,这些数据保存在数组中,默认初始容量是16

数组是有序的,但是hashmap中的数据并不是按照顺序存放的,而是先根据key计算出hash值,再余数组容量-1

hash值&(数组容量-1),等同于数组容量取余得到的余数就是元素的位置

 

 如此依赖势必会有多个元素被分配到同一个位置上

 hashmap用链表的形式将多个元素链接起来解决了多个元素位置冲突的问题

 1.无序

存入顺序,取出顺序

2.key可以为null,并且分配的hash值為0

 3.key不可以重复,重复的key新值会覆盖旧值;

4.hash冲突:它是两个不同的key产生了相同的hash值,发生hash冲突的原因是取值范围的问题,例如一个二进制位取值范围是:0-1,要在这个范围内取值要么是0要么是1,发生hash碰撞的概率是1/2

 如果取值范围是16位的时候,发生碰撞的概率会越小

在hashmap中hash值是int类型4个自字节长度是32个二进制位

 碰撞的概率是:

 更长的hash值意味着碰撞的概率越低,但也需要更大的空间和计算,我们需要在性能和成本之间做好权衡

 5.链化<==>树化

树化:当链表长度大于8,并且容量大于等于64的时候会将链表转化为红黑树

 转化为红黑树的目的是为了提高查询速度,因为链表一旦长了以后查询就会变得很慢,

链化:另外红黑树也会变回链表,当红黑树种的节点小于等于6的时候,红黑树将转化为链表,这个过程称之为链化,此时的节点数很少,链表与红黑树的查询速度不差上下,而且在新增元素的时候链表不用计算节点的位置,直接插在尾部,但红黑树还要计算节点的位置,因此他们两个相互转换可以形成很好的互补;

 

 

 

 6.扩容:当元素个数超出临界值的时候,hashmap就需要扩容

临界值=容量*负载因子

 扩容后是之前的两倍,频繁扩容很影响hashmap的性能,所以合适的容量与负载因子至关重要

 7.hashmap实现了map接口,拥有map里面的所有方法,其中8个方法是最常用的

 

鼎鼎大名的HashMap一定要整体总结理解下:

先介绍一下hashmap?

答:HashMap嘛,在jdk7是数组+链表,jdk8数据结构做了升级,变成了数组+链表+红黑树了;

那你说说为什么这么设计,以及jdk8为什么要做出这种升级呢?

答:Map这种集合容器,最主要的应用就是想通过一个key最快的时间找到对应的value,事实上这个时间复杂度接近为O(1),那么怎么样才能实现这么快的速度呢?于是就引入了数组,数组可以理解为内存中一块连续的内存空间,且每一小块空间都有自己的索引,通过这个索引就能直接找到对应空间的值。可以直接把key和数组中的某个索引对应上,我放也放到这个索引里面,取也直接取这个索引去取,是不是依赖数组的特性,我就可以用O(1)的时间复杂度快速定位到我想要的值了;

那链表和红黑树又是怎么回事?

答:在把key映射成数组索引值这件事情上,期望的不同的key映射到不同的数组索引值中;但是天不遂人愿,总有可能两个key好巧不巧就映射成了同一个数组索引值,这就是哈希碰撞;碰撞之后就有两种情况:

      一是同一个key,我就要把原有的key的值覆盖掉;

      二就是两个key算出来的值是一样的,那就只能把两个key和value放到同一个数组索引的内存里面,也就形成了链表,当然,碰撞的越来越多,这个链表就会越来越长;

链表越来越长,那么为什么红黑树能就解决呢?

答:要从链表和红黑树的查找效率说起,链表这种数据结构不需要连续的内存空间,内部通常是持有了下一个节点的引用,所以,链表要查询出某一个元素,就要从头节点开始查,直到next为null才能确定整条链表查询结束,在最坏情况下,链表的长度是n,就是遍历n次才能找到元素,所以,链表的时间复杂度为O(n)。
这种的查询速度如果数据多了是不可以容忍的,怎么解决呢?

答:如果链表的长度大于了8,达到了9,就变成红黑树,当然还要满足整个hash表中的元素达到了64,之所以条件比较苛刻,是因为链表转数组本身就很耗性能,不到万不得已,万万使不得啊。红黑树这种数据结构首先是二叉搜索树,二叉搜索树就满足了查找的时间复杂度是O(lgn),是一种折半查找,一次排除一半的数据,就算你有100W数据,查找固定的数,也只需要20次,就是这么拽,而红黑树在有二叉搜索树的查询效率的前提下,又保证了树的平衡。所以链表在上述情况下会进化成红黑树,当然,又进化也有退化,退化的节点就是同一个哈希桶中的元素数小于6,就会从红黑树变回链表。之所以中间留了个7,就是为了防止频繁的树化、链表化、树化、链表化。

那么key是怎么转换成数组索引的呢?

答:public native int hashCode(); 这个方法可以返回一个32位的数字,当然,这个32位的数字是不能直接去当数组的索引的,因为一般情况下不会有那么大的数组。所以这个hashcode肯定是要经过某种转换,如果数组的长度是16的话,应该转换成为的值在0到15之间,才能保证落在数组的某一个索引值里面。这种的实现方式,常规的能想到的是取模。但是我们都知道,在计算机中,位运算的效率是最高的,于是,这个公式是这个样子的 (n - 1) & hash ,这种运算的结果和取模不一定相同(有很多博客说相同是错误的)。至于这么做为什么就能达到和取模一样的效果的呢?我们还是拿16举例:

 再加上位运算的速度相当快,所以HashMap是采用这种方式寻找数组索引的。曾经对相同的100W样本做过取模和位运算,大概快了几十倍把。

公式 (n - 1) & hash 中的hash不是直接用到hashcode,这个jdk7和jdk8还是不同的:

答:先取得key的hashcode,然后扰动函数高位低位特性融合,最后算出在数组中的索引

jdk1.7是采用的头插法会造成链表成环,jdk8是尾插法,就不会了。

那jdk8之后HashMap是不是就线程安全了?

答:不是的,jdk8只是不会发生链表成环的情况,但是在put操作的时候,会出现元素被覆盖的情况,并且size++也是有线程安全问题的,如果要考虑多线程的情况下使用,建议使用ConcurrentHashMap,里面有分段锁,所谓分段锁,jdk7中ConcurrentHashMap外面有一个Segment数组,这个Segment继承了ReentrantLock,我们就可以对每一个Segment单独上锁,既能保证线程安全,锁的粒度又不会太大,性能又不会太差;
 

数组什么时候会扩容?

答:有一个东西叫负载因子,默认是0.75,但是科学家前辈算出来的0.69几才是最完美的值。这个值是用来在空间和时间上取得平衡,比如原来数组长度是16,那么达到 16 *0.75=12 就会扩容了,一般扩容是原有数组的两倍。

--------------------------------------------------------------------------------------------------------------------------------

LinkedHashMap 链式哈希映射

1.介绍linkedHashMap  linked,链式的意思;hashmap,Hash映射

2.添加顺序

3.访问顺序

4.常用方法

用双向链表来保证了元素的顺序

 来记录的头结点和尾结点

 

 2.添加顺序和取出顺序是一致的

 3.被访问的元素会排在最后,通俗的讲就是被get过的元素会排在最后

 可以设置排序方式:

 有5个构造方法:4个构造方法有默认值,只有一个构造方法是可以指定排序顺序的

 

 4.linkedhashmap的常用方法有那些:它既继承了hashmap又实现了map接口

 当时常用的只有8个方法:

  ------------------------------------------------------------------------------------------------------------------------------

TreeMap:二叉树映射

介绍treemap,

1.排序:根据key进行排序

遍历方式:中序遍历

2.无序

3.key不可以为null:会抛空指针,而且无法参与比较(null.compareTo("E"))

4. key唯一 :当两个key相同的时候,走else分支,新值会覆盖旧值

treemap 的常用方法

 是以二叉树的形式存储数据的,二叉树是红黑树,红黑树拥有自平衡的特点

 红黑树的5个性质:

 常用的方法有9个:

   ------------------------------------------------------------------------------------------------------------------------------

HashSet 散列集合,内部是hashmap存储数据

它的四个构造方法都是在创建hashmap对象,所以,我们创建hashset就是在创建一个hashmap; 

存数据的时候只需要把key当velue,值统一用一个“present”值填充

添加顺序和取出顺序:存入的顺序和取出的顺序不一致,其他的跟hashmap一致;

 hashmap和hashset的区别:

    ------------------------------------------------------------------------------------------------------------------------------

LinkedHashSet

元素新增和取出的顺序一致,其他特点和优缺点跟linkedHashMap一样;

 LinkedHashSet常用方法有那些?常用的有7个

LinkedHashSet和LinkedHashMap的区别:LinkedHashSet只有一种顺序,那就是添加顺序,也是默认的取出顺序

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值