list,set,map总结
学习了集合,脑子里list,set,map之间的关系有混乱,在这里整理一下.有兴趣的朋友可以看下.
先看下 list,set,map各自的特点
List:
索引数组:在内存空间上是连续的,计数也是连续的,总体就是有序的.下标是从0开始的,挨个递增的数组,可以通过数字下标找到相应的元素
关联数组:在内存空间上是连续的,计数不是连续的,就是无序的.下标是字符串的数组,可以通过下标这个字符串去找相对应的元素
ArrayList():
List接口的大小可变数组的实现.实现了所有可选列表操作,并允许包括null在内的所有元素.
每个ArrayList实例都有一个容器.该容器是指用来存储表元素的数组的大小.它总是至少等于列表的大小.随着向ArrayList中不断添加元素,其容量也自动增长.并未指定增长策略的细节,因为这不只是添加元素带来分摊固定时间开销那样简单
ArrayList集合底层默认初始化容量是10,扩大容量后是原始容量的1.5倍;
Vector:默认也是10,扩大容量是原来的2倍
ArrayList是Vector的升级,所以Vector已经废弃
针对数组代码的优化,尽量不要进行扩容操作,在建立集合的开始,就按照预估值指定初始化容量
List集合的遍历方式:
while-迭代器遍历
for-迭代器遍历
foreach-迭代遍历
for-get遍历(只有List才可以,set,map都不可以)
LinkedList:
List 接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括 null)。除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾 get、remove 和 insert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列。
此类实现 Deque 接口,为 add、poll 提供先进先出队列操作,以及其他堆栈和双端队列操作。
所有操作都是按照双重链接列表的需要执行的。在列表中编索引的操作将从开头或结尾遍历列表(从靠近指定索引的一端)。
注意,此实现不是同步的。如果多个线程同时访问一个链接列表,而其中至少一个线程从结构上修改了该列表,则它必须 保持外部同步。(结构修改指添加或删除一个或多个元素的任何操作;仅设置元素的值不是结构修改。)这一般通过对自然封装该列表的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedList
方法来“包装”该列表。最好在创建时完成这一操作,以防止对列表进行意外的不同步访问,如下所示:
List list = Collections.synchronizedList(new LinkedList(...));
注:LinkedList 参考于 JDK API 1.6.0 中文版
Set:
HashSet:
为什么会有hash(散列)?
散列数据结构,但是java这里屏蔽了
直接就用封装的形式把他封装到了 HashSet,HashMap,HashTable中,其中HashTable已经过时了
hash算法:java中指的hashCode()函数及其重写
目标:生成一个唯一的标识符
根据对象的特点为每个对象生成一个唯一的hashCode,然后把这个hashCode作为下标,把对象作为元素值保存到数组中.这个时候这个数组就可以看做关联数组.java中没有关联数组这个概念,单独取了个名HashSet
hash的目的就是为了查询快:hash值是一个固定位数值,整型值.所以在定长数据的情况下,查询是极快的,用数组也行,但是数组只能保存同一数据类型,HashSet能保存任意数据类型
Hash的过程:
拿到对象,调用他自己的hash算法,生成一个唯一的hash值,然后把这个值保存到数组中,整个过程就是hash.数组,和唯一值,和对象的关系,我们就叫HashSet;
hash算法在java中就是指Object中的hashCode()函数里面的算法,这一个是你根据你的类来自己定义的
为什么无序,为什么不可重复?
1 生成的hashCode没有一定大小关系,就无所谓顺序
2 因为每个对象都必须生成一个唯一的hashCode,如果是重复的,肯定生成一个同一个hashCode.这个时候就没有办法唯一性查询出某个数据.所以不可重复
HashSet 是HashMap的一个实现,本质是HashMap.是HashMap集合中key部分,所以所以他们都需要重写Object中的hashCode()和equals();
为什么需要重写hashCode()和equals()?
在Object类中,原生的hashCode()方法,是把内存地址作为原始数据进行hashCode()运行得到的hash值.但是在实际中我们不可能知道每个对象的内存地址,如果我们用了内存地址,hash后就成了一个没有任何意义的hash数组(hash表).没有任何意义.我们如果需要比较大小的话,就只能根据类中给每个对象指定那个唯一的特征码进行运算,如:数据库中的ID,或者任何一个特殊的编号.你可以用类的地址作为hash原始值.equals()也一样,是对两个对象的内存地址进行比较,而实际中相等判断肯定不是比较内存地址,所以也需要按照某个属性进行重写,这样才有意义
SortedSet:
SortedSet集合存储元素为什么可以自动排序?
因为被存储的元素(对象)实现了Compareable借口;
Oracle编写的TreeSet集合在添加元素的时候,会自动调用CompareTo方法完成比较.
集合Tree类型底层是红黑树,在装入之前就排序,排序有三种(两个借口):
1 java.lang.Compareable;里面compareTo()方法,需要把compareTo()加载重写在对象里面
这种方法需要对象类去实现Comparable借口,重写方法
2 让SortedSet集合做到排序还有另外一种方法:java.util.Comparator;比较器借口;写一个比较器在元素加载之前先进行比较
3 写一个匿名比较类,但是本质还是java.util.Comparator比较器类
Map:
Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个 value。Map接口提供3种集合的视图,Map的内容可以被当作一组key集合,一组value集合,或者一组key-value映射。
Map集合和Collection不一样,但是操作基本一样,只是有一些细微的差别
增:
Object put(Object key, Object value);
向集合中添加键值对,也就是映射关系对象
删
void clear();
清空Map;
改
数据类型转换
查
int size(); //获取Map中键值对的个数
boolean isEmpty();//判断是否为空
Object get(Object key);//通过key获取对应对象的值
Collection values();//获取map集合中所有value对象的集合
boolean containsKey(Object key);//判断集合中是否有这样的key键
boolean containsValue(Object value)//判断集合中是否有这样的值
Set keySet();//获取集合中所有的key,以集合方式返回
Set entrySet;//返回此映射中包含的映射关系,以Set集合方式返回
V remove(Object key) 如果存在一个键的映射关系,则将其从此映射中移除(可选操作)
怎么向Hash表中添加元素?
1 要添加的时候,HaspMap保存的是映射关系,这个映射关系靠什么来维护(Map.Entry(K,V)),是一个借口,那我们就可以传入各种对象的映射关系.
如果生成了重复的值,就会往HashTable同位上加一个单项链表
单项链表中每个对象都是一个Entry.
单向链表节点:有一个Entry(key,value)
final key
下一个链表节点的内存地址
2 添加过程:先调用要存储的映射关系的key对象,然后调用key对象自身的hashCode(),生成hash码,向数组中添加元素如果没有这个hash码,就占用一个数组的空间,保存这个key-value映射对象Entry.如果数组中已经存在了这个hash码,就把数组中当前保存的那个节点向后移动一位,再把当前的这个映射关系对象Entry保存到数组中.equals()比较一次,比较是key对象生成的hash码
equals(),把键的值挨个在链表中进行比较,如果返回true那么就代表该链表中已经有了这个key值,就放弃添加,如果没有则返回false,然后连值一起放到当前hash值得链表
下面这张图是向Hash表中添加元素的图解