Java集合类:set、list、queue、map
简述
- Collection是最基本的集合接口,一个Collection代表一组Object的集合,这些Object被称作Collection的元素。Collection是一个接口,用以提供规范定义,不能被实例化使用
- List是可重复集合,Set是不可重复集合,这两个接口都实现了Collection父接口. Map未继承Collection,而是独立的接口, Map是一种把键对象和值对象进行映射的集合,它的每一个元素都包含了一对键对象和值对象, Map中存储的数据是没有顺序的, 其key是不能重复的,它的值是可以有重复的。Queue保持一个队列(先进先出)的顺序.
- Interface Iterable
迭代器接口,这是Collection类的父接口。实现这个Iterable接口的对象允许使用foreach进行遍历,这个Iterable接口只有一个方法: iterator()
set
- 无序,不能包含重复元素
- Set判断两个对象相同不是使用”==”运算符,而是根据equals方法。
- Set的实现类有HashSet和TreeSet;
- HashSet: 内部是由哈希表(实际上是一个 HashMap 实例)支持的。它不保证 set元素的迭代顺序.
- TreeSet: TreeSet使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序.
HashSet
- HashSet是Set接口的典型实现,HashSet使用Hash算法来存储集合中的元素,因此具有良好的存取和查找性能。当向HashSet集合中存入一个元素时,HashSet会调用该对象的 hashCode()方法来得到该对象的hashCode值,然后根据该HashCode值决定该对象在HashSet中的存储位置。
LinkedHashSet
- LinkedHashSet集合也是根据元素的hashCode值来决定元素的存储位置,但和HashSet不同的是,它同时使用链表维护元素的次序,这样使得元素看起来是以插入的顺序保存的。当遍历LinkedHashSet集合里的元素时,LinkedHashSet将会按元素的添加顺序来访问集合里的元素。
LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet的性能,但在迭代访问Set里的全部元素时(遍历)将有很好的性能
SortedSet
- 此接口主要用于排序操作,即实现此接口的子类都属于排序的子类
- TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态
EnumSet
- EnumSet是一个专门为枚举类设计的集合类,EnumSet中所有元素都必须是指定枚举类型的枚举值,该枚举类型在创建EnumSet时显式、或隐式地指定。EnumSet的集合元素也是有序的,它们以枚举值在Enum类内的定义顺序来决定集合元素的顺序
Queue
- Queue用于模拟”队列”这种数据结构(先进先出 FIFO)。队列的头部保存着队列中存放时间最长的元素,队列的尾部保存着队列中存放时间最短的元素。新元素插入(offer)到队列的尾部,访问元素(poll)操作会返回队列头部的元素,队列不允许随机访问队列中的元素。
- PriorityQueue并不是一个比较标准的队列实现,PriorityQueue保存队列元素的顺序并不是按照加入队列的顺序,而是按照队列元素的大小进行重新排序.
- Deque接口代表一个”双端队列”,双端队列可以同时从两端来添加、删除元素,因此Deque的实现类既可以当成队列使用、也可以当成栈使用
- ArrayDeque 是一个基于数组的双端队列,和ArrayList类似,它们的底层都采用一个动态的、可重分配的Object[]数组来存储集合元素,当集合元素超出该数组的容量时,系统会在底层重新分配一个Object[]数组来存储集合元素
map
- Map用于保存具有”映射关系”的数据,Map的key不允许重复。
- Map接口有三个实现类:Hashtable,HashMap,TreeMap,LinkedHashMap
- Hashtable: 内部存储的键值对是无序的是按照哈希算法进行排序,与HashMap最大的区别就是线程安全.键或者值不能为null,为null就会抛出空指针异常
- HashMap: 内部存储的键值对是无序的是按照哈希算法进行排序,与Hashtable最大的区别就是非线程安全的,键或值可以为null
- TreeMap: 基于红黑树(red-black tree)数据结构实现, 按 key 排序,默认的排序方式是升序.
LinkedHashMap:有序的Map集合实现类,相当于一个栈,先put进去的最后出来,先进后出.
Map集合有哪些常用方法
Value put(Key k,Value v): 将给定的Key - Value 对存入到Map中返回值为被替换的value.注:Map不允许有重复的Key,若存入的key已经存在与Map中,则是替换value操作,返回值就是被替换的value,若存入的key在Map中不存在,则返回值为Null
Value get(Key k): 根据给定的key获取对应的value.
Value remove(Key k): 根据给定的key删除这组键值对,返回值是该键值对的value若没有,则返回null.
遍历Map有几种方式
一共三种方式:
1) Set keySet():遍历所有的key, 将当前Map中所有的key存入到一个Set集合后返回
HashMap
- 和HashSet集合不能保证元素的顺序一样,HashMap也不能保证key-value对的顺序。并且类似于HashSet判断两个key是否相等的标准也是: 两个key通过equals()方法比较
LinkedHashMap
- LinkedHashMap也使用双向链表来维护key-value对的次序,该链表负责维护Map的迭代顺序,与key-value对的插入顺序一致(注意和TreeMap对所有的key-value进行排序进行区分)
Hashtable
- 是一个古老的Map实现类
Properties
- Properties对象在处理属性文件时特别方便(windows平台上的.ini文件),Properties类可以把Map对象和属性文件关联起来,从而可以把Map对象中的key-value对写入到属性文件中,也可以把属性文件中的”属性名-属性值”加载到Map对象中
SortedMap
- 正如Set接口派生出SortedSet子接口,SortedSet接口有一个TreeSet实现类一样,Map接口也派生出一个SortedMap子接口,SortedMap接口也有一个TreeMap实现类
TreeMap
- TreeMap就是一个红黑树数据结构,每个key-value对即作为红黑树的一个节点。TreeMap存储key-value对(节点)时,需要根据key对节点进行排序。TreeMap可以保证所有的key-value对处于有序状态。同样,TreeMap也有两种排序方式: 自然排序、定制排序
WeakHashMap
- WeakHashMap与HashMap的用法基本相似。区别在于,HashMap的key保留了对实际对象的”强引用”,这意味着只要该HashMap对象不被销毁,该HashMap所引用的对象就不会被垃圾回收。但WeakHashMap的key只保留了对实际对象的弱引用,这意味着如果WeakHashMap对象的key所引用的对象没有被其他强引用变量所引用,则这些key所引用的对象可能被垃圾回收,当垃圾回收了该key所对应的实际对象之后,WeakHashMap也可能自动删除这些key所对应的key-value对
IdentityHashMap
- IdentityHashMap的实现机制与HashMap基本相似,在IdentityHashMap中,当且仅当两个key严格相等(key1 == key2)时,IdentityHashMap才认为两个key相等
EnumMap
- EnumMap是一个与枚举类一起使用的Map实现,EnumMap中的所有key都必须是单个枚举类的枚举值。创建EnumMap时必须显式或隐式指定它对应的枚举类。EnumMap根据key的自然顺序(即枚举值在枚举类中的定义顺序)
List
- List的实现类有ArrayList, Vector和LinkedList.
- ArrayList和Vector内部是线性动态数组结构,在查询效率上会高很多,Vector是线程安全的,相比ArrayList线程不安全的,性能会稍慢一些.
- LinkedList:是双向链表的数据结构存储数据,在做查询时会按照序号索引数据进行前向或后向遍历,查询效率偏低,但插入数据时只需要记录本项的前后项即可,所以插入速度较快。
ArrayList
- ArrayList是基于数组实现的List类,它封装了一个动态的增长的、允许再分配的Object[]数组。
- Vector
Vector和ArrayList在用法上几乎完全相同,在JDK1.2以后,java提供了系统的集合框架,就将Vector改为实现List接口,统一归入集合框架体系中
- Stack是Vector提供的一个子类,用于模拟”栈”这种数据结构(LIFO后进先出)
- LinkedList implements List
<E>
, Deque<E>
。实现List接口,能对它进行队列操作,即可以根据索引来随机访问集合中的元素。同时它还实现Deque接口,即能将LinkedList当作双端队列使用。自然也可以被当作”栈来使用”
ArrayList和LinkedList相同和区别
- ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
- 对于随机访问get和set,ArrayList优于LinkedList,因为LinkedList要移动指针
- 当需要插入数据的时候,如果是在集合的前段(大概集合容量的前1/10)处插入数据时,linkedlist性能明显优于arraylist,但是!!!!当在集合的中部甚至靠后的位置插入大量数据时,arraylist的性能反而远远优于linkedlist,所以,linkedlist相较于arraylist的唯一优势在于集合前段的数据的插入
- 对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
- 对ArrayList和LinkedList而言,在列表末尾增加一个元素所花的开销都是固定的。对ArrayList而言,主要是在内部数组中增加一项,指向所添加的元素,偶尔可能会导致对数组重新进行分配;而对LinkedList而言,这个开销是统一的,分配一个内部Entry对象。
- 在ArrayList的中间插入或删除一个元素意味着这个列表中剩余的元素都会被移动;而在LinkedList的中间插入或删除一个元素的开销是固定的。
- LinkedList不支持高效的随机元素访问。
- ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗相当的空间
ArrayList和Vector的区别
- ArrayList和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector由于使用了synchronized方法(线程安全),通常性能上较ArrayList差。而LinkedList使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,索引就变慢了,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。
- 这两个类都实现了List接口(List接口继承了Collection接口),他们都是有序集合,我们以后可以按位置索引号取出某个元素,HashSet之类的集合不可以按索引号去检索其中的元素,也不允许有重复的元素。
- 同步性:
Vector是线程安全的,也就是说是它的方法之间是线程同步的,而ArrayList是线程序不安全的,它的方法之间是线程不同步的.- 数据增长:
ArrayList与Vector都有一个初始的容量大小,Vector默认增长为原来两倍,而ArrayList的增长策略是增长为原来的1.5倍。ArrayList与Vector都可以设置初始的空间大小,Vector还可以设置增长的空间大小,而ArrayList没有提供设置增长空间的方法。
总结:即Vector增长原来的一倍,ArrayList增加原来的0.5倍。
ArrayList,Vector, LinkedList的存储性能和特性
- ArrayList和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector由于使用了synchronized方法(线程安全),通常性能上较ArrayList差,而LinkedList使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。
怎么实现集合中引用类型元素的自定义排序
当一个类实了Comparable接口后,接口要求必须重写方法comparaTo,该方法的作用
是定义当前对应于参数给定对象比较大小规则
方法要求返回一个整数,该整数不关注具体值,只关注取值范围,即:
当返回值>0当前对象大于参数对象
当返回值<0当前对象小于参数对象
当返回值=0两个对象相等
然后在使用时直接调用Collections.sort(list);传入要比较的集合就会按照自己定义的比 较规则进行比较.
注: 该方法在排序自定义元素时,对我们的程序有侵入性
侵入性:当我们使用一个功能时,该功能要求我们为其修改的代码越多,侵入性越强高侵入性不利于程序扩展,应尽量避免, 侵入性的弊端是一旦该功能不再需要时,之前修改的代码都不具意义,增加后期维护代码成本
List集合有哪些常用方法
List接口继承自Collection接口, 是可重复集,并且有序. 提供了一系列支持使用下标操作元素的方法:
E get(index): 获取指定下标处的元素
E set(int index,E e):将给定的元素设置到指定位置处,返回值原位置元素
E remove(int index):删除并返回指定下标处的元素,返回为原位置的元素
boolean remove(Object o): 删除集合中与给定元素equals比较为true的元素
boolean add(E e):向集合内添加一个元素
void add(int index,E e): 在指定位置插入规定元素
void clear():清空集合
List subList(int start,int end): 截取当前集合中指定范围的子集.
注: 对子集的操作就是对原集合中对应元素的操作
重写equals方法的规则
1).如果传过来的obj是null那么就返回false
2).如果obj==this,传过来的obj和当前对象时同一个对象,返回true
3).如果obj 和当前对象是相同类型,则返回当前对象和obj的属性的比较,否则返回false
public boolean equals(Object obj){
if(obj==null){
return false;}
if(obj==this){
return true;}
if(obj instanceof Point){
Point p=(Point)obj;
return this.x==p.x&&this.y==p.y;}
return false;}
为什么要重写hashcode()和equals()
- 哈希碰撞会对hashMap的性能带来灾难性的影响。如果多个hashCode()的值落到同一个桶内的时候,这些值是存储到一个链表中的。最坏的情况下,所有的key都映射到同一个桶中,这样hashmap就退化成了一个链表——查找时间从O(1)到O(n)
- 如果某个桶中的记录过大的话,HashMap会动态的使用一个专门的treemap实现来替换掉它。这样做的结果会更好,是O(logn),而不是糟糕的O(n)需要注意的是,这是JDK8才实现的功能。
- hashcode方法.并且两个方法应满足:
1:一致性,即:当两个对象equals比较为true,那么hashcode值应当相等.反之亦然.
因为当两个对象hashcode值相等,但是equals比较为false,那么在HashMap中
会产生链表,影响查询性能.
- 2:成对重写,即重写equals就应当重写hashcode.
转载地址:http://blog.csdn.net/yeiweilan/article/details/73381540