java中个个容器的属性,性能,参数对比;




Java 键值对 不重复 java中键值对_数据


Java容器的性能及属性的对比

List:Vector,ArrayList,LinkedList

Vector:内部是数组数据结构,可以理解为加锁的ArrayList,线程安全的,增删,查询都很慢。

Vector在JDK1.0版本就已经出现了,在1.2版本后出现了Collection集合,开始有集合框架,Vector改进为可以实现List接口,纳入集合框架。构造函数Vector()构造一个空向量,内部数组默认初始化大小为10。Vector中的方法都是从Collection和List继承来的。但Vector有一些特有的方法 :void addElement(E obj)

E firstElement()

E lastElement()

boolean removeElement(Object obj)

void setElementAt(E obj, int index)等…这些方法和对应不带Element的方法功能相同。

ArrayList

ArrayList是大小可变的数组,ArrayList()构造函数默认初始化一个容量为10的空列表,如果刚开始给它开辟的内存是11,那么,它的初始化就是11的容量;

ArrayList中每一个元素都有自己的地址值,数组中存储着这些元素的地址值,因为数组是一片连续的空间,所以在查询某一元素的时候速度很快。但是删除某个元素的时候,如果是删除最后一个则很快,否则删除的元素位置越靠前越慢,因为删除后后面的元素都要位移前一位,查询是根据数组的下标索引查的,所以相对于移动元素的时间很快,同样添加元素也是一样,越靠后越快;

LinkedList 是双向链接列表数据存储格式的,简称链表格式

链表中每个元素都有自己的地址,第一个元素记住第二个元素的地址,第二个元素记住第三个元素的地址,以此类推…如果想要查询其中的某一个元素,要从第一个元素挨个做判断,因为这些元素不是连续的,所以很慢。如果要增加一个元素,让这个元素把地址给前一个元素,再记住后一个元素的位置即可;如果要删除一个元素,只要它前一个元素记住它后一个元素的地址即可,所以增删的速度很快。 增删的速度也是相对的,LinkedList是双向链表如果删除第一个元素那是最快的,如果删除最后一个元素也很快,同样添加第一个元素或最后一个元素会很快.

HashSet

速度快、效率高,

不能保证元素的排列顺序,顺序有可能发生变化

不是同步的

集合元素可以是null,但只能放入一个null

当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据 hashCode值来决定该对象在HashSet中存储位置。

简单的说,HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode()方法返回值相 等

注意,如果要把一个对象放入HashSet中,重写该对象对应类的equals方法,也应该重写其hashCode()方法。其规则是如果两个对 象通过equals方法比较返回true时,其hashCode也应该相同。另外,对象中用作equals比较标准的属性,都应该用来计算 hashCode的值。

HashSet要求放入的对象必须实现HashCode()方法,放入的对象,是以hashcode码作为标识的,而具有相同内容的 String对象,hashcode是一样,所以放入的内容不能重复。但是同一个类的对象可以放入不同的实例 。

TreeSet

1、TreeSet 是二差树实现的,Treeset中的数据是自动排好序的,不允许放入null值。

2、HashSet 是哈希表实现的,HashSet中的数据是无序的,可以放入null,但只能放入一个null,两者中的值都不能重复,就如数据库中唯一约束。

是一个可排序的Set,TreeSet是SortedSet接口的唯一实现类,TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序 和定制排序,其中自然排序为默认的排序方式。向TreeSet中加入的应该是同一个类的对象。

TreeSet判断两个对象不相等的方式是两个对象通过equals方法返回false,或者通过CompareTo方法比较没有返回0

自然排序

自然排序使用要排序元素的CompareTo(Object obj)方法来比较元素之间大小关系,然后将元素按照升序排列。

Java提供了一个Comparable接口,该接口里定义了一个compareTo(Object obj)方法,该方法返回一个整数值,实现了该接口的对象就可以比较大小。

obj1.compareTo(obj2)方法如果返回0,则说明被比较的两个对象相等,如果返回一个正数,则表明obj1大于obj2,如果是 负数,则表明obj1小于obj2。

如果我们将两个对象的equals方法总是返回true,则这两个对象的compareTo方法返回应该返回0

定制排序

自然排序是根据集合元素的大小,以升序排列,如果要定制排序,应该使用Comparator接口,实现 int compare(T o1,T o2)方法。

使用场景:HashSet是基于Hash算法实现的,其性能通常都优于TreeSet。我们通常都应该使用HashSet,在我们需要排序的功能时,我们才使用TreeSet。

HashSet与TreeSet的底层运行方式: TreeSet集合对象的加入过程
TreeSet的底层是通过二叉树来完成存储的,无序的集合
当我们将一个对象加入treeset中,treeset会将第一个对象作为根对象,然后调用对象的compareTo方法拿第二个对象和第一个比较,当返回至=0时,说明2个对象内容相等,treeset就不把第二个对象加入集合。返回>1时,说明第二个对象大于第一个对象,将第二个对象放在右边,返回-1时,则将第二个对象放在左边,依次类推

HashSet集合对象的加入过程

Hashset底层是hash值的地址,它里面存的对象是无序的。 第一个对象进入集合时,hashset会调用object类的hashcode根据对象在堆内存里的地址调用对象重写的hashcode计算出一个hash值,然后第一个对象就进入hashset集合中的任意一个位置。

第二个对象开始进入集合,hashset先根据第二个对象在堆内存的地址调用对象的计算出一个hash值,如果第二个对象和第一个对象在堆内存里的地址是相同的,那么得到的hash值也是相同的,直接返回true,hash得到true后就不把第二个元素加入集合(这段是hash源码程序中的操作)。如果第二个对象和第一个对象在堆内存里地址是不同的,这时hashset类会先调用自己的方法遍历集合中的元素,当遍历到某个元素时,调用对象的equals方法,如果相等,返回true,则说明这两个对象的内容是相同的,hashset得到true后不会把第二个对象加入集合。

HashMap 主要用来存放键值对,它基于哈希表的Map接口实现,是常用的Java集合之一,数据是无序的,jdk1.8在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。


Java 键值对 不重复 java中键值对_数据_02

HashMap的数组


Java 键值对 不重复 java中键值对_链表_03

HashMap的数组的链表

HashMap JDK1.8 之前 HashMap 由 数组+链表 组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的.

HashMap的时间复杂度O(1),但是如果冲突较多,链表的时间复杂度是O(n),所以在JDK1.8 以后,当链表长度大于阈值(默认为 8)时,将链表转化为红黑树,以减少搜索时间,红黑树的时间复杂度是O(log n)。 所以查询一个HashMap数据的时间为 O(1) + O(n)或O(1) + O(log n) 平均为O(1)

总结:HashMap根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度,插入、删除和定位元素,HashMap是最好的选择。

TreeMap

TreeMap是基于红黑树实现的一个保证有序性的Map 基于红黑树,所以TreeMap的时间复杂度是O(log n),如果需要有排序的数据,直接存放进TreeMap中就行,TreeMap自己会给排序,不需要再写排序代码。

总结:TreeMap取出来的是排序后的键值对。插入、删除需要维护平衡会牺牲一些效率。但如果要按自然顺序或自定义顺序遍历,那么TreeMap会更好。

HashMap通过hashcode对其内容进行快速查找,而 TreeMap中所有的元素都保持着某种固定的顺序,如果你需要得到一个有序的结果你就应该使用TreeMap。HashMap通常比TreeMap效率要高一些,一个是哈希表,一个是二叉树,建议多使用HashMap,在需要排序的Map时候才用TreeMap。HashMap的查询速度比TreeMap要快。

HashMap 数组链表的红黑树TreeMap 二叉树的红黑树

LinkedHashSet 是具有可预知迭代顺序的Set接口的哈希表和链接列表实现。此实现与HashSet的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序可为插入顺序或是访问顺序。

注意,此实现不是同步的。如果多个线程同时访问链接的哈希Set,而其中至少一个线程修改了该Set,则它必须保持外部同步。

LinkedHashSet:继承于HashSet,基于LinkedHashMap来实现.底层是LinkedHashMap实现

      Set接口的一个实现.和HashSet的区别,LinkedHashSet维护一个双重链接列表,定义了迭代顺序可为插入顺序,或者是访问顺序.

CurrentHashMap

ConcurrentHashMap采用了分段锁技术,其中Segment继承于ReetrantLock。不会像HashTable

那样不管是put还是get操作都需要做同步处理,理论上ConcurrentHashMap支持Segment数组数量的线程并发。

每当一个线程占用锁访问一个Segment时,不会影响到其他的Segment。

ArrayList、Vector、HashMap、HashSet的默认初始容量、加载因子、扩容增量

当底层实现涉及到扩容时,容器或重新分配一段更大的连续内存(如果是离散分配则不需要重新分配,离散分配都是插入新元素时动态分配内存),要将容器原来的数据全部复制到新的内存上,这无疑使效率大大降低。

加载因子的系数小于等于1,意指 即当 元素个数 超过 容量长度*加载因子的系数 时,进行扩容。

另外,扩容也是有默认的倍数的,不同的容器扩容情况不同。

List

ArrayList、Vector默认初始容量为10

Vector:线程安全,但速度慢

    底层数据结构是数组结构

    加载因子为1:即当 元素个数 超过 容量长度 时,进行扩容

    扩容增量:原容量的 1倍

      如 Vector的容量为10,一次扩容后是容量为20,

当初始化给11的时候,首次内存空间就是11,然后扩容的话是22,

ArrayList:线程不安全,查询速度快

    底层数据结构是数组结构

    扩容增量:原容量的 0.5倍+1

      如 ArrayList的容量为10,一次扩容后是容量为16

当初始化给11的时候,首次内存空间就是11,然后扩容的话是19,

Set(集) 元素无序的、不可重复。

HashSet:线程不安全,存取速度快

     底层实现是一个HashMap(保存数据),实现Set接口

     默认初始容量为16(为何是16,见下方对HashMap的描述)

     加载因子为0.75:即当 元素个数 超过 容量长度的0.75倍 时,进行扩容

     扩容增量:原容量的 1 倍

      如 HashSet的容量为16,一次扩容后是容量为32

构造方法摘要HashSet()
HashSet(int initialCapacity)
构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和默认的加载因子(0.75)。
HashSet hs=new HashSet(1);
所以可见 HashSet类,创建对象的时候是可以的制定容量的大小的 ,期中第二个就具有这个工功能。

Map是一个双列集合

HashMap:默认初始容量为16

     (为何是16:16是2^4,可以提高查询效率,另外,32=16<<1 -->至于详细的原因可另行分析,或分析源代码)

     加载因子为0.75:即当 元素个数 超过 容量长度的0.75倍 时,进行扩容

     扩容增量:原容量的 1 倍

      如 HashSet的容量为16,一次扩容后是容量为32

理论上讲ArryaList的数据是线性的,LinkedList是离散的数据,LinkedList更消耗内存,但是ArrayList有个扩容机制,就是假设ArrayList 有11条数据,那么它的内存是16,所以怎么解释?但是数据给出的是LinkedList更消耗内存:


Java 键值对 不重复 java中键值对_链表_04

ArrayList和LinkedList分别在64位操作系统和32位操作系统上运行的效率

未完待续...