目录
1、List和Set的区别
2、ArrayList和LinkedList区别
3、HashMap和HashTable的区别?底层实现是什么?
4、ConcurrentHashMap原理,jdk7和jdk8版本的区别
5、ArrayList 和 Vector 的区别。
6、hashmap 的数据结构
7、HashMap 的工作原理是什么?
8、Hashmap 什么时候进行扩容呢?
9、Set 里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用 == 还是equals()? 它们有何区别?
10、两个对象值相同 (x.equals(y) == true),但却可有不同的 hash code,这句话对不对?
11、Java 集合类框架的基本接口有哪些?
12、HashSet 和 TreeSet 有什么区别?
13、HashSet 的底层实现是什么?
14、LinkedHashMap 的实现原理?
15、为什么集合类没有实现 Cloneable 和 Serializable 接口?
16、 Iterator 和 ListIterator 的区别是什么?
17、数组 (Array) 和列表 (ArrayList) 有什么区别?什么时候应该使用 Array 而不是ArrayList?
18、Set 里的元素是不能重复的,那么用什么方法来区分重复与否呢?是用 == 还是equals()?它们有何区别?
19、 Comparable 和 Comparator 接口是干什么的?列出它们的区别。
20、Collection 和 Collections 的区别。
List 有序,按对象进行的顺序保存对象,可重复,允许多个Null元素对象,可以使用Iterator取出所有元素,在逐一遍历,还可以使用get(index)获取指定下标的元素。
Set:无序,不可重复,最多允许有一个Null元素对象,取元素时只能用Iterator接口取得所有元素,在逐一遍历各个元素。
ArrayList:基于动态数组,连续内存存储,适合下标访问(随机访问),扩容机制,因为数组长度固定,超过长度存放时需要新建数组,然后将老数组拷贝到新数组,如果不是尾部插入数据还会涉及到元素的移动(往后复制一份,插入新元素),使用尾插法并指定容量可以极大的提升性能,甚至超过linkedList(需要创建大量的node对象)
LinkedList:基于链表,可以存储在分散的内存中,适合做数据插入及删除操作,不适合查询;需要逐一遍历
遍历LinkedList必须使用Iterator不能使用for循环,因为每次for循环体内通过get(i)获取某一个元素时需要对list重新遍历,性能消耗极大。
另外不要试图使用indexof等返回元素索引,并利用其进行遍历,使用indexOf对list进行遍历,当结果为空时会遍历整个列表。
区别:
1、HashMap方法没有synchronized修饰,线程非安全,HashTable线程安全;
2、HashMap允许key和value为空,而HashTable不允许
底层实现:数组+链表实现 jdk8开始链表高度到8,数组长度超过64,链表转变为红黑树,元素以内部类node节点存在
计算key的hash值,二次hash然后将数组长度取模,对应到数组下标
如果没有产生hash冲突(下标位置没有元素),则直接创建node存入数组;
如果产生hash冲突,先进行equal比较,相同则取代该元素,不同,则判断链表高度插入链表,链表高度达到8,并且数组长度到64则转变为红黑树,长度低于6则将红黑树转回链表。
key为null,存在下标0的位置
数组扩容
原文链接:https://youzhixueyuan.com/concurrenthashmap.html
总结
其实可以看出JDK1.8版本的ConcurrentHashMap的数据结构已经接近HashMap,相对而言,ConcurrentHashMap只是增加了同步的操作来控制并发,从JDK1.7版本的ReentrantLock+Segment+HashEntry,到JDK1.8版本中synchronized+CAS+HashEntry+红黑树。
1.数据结构:取消了Segment分段锁的数据结构,取而代之的是数组+链表+红黑树的结构。
2.保证线程安全机制:JDK1.7采用segment的分段锁机制实现线程安全,其中segment继承自ReentrantLock。JDK1.8采用CAS+Synchronized保证线程安全。
3.锁的粒度:原来是对需要进行数据操作的Segment加锁,现调整为对每个数组元素加锁(Node)。
4.链表转化为红黑树:定位结点的hash算法简化会带来弊端,Hash冲突加剧,因此在链表节点数量大于8时,会将链表转化为红黑树进行存储。
5.查询时间复杂度:从原来的遍历链表O(n),变成遍历红黑树O(logN)。
这两个类都实现了 List 接口( List 接口继承了 Collection 接口),他们都是有序集合,即存储在这两个集合中的元素的位置都是有顺序的,相当于一种动态的数组,我们以后可以按位置索引号取出某个元素,并且其中的数据是允许重复的,这是HashSet 之类的集合的最大不同处, HashSet 之类的集合不可以按索引号去检索其中的元素,也不允许有重复的元素(本来题目问的与 hashset 没有任何关系,但为了说清楚 ArrayList 与 Vector 的功能,我们使用对比方式,更有利于说明问题)。接着才说 ArrayList 与 Vector 的区别,这主要包括两个方面。
同步性: Vector 是线程安全的,也就是说是它的方法之间是线程同步的,而 ArrayList 是线程序不安全的,它的方法之间是线程不同步的。如果只有一个线程会访问到集合,那最好是使用 ArrayList,因为它不考虑线程安全,效率会高些;如果有多个线程会访问到集合,那最好是使用 Vector,因为不需要我们自己再去考虑和编写线程安全的代码。
备注:对于 Vector&ArrayList、 Hashtable&HashMap,要记住线程安全的问题,记住 Vector 与 Hashtable 是旧的,是 java 一诞生就提供了的,它们是线程安全的, ArrayList 与 HashMap 是 java2 时才提供的,它们是线程不安全的。所以,我们讲课时先讲老的。
数据增长:
ArrayList 与 Vector 都有一个初始的容量大小,当存储进它们里面的元素的个数超过了容量时,就需要增加 ArrayList 与 Vector 的存储空间,每次要增加存储空间 时,不是只增加一个存储单元,而是增加多个存储单元,每次增加的存储单元的个数在内存空间利用与程序效率之间要取得一定的平衡。 Vector 默认增长为原来两倍,而ArrayList 的增长策略在文档中没有明确规定(从源代码看到的是增长为原来的 1.5倍)。 ArrayList 与 Vector 都可以设置初始的空间大小, Vector 还可以设置增长的空间大小,而 ArrayList 没有提供设置增长空间的方法。 总结:即 Vector 增长原来的一倍, ArrayList 增加原来的 0.5 倍。
在 java 编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的, hashmap 也不例外。 Hashmap 实际上是一个数组和链表的结合体(在数据结构中,一般称之为 “链表散列 “)
java中的HashMap是以键值对(key-value)的形式存储元素的。HashMap需要一个hash函数,它使用hashCode()和equals()方法来向集合、从集合添加和检索元素。当调用put()方法的时候,HashMap会计算key的hash值,然后把键值对存储在集合合适的索引上。如果key已经存在了,value会被更新成新值。HashMap的一些重要的特性是它的容量(capacity),负载因子(load factor)和扩容极限(threshold resizing)。
当 hashmap 中的元素个数超过数组大小 loadFactor 时,就会进行数组扩容,loadFactor 的默认值为 0.75,也就是说,默认情况下,数组大小为 16,那么当 hashmap 中元素个数超过 160.75=12 的时候,就把数组的大小扩展为 216=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知 hashmap 中元素的个数,那么预设元素的个数能够有效的提高 hashmap 的性能。比如说,我们有 1000 个元素 new HashMap(1000),但是理论上来讲 new HashMap(1024) 更合适,不过上面 annegu 已经说过,即使是 1000, hashmap 也自动会将其设置为 1024。 但是 new HashMap(1024) 还不是更合适的,因为 0.75*1000 < 1000, 也就是说为了让 0.75 * size > 1000, 我们必须这样 new HashMap(2048) 才最合适,既考虑了 & 的问题,也避免了 resize的问题。
Set 里的元素是不能重复的,元素重复与否是使用 equals() 方法进行判断的。equals() 和 == 方法决定引用值是否指向同一对象 equals() 在类中被覆盖,为的是 当两个分离的对象的内容和类型相配的话,返回真值。
对。如果对象要保存在 HashSet 或 HashMap 中,它们的 equals 相等,那么,它们的 hashcode 值就必须相等。 如果不是要保存在 HashSet 或 HashMap,则与 hashcode 没有什么关系了,这时候 hashcode 不等是可以的,例如 arrayList 存储的对象就不用实现 hashcode,当然,我们没有理由不实现,通常都会去实现的。
集合类接口指定了一组叫做元素的对象。集合类接口的每一种具体的实现类都可以选择已它自己的方式对元素进行保存和排序。有的集合类允许重复的键,有些不允许。java集合类提供了一套设计良好的支持对一组对象进行操作接口和类。java集合类里面最基本的接口有:
Collection:代表一组对象,每一个对象都是它的子元素。
Set:不包括重复元素的Collection。
List:有顺序的Collection,并且可以包含重复的元素。
Map:可以把键(key)映射到值(value)的对象,键不能重复。
HashSet 是由一个 hash 表来实现的,因此,它的元素是无序的。 add(),remove(), contains() TreeSet 是由一个树形的结构来实现的,它里面的元素是有序的。因此, add(),remove(), contains() 方法的时间复杂度是 O(logn)。
通过看源码知道 HashSet 的实现是依赖于 HashMap 的, HashSet 的值都是存储在 HashMap 中的。在 HashSet 的构造法中会初始化一个 HashMap 对象,HashSet 不允许值重复,因此, HashSet 的值是作为 HashMap 的 key 存储在HashMap 中的,当存储的值已经存在时返回 false。
LinkedHashMap 也是基于 HashMap 实现的,不同的是它定义了一个 Entry header,这个 header 不是放在 Table 里,它是额外独立出来的。 LinkedHashMap 通过继承 hashMap 中的 Entry, 并添加两个属性 Entry before,after, 和 header 结合起来组成一个双向链表,来实现按插入顺序或访问顺序排序。 LinkedHashMap 定义了排序模式 accessOrder,该属性为 boolean 型变量,对于访问顺序,为 true;对于插入顺序,则为 false。一般情况下,不必指定排 序模式,其迭代顺序即为默认为插入顺序。
克隆(cloning)或者是序列化(serialization)的语义和含义是跟具体的实现相关的,因此,应该由集合类的具体实现来决定如何克隆或者是序列化。
下面列出了他们的区别:
Iterator 可用来遍历 Set 和 List 集合,但是 ListIterator 只能用来遍历 List。Iterator 对集合只能是前向遍历, ListIterator 既可以前向也可以后向。 ListIterator 实现了 Iterator 接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引,等等。
Array 可以包含基本类型和对象类型, ArrayList 只能包含对象类型。 Array 大小是固定的, ArrayList 的大小是动态变化的。 ArrayList 处理固定大小的基本数据类型的时候,这种方式相对比较慢。
ArrayList 处理固定大小的基本数据类型的时候,这种方式相对比较慢。
Set 里的元素是不能重复的,那么用 iterator() 方法来区分重复与否。 equals() 是判读两个 Set 是否相等 equals() 和 == 方法决定引用值是否指向同一对象 equals() 在类中被覆盖,为的是当两个分离的对象的内容和类型相配的话,返回真值
Java 提供了只包含一个 compareTo() 方法的 Comparable 接口。这个方法可以个给两个对象排序。具体来说,它返回负数, 0,正数来表明输入对象小于,等于,大于已经存在的对象。 Java 提供了包含 compare() 和 equals() 两个方法的 Comparator 接口。compare() 方法用来给两个输入参数排序,返回负数, 0,正数表明第一个参数是小 于,等于,大于第二个参数。 equals() 方法需要一个对象作为参数,它用来决定输入参数是否和 comparator 相等。只有当输入参数也是一个 comparator 并且输入参数和当前 comparator 的排序结果是相同的时 候,这个方法才返回 true。
collection 是集合类的上级接口, 继承与它的接口主要是 set 和 list。 collections 类是针对集合类的一个帮助类. 它提供一系列的静态方法对各种集合的搜索, 排序, 线程安全化等操作。