1. Set和Map
- Set和Map的关系
- Map 2 Set:把Map中的所有key集中起来看,就是一个Set(key不能重复,无序)(Map集合方法:set<K> keySet,返回所有key组成的Set集合)
- Set 2 Map:将key-value捆绑在一起看(value当成key的附属物),并将这么捆绑的对放入Set中,即可实现将Set当做Map来使用。
- HashSet和hashMap
- HashSet:系统采用Hash算法来存储元素;HashMap:系统采用Hash算法来存储key,(value作为key的附属<Entry>)
- 集合中存储的当然是对对象的引用啦,也即集合是多个引用变量的集合
- HashMap:
- 在底层将key-value对当成一个整体来处理,即一个Entry对象。
- HashMap底层采用一个Entry[]数组(table)来保存所有的k-v对(数组元素被称为桶<bucket>)
- transient修饰(来自stackoverflow)
- 1.transient 是表明该数据不参与序列化。因为 HashMap 中的存储数据的数组数据成员中,数组还有很多的空间没有被使用,没有被使用到的空间被序列化没有意义。所以需要手动使用 writeObject() 方法,只序列化实际存储元素的数组。
- 2. 由于不同的虚拟机对于相同 hashCode 产生的 Code 值可能是不一样的,如果你使用默认的序列化,那么反序列化后,元素的位置和之前的是保持一致的,可是由于 hashCode 的值不一样了,那么定位函数 indexOf()返回的元素下标就会不同,这样不是我们所想要的结果.
- HashMap的实际容量(capacity):大于initialCapacity的、最小的2的n次方法值;
- 极限容量:capacity*loadFactor(loadFactor为负载因子,是时间、空间成本的折中,0.75)
- transient修饰(来自stackoverflow)
- 根据Hash算法来决定Entry对象的存储位置(在哪个桶中);取Entry也是如此
- Hash冲突时,通过“Entry链”来解决。Entry对象可以包含一个引用变量<Entry构造器中的最后一个参数>(指向下一个Entry),构成链
- 新添加的Entry位于链的头部
- HashSet的底层是用一个HashMap来处理的,key为Set中的元素,value存储了一个静态的Object对象
- 向hashMap中添加key相同(hashCode和equals方法返回值都相同)的entry,将会将新的value值覆盖旧的value值;hashSet中添加相同的key时,不会覆盖,key还是原来的key(其value存储了的静态的Object对象会覆盖)(hashMap中的key也是原来的key)
- 添加一个key相同的元素时,value会覆盖,但是key不会改变(key还是原来的key,即key不会被覆盖)
- key相同指的是(hashcode、equals方法返回相同的元素)
- 当将一个对象当成HashMap的key/存入HashSet,要重写hashcode、equals方法,并且两个方法的返回值保持一致。
- 向hashMap中添加key相同(hashCode和equals方法返回值都相同)的entry,将会将新的value值覆盖旧的value值;hashSet中添加相同的key时,不会覆盖,key还是原来的key(其value存储了的静态的Object对象会覆盖)(hashMap中的key也是原来的key)
- TreeMap和TreeSet
- TreeSet底层是通过TreeMap实现的
- TreeMap底层是通过一个红黑二叉树实现的,每个Entry都是二叉树的一个节点。即TreeMap本质上讲就是一个“红黑树”,其中的每个Entry就是该红黑树的一个节点。
2. Map 和 List
- Map的Values()方法
- 返回的并不是一个List集合,而是一个Values(内部类)集合对象,该集合类并不能添加元素,主要用于遍历所有value
- 即values()方法返回的是一个不存储元素的Collection集合,当程序遍历该集合时,实际上就是遍历Map对象的value
- Map和List的关系
- 使用上相似,底层并没有多大的相似之处
- 使用上:既可以说List相当于所有key都是int类型的Map,也可以说Map相当于索引是任意类型的List
3. ArrayList和LinkedList
- Vector——Stack;Deque(双端队列)——ArrayDeque<推荐>
- Vector和Arraylist的区别 (共同点:实现List接口,底层是基于Java数组的)
- Arraylist的数组用了transient修饰
- transient修饰,序列化对象时不会直接序列化,而是可以通过方法来实现定制序列化
- Vector线程安全,但是已基本被替代
- 需要线程安全时,也要避免使用Vector。考虑通过Collections工具类的synchronizedList方法(有一系列包装方法)将普通Arraylist包装成线程安全的Arraylist
- Arraylist的数组用了transient修饰
- Arraylist和Linkedlist的实现差异
- Arraylist底层数组实现:添加、删除性能差;获取指定位置元素的性能好
- LinkedList本质上就是一个双向链表。底层通过内部类Entry实现。不涉及到具体位置情形时,性能很好。涉及到具体位置情形时,性能较差(需要挨个查找)
- ArrayList整体性能优于LinkedList,大部分时使用ArrayList。如果经常涉及到 添加、删除,尤其经常add(E e)时,考虑LinkedList
4.Iterator迭代器(接口)
- “迭代器模式”。Java要求各集合都提供一个iterator()方法返回一个Iterator用于遍历该集合中的元素,至于返回的Iterator到底是哪个实现类,程序并不关心
- 系统为遍历多种数据列表、集合、容器提供一个标准的“迭代器接口”,这些数据列表、集合、容器就可以面向相同的接口编程来访问元素。不同的数据列表、集合、容器如何实现这个“接口”,则交给它们自己去实现
- 迭代时删除指定元素
- 通常情况下,在进行迭代(遍历操作)时不应该进行删除(迭代时,迭代器起的主要是遍历的作用,并没有保留迭代的元素),否则ConcurrentModificationException(next()方法中的checkForModification()方法中抛出)
- 特殊情况下遍历List集合的倒数第2个元素时,Set集合的最后一个元素时,可以删除。
- why:List,如果此时恰好是在遍历倒数2个元素,删除元素将会使size变为size-1,遍历会提前结束(hasNext()方法确定),就不胡调用next(),因此不会抛出异常;Set,遍历最后一个元素时,本身就是最后一个,即不会再去next,因此,删除也不会异常