两大类:collection接口实现的set、list、queue。Map接口实现的。
List:不唯一。
ArrayList:
原理:动态数组实现,和数组的差别就在动态两个字。面试比较多的是扩容逻辑。(逻辑有点像分页逻辑,要考虑两头)默认是10,按照原来的容量1.5倍进行扩容,如果不够,那就设置为需要的大小,如果大于最大INT整数,则设置最大值。
哪种循环最快:(总共有4种。和数据量有关系。一般情况用增强for循环或者迭代器都可以(不是随机索引for),还有java8推出的foreach+lambd,随着数据量越多越快,但是需要jvm预热,即第一次很慢,后续就很快)
新增删除查询效率:
支持快速随机访问。(即可以通过索引快速访问元素。因为是数组实现的。)
新增和删除,涉及到扩容的情况,数组需要进行内存的复制,元素的移动,所以通常情况是效率不高。
【 另外关于删除和新增的效率问题,这里需要特别注意,就是在数组尾部添加数据时,如果不需要扩容,是非常快的,但是实际情况,扩容是动态的,所以才有了查询快和新增删除效率不高这种说法。】
应用场景:想想数组的应用场景。数组可以包含对象。
线程安全替代方案:
1、JUC框架中的 copyonwriteArrayList类代替。
2、vector(淘汰,性能太低)
3、Collections.syncronizedList.
编程建议:
1、能预估到容量大小,并且容量会特别大的情况,那最好是初始化容量。原因是初始容量是10,当调用add方法时会不停的扩容,不停的创建新数组再复制,造成无形的新能损耗。
Linkedlist:
原理:双向链表实现。删除只需要修改前后节点指针的引用即可。关键点是理解链表的数据结构特点。节点保存了前后节点的信息。空间占用更多。但是增加删除更加效率。
应用场景:一般是当做队列或者栈的场景去使用。,一般不需要随机访问(即以下标直接访问),增删多的可以使用linkedlist(现实情况是你要增删的前提是需要查找)。
Queue: 队列。
fifo特性。(先进先出)
Map:
HashMap:
原理:数组+链表实现,键值对的哈希结构,
key通过计算hashcode,可以实现快速查找。
也可以实现快速插入和删除。这里不得不说数据经过hash处理后,变得更容易实现某些功能了。
另外说说扩容,参照hashset.
应用场景:存储键值对。但是没有顺序。
线程安全:cocurrentHashMap.
编码建议:同hashList。初始容量是16。
LinkedHashMap:
原理:同上。
应用场景:保存数据的插入顺序。(可以通过设置访问属性,调整数据顺序)
TreeMap:
原理:红黑树实现,key有序的map集合。要理解treemap,需要了解红黑树。大概的功能就是实现排序,并且兼顾效率。
应用场景:按照整数自然顺序进行排序。(可以直接实现比较器,有点类似treeSet)
Set:唯一。
HashSet:
原理:底层数据结构是hashMap,所有值存储在hashMap的key上 ,hashcode计算散列值,获取存储的位置,equals判断是否进行覆盖。原始容量2的n次幂(n=4)= 16。扩展因子0.75。即容量占用0.75的时候,就开始扩展。扩展到2的进一次幂(5)。
应用场景:快速查找。因为获取散列值的计算是O(1)【当然相同hash值需要再查找,但是这种情况不多】。
TreeSet:
原理:底层也是hashMap,但是可以实现排序。
应用场景:排序。
hashcode和equals.
hashmap和hashset中是根据传入数据的hashcode方法获取到hash值,并根据这个值计算出在数组中的位置。在hashmap中,如果两个hashcode相同,再比较equals,判断是否相同,不相同才算真正不相同。
另外一个问题:为啥重写equals要重写hashcode。理解这个问题需要思考现实中的需求。一般重写equals说明两个值是相等的,当把两个相等的值放到hashmap或者hashset中,我们肯定是希望值放入一个,但是如果没有重写hashcode,因为hashmap和set的规则是根据hashcode来判断是否一致的,所以为了语义的统一,重写equals方法都要重写hashcode。
有了上述限定后,可以得到这样的关系。equals相等,hashcode必定相等。hashcode相等,equals不一定相等。hashcode不相等,equals必定不相等。equlas不相等,hashcode必定不相等。
总结起来就是:equals可以决定hashcode(是否相等),hashcode决定不了equals.