为什么提供不同的集合类
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框架.