集合及其实现类

为什么提供不同的集合类

Java是面向对象的语言,我们在编程的时候自然需要存储对象的容器,数组可以满足这个需求,但是数组的初始长度是固定的,但是我们往往需要一个长度可变的容器.因此,集合出现了.

集合与数组的区别

(1)长度区别:集合长度可变,数组长度不可变.

(2)内容区别:集合可存储不同类型的元素,数组存储单一类型元素萨.

(3)元素区别:集合只能存储引用类型元素,数组可存储引用类型,也可存储基本类型.

然而在我们的开发实践中,经常需要保存一些可变长度的数据集合,于是,我们需要一些能够动态增长长度的容器来保存我们的数据.

而我们需要对数据的保存的逻辑可能各种各样,于是就有了各种各样的数据结构.java中对于各种数据结构的实现,就是我们用到的集合.

Java的集合框架是由很多接口,抽象类,具体类组成的,都位于java.util包中.

泛型:

为什么会有泛型?

早期的Object类型可以接受任意的对象类型,但是在实际的使用中,会有类型转换的问题.也就存在这隐患,所以Java提供了泛型来解决这个安全问题.

泛型,即"参数化类型".一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参.

参数化类型,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后再使用/调用是传入具体的类型(类型实参).

1.泛型的类型参数只能是类类型(包括自定义类)

2.泛型的类型参数可以有多个.

若没有定义具体类型,默认为Object

List,Set,Map集合各自的特点

List和Set都继承了Collection类

List(有序,可重复)

ArrayList

底层数组结构是数组,查询快,增删慢.

线程不安全,效率高.

LinkedList

底层数据结构是链表,查询慢,增删快.

线程不安全,效率快.

Vector

底层数据结构是数组,查询快,增删慢.

线程安全,效率低.

Set(无序,唯一)

HashSet

底层是数据结构是哈希表.

LinkedHastSet

底层数据结构由链表和哈希表组成.

由链表保证元素有序.

由哈希表保证元素唯一.

TreeSet

底层数据结构是红黑树.(是一种平衡二叉树)

根据比较的返回值是否为0来决定保证元素的唯一性.

两种排序方式

自然排序(元素具备可比性)

让元素所属的类实现Comparable接口

比较器排序(集合具备比较性)

让集合接收一个Comparator的实现类对象.

Map(双列集合,键值对)

注:Map集合的数据结构仅仅针对键有效,与值无关,关键的是键值对形式的元素,键唯一,值可重复.

HashMap

底层数据结构是哈希表.线程不安全,效率高.

哈希表依赖两个方法:hashCode()和equals()

执行顺序:

首先判断hashCode()值是否相同.

true,继续执行equals()看其返回值,

依然为true,说明元素重复,不添加.

若为false,则添加到集合.

false,直接添加到集合.

最终:自动生成hashCode()和equals()

TreeMap

底层是红黑树(是一种平衡二叉树)

根据比较的返回值是否为0来决定保证元素的唯一性.

两种排序方式:

自然排序(元素具有可比性)

让元素所属的类实现Commparable接口

比较器排序(集合具有可比性)

让集合接受一个Commparator的实现类对象.

判断是否需要键值对形式,需要则使用Map,不需要则使用Collection类下面的两个Set和List.

List按对象进入的顺序保存对象,不做排序或编辑操作.Set对每个对象只接收一次,并使用自己内部的排序方法(通常,你只关心某个元素是否属于Set,而不关心它的顺序,否则使用List).Map同样对每个元素保存一份,但这是基于"键"的,Map也有内置的排序,因而不关心元素添加的顺序.如果添加元素的顺序对你很重要,应该使用LinkedHashSet或LinkedHashMap

List集合实现类有哪些,各自特点

一.ArrayList:

内部存储的数据结构是数组(动态调整大小)实现,默认初始值为10,每次扩容大小是增加50%(在Java8版本以后的版本,Java6使用的是1.5倍)

优点:使用数组实现,因此内部元素可以通过索引实现快速随机访问(检索集合中特定索引位置的元素)

缺点:

(1)从ArrayList中间位置插入和删除元素,都需要循环移动其他元素元素的位置.

(2)数组空间不够需要扩容时,会开辟一个新的数组把旧的数组元素拷贝过去,比较性能.

(3)线程不安全

扩容:

jdk8:int newCapacity =oldCapacity+(oldCapacity>>1);扩容50%作为新数组的容量.

jdk6:nt newCapacity =(oldCapacity*3)/2+1;

这是在原来数组大小基础上扩大1.5倍作为新数组的大小

二.LinkedList

内部存储使用的数据结构是双向链表实现.

优点:

使用链表实现,适合动态的插入和删除.

缺点:

(1)随机访问元素的速度相对较慢.

(2)基于链表数据结构的实现,占用的内存空间比较大(除了数据本身,还要保存指针信息).

三.Vector

基于数组(动态调整大小)数据结构实现,初始容量是10.

优点:线程安全

缺点:效率低,增加元素,删除元素,查找元素都满慢.

四:ArrayList和LinkedList性能差别的原因.

1.随机访问方面(指检索集合中特定索引的元素):

ArrayList可以根据索引直接找到值并返回;而LinkedList则是通过二分查找检索.

2.随机插入,删除方面:

ArrayList的底层是数组,所以在随机插入,删除时,需要移动大量的数据,效率低.

而LinkedList是链表实现的,在随机插入,删除时会先找到插入点,直接插入.在插入,删除时,LinkedList也会使用二分查找找到,插入删除的元素.

五: ArrayList和Vector之间的比较:

1.线程安全:

Vector是线程安全的:在同一时间只有一个线程可以访问Vector对象,而ArrayList是线程不安全的,在同一时间可以有多个线程访问ArrayList对象.例如:如果一个线程正在执行添加操作,那么在多线程环境中可能有另一个线程执行删除操作.

2.性能方面:

ArrayList更快,,因为它是不同步的(线程不安全的),而Vector操作由于是同步的(线程安全的),所以性能更慢.如果一个线程在访问Vector,它就获得了一个锁,那其他想要操作Vector的线程就必须等着,直到锁被释放才能访问Vector.

3.数据增长方面:

ArrayList与Vector的底层都是基于数组(动态调整大小)实现的.它们都会动态增长和缩小,从而保持最合适的存储空间的大小.但是它们在重新调整大小的方式是不同的.如果它们在重新调整大小的方式是不同的.如果元素属数量超过了容量,ArratList会增加当前数组的50%,而Vector会增加100%.

4.日常使用方面:

如果没有特别要求一定要使用Vector,一般都偏向于ArrayList.即使要求在线程安全的时候,也可以通过使用显式Collections.synchronizedList这个方法。

六.使用场景:

(1)ArrayList的使用场景:

1.其拥有动态数组的属性,适合在声明时不知道数据结构大小时使用,避免浪费内存空间.

2.适合需要快速查找和遍历.

3.不适合经常需要插入和删除操作.

(2)LinkedList的使用场景:

1.适合需要随机插入,随机删除效率要求比较高时.

2.可以作为队列和栈使用

(3)Vector的使用场景:

一般在开发中很少用到这个Vector.Vector是jdk1.0时加入的,存在的意义可能是为了兼容性.

ArrayList初始容量,扩容机制

创建一个ArrayList集合时,集合初始容量为0,在第一次添加元素时会对集合进行扩容,扩容为10;当集合添加元素大于容量后,集合会扩容到原来的3/2.

HashMap存储数据的特点

数据结构中有数组和链表来实现对数据的储存,但这两者基本上是个极端.

数组:数组的存储区是连续的,占用内存严重,故空间复杂度非常大.但数组的二分查找时间度小;数组的特点:寻址easy,插入和删除困难.

链表:链表的存储区离散,占用内存比较宽松,故空间的复杂度非常小,但时间复杂度非常大;链表的特点:

寻址困难,插入和删除easy.

哈希表:

HashMap是由数组+链表组成.寻址easy,插入和删除easy.(存储单元数组Entry[],数组里面包括链表)

HashMap事实上也是由一个线性的数组实现的.

所以能够理解为其存储数据的容器就是一个线性容器;

HashMap里面有一个内部静态类Enty,其重要的属性有key,value,next,从属性key,value就能够非常明显的看出来Enty就是HashMap键值对实现的一个基础的bean;也就是说HashMap的基础就是一个线性数组,这个数组就是Entry[].Map里面的内容都保存在Entry[]中.

2.Hash的存取实现

(1)存储

这里HashMap用了一个算法.

int hash=key.hashCode();

获取key的hashCode,这个值一定是一个固定的int值

int index=hash%Entry[].length

获取数组的下表:key的hash值对Entry数组长度进行取余

假设两个key通过hash%Entry[]得到的index同样,会不会覆盖.

答案是不会的.Entry类有一个next属性,作用是指向下一个Entry.例,第一个键值对A进来,通过计算key的hash得到的.

index=0.记做Entry[0]=A.一会又进来一个键值对B,通过计算其index也等于0,如今怎么办,HashMap会这样做B.next=A,Entry[0]=B,那么C.next=B,Entry[0]=C;这样我们发现了index=0的地方事实上存取了ABC三个键值对,他们通过next属性连在一起,也就是说Entry[]中存储的是最后插入的数据.

(2)取值:

获取key的hashcod值,通过hash值去hash%Entry[].length,获取Entry[Hash%Entry.length],定位到该元素之后,在遍历该元素处的链表,

取值的时候:

int hash=key.hashCode();

int index=hash%Entry[].length;

return Entry[index]

当哈希表的容量超过默认容量时,必须调整table的大小.当容量达到最大值时,该方法Interger.MAX_VALUE返回.这是,就需要创建一张表,将原来的表映射到新表中.

(3)HashMap,HashTable和ConcurrentHashMap的线程安全问题.

HashMap:线程不安全的.

HashTable:锁住整张hash表,让线程独占,hashMap同意为空.

通过分析Hashtable就知道,synchronized是针对整张hash表的,即每次锁住整张表让线程独占.安全的背后是巨大的浪费.

ConcurrentHashMap:一个更快的hashmap,它提供了好得多的并发性.多个读操作差点总能够并发的运行.

他是锁段(默认:把hash表分为16个段),在get,put,remove等操作中,ConcurrentHashMap仅仅锁定当前须要用到的段,仅仅在求size的时候才解锁整张hash表.

HashMap如何保证键不重复

1.对于HashMap HashSet的实现是:

维护了一张HashTable.容器中的元素全部存储在Hashtable中,每次添加元素都会先判断是否有重复的元素,hashcode()进行比较,若一样再equals()方法比较,他们的底层数据结构如果也相同的话,JVM就认为数据已经存在了,就不回添加数据了.

2.对于TreeMap TreeSet

他们底层的数据结构实现是:维护了一颗二叉树.容器添加元素的时候,他们又是怎么判断是否有相同的元素的?我们都知道TreeMap TreeSet 他们都是有序的存储数据.为了维护数据的唯一性,在存入数据的时候,他们会调用元素中实现的Comparable的compareTo().或者集合本身创建的时候传入了迭代器.具体的实现是:调用比较的方法,调用比较的方法,返回-1的时候添加到左子树,返回1的时候添加到右子树,返回0的时候有相同数据不添加该元素.

HashMap底层存储结构,其中重要的一些参数有哪些?

HashMap实际上是一个"链表的数组"的数据结构,每个元素存放链表头结点的数组,即数组和链表的结合体.

HashMap底层就是一个数组,数组中的每一项又是一个链表.

HashMap的性能参数:

1.HashMap();构建一个初始容量为16,负载因子为0.75的HashMap.

2.HashMap(int initialCapacity);构建一个初始容量为initialCapacity,负载因子为0.75的HashMap.

3.HashMap(int initialCapacity,float loadFactor);以指定初始容量,指定的负载因子创建一个HashMap.

4.hashMap的基础构造器HashMap(int initialCapacity,float loadFactor);带有两个参数,他们是初始容量initialCapacity加负载因子loadFactor.

5.initialCapacity:HashMap的最大容量,即为底层数组的长度.

6.loadFactor:负载因子loadFactor定义为:散列表的实际元素数目(n)/散列表的容量(m)负载因子衡量的是一个散列表的空间使用程度,负载因子越大表示散列表的装填程度越高,反之越小.对于链表法的散列表来说,查找一个元素的平均时间为O(1+a),因此如果负载因子越大,对空间的利用更充分,ran而后果是查找效率的降低;如果负载因子太小,那么散列表的数据过于稀松,对空间造成严重浪费.

Collection和Collections的区别

1.Collection是一个集合接口.它提供了对集合对象进行基本操作的通常接口方法.Collection接口在Java类库中有很多具体的实现.Collection接口的意义是为各种具体的集合提供最大化的统一操作方式.

map,set,list,vector都实现了Collection

2.Collections是一个包装类.它包含各种有关集合操作的静态多态方法.此类不能实例化,就像一个工具类,服务于Java的Collection框架.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值