List、Set、Map各自的特点
- 一、结构特点
- 二、实现类
- 三、List、Set、Map区别
- 四、补充知识
- (一)、HashMap 和 HashTable的区别?
- (二)HashMap和HashSet的区别
- (三)ArrayList和LinkedList的区别
- (四)ArrayList与Vector的区别?
- (五) ArrayList的底层是数组,数组的名称是什么?类型是什么?
- (六) ArrayList底层数组的默认初始化容量是多少?当超出这个大小时,每次扩容是多少?
- (七)当向ArrayList集合中添加元素时需要调用add()方法,add()方法的执行流程是怎样的?
- (八)在调用ArrayList的remove(int index)方法时,执行流程是怎样的?
- (九) ArrayList在调用get(int index)方法查询的时候,执行流程是怎样的?
- (十)ArrayList的大小是如何自动增加的?你能分享一下你的代码吗?
- (十一)什么情况下你会使用ArrayList?什么情况下你会选择LinkedList?
- (十二). ArrayList的时间复杂度是多少?
- (十三)HashSet中调用add()时不允许重复原理
- (十四)HashMap执行put()方法时候的原理
- 五、List、set、Map的遍历
一、结构特点
(一)list
1、实现了collection接口
2、允许重复的对象
3、可以有多个空值
4、里边元素有序(即为每个元素的插入顺序)
5、单列数据集合
6、实现类ArrayList、LinkedList 、Vector
(二)Set
1、实现了collection接口
2、元素唯一,不保存重复元素。
3、允许出现空值(只能有一个)
4、单列数据集合
5、元素位置固定(由hashcode决定)
6、元素无序(指不按照用户输入的顺序排序)
7、实现类HashSet、TreeSet、LinkedHashSet、SortedSet等
(三)Map
1、Map是存储键值对这样的双列数据的集合;(key,value)
2、Map中存储的数据是无序(按照hashcode来排的)
3、键不允许重复,值可用重复
4、键只能有一个null,值可用有多个null
5、实现类HashMap、HashTable、LinkedHashMap等
总结: List单列集合,元素:有序、可重复、可为空;
Set单列集合,元素:无序、不重复、只有一个空元素;
Map单列集合,元素:无序、键不重,值可重、可一个空键、多可空值;
二、实现类
(一)List接口有三个实现类
1 LinkedList
基于链表实现,链表内存是散列的,增删快,查找慢;
2 ArrayList
基于数组实现,非线程安全,效率高,增删慢,查找快;
3 Vector
基于数组实现,线程安全,效率低,增删慢,查找慢;
(二)Map接口有四个实现类
1 、HashMap
基于 hash 表的 Map 接口实现,非线程安全,高效,支持 null 值和 null
键;
2 、HashTable
线程安全,低效,不支持 null 值和 null 键;
3 、LinkedHashMap
是 HashMap 的一个子类,保存了记录的插入顺序;
4 、SortMap 接口
5、TreeMap,能够把它保存的记录根据键排序,默认是键值的升序排序
(三) Set接口有两个实现类
1 HashSet
底层是由 Hash Map 实现,不允许集合中有重复的值,使用该方式时需要重写 equals()和 hash Code()方法;
2 LinkedHashSet
继承于 HashSet,同时又基于 LinkedHashMap 来进行实现,底层使用的是 LinkedHashMap
三、List、Set、Map区别
- List 集合中对象按照索引位置排序,可以有重复对象,允许按照对象在集合中的索引位置检索对象,例如通过list.get(i)方法来获取集合中的元素;
- Map 中的每一个元素包含一个键和一个值,成对出现,键对象不可以重复,值对象可以重复;
- Set 集合中的对象不按照特定的方式排序,并且没有重复对象,但它的实现类能对集合中的对象按照特定的方式排序,例如 Tree Set 类,可以按照默认顺序,也可以通过实现 Java.util.Comparator< Type >接口来自定义排序方式。
四、补充知识
(一)、HashMap 和 HashTable的区别?
1、HashMap 是线程不安全的,HashMap 是一个接口,是 Map的一个子接口,是将键映射到值得对象,不允许键值重复,允许空键和空值;由于非线程安全, HashMap的效率要较 HashTable 的效率高一些。HashMap的初始容量和负载因子决定,初始容量16,负载因子0.75。当当前大小和当前容量的比例超过了扩容因子,就会扩容,扩容后大小为 一倍。即每次为2n
2、HashTable 是线程安全的一个集合,不允许 null 值作为一个 key 值或者 Value 值;HashTable 是 sychronize(同步化),多个线程访问时不需要自己为它的方法实现同步,而 HashMap 在被多个线程访问的时候需要自己为它的方法实现同步; 目前不用HashTable,需要线程安全时候用ConcurrentHashMap(分段锁)替代HashTable。HashTable初始容量为11,每次扩容2n+1
(二)HashMap和HashSet的区别
(三)ArrayList和LinkedList的区别
答:ArrayList的底层数据结构为数组,增删慢、查询快,线程不安全,效率高。
LinkedList的底层数据结构为链表,增删快、查询慢,线程不安全,效率高。
备注:一个对象可以被反复存储进List中,每调用一次add方法,这个对象就被插入进集合中一次,其实,并不是把这个对象本身存储进了集合中,而是在集合中用一个索引变量指向这个对象,当这个对象被add多次时,即相当于集合中有多个索引指向了这个对象。
(四)ArrayList与Vector的区别?
答:Vector底层数据结构为数组,增删慢、查询快,线程安全,效率低,每次扩容为原来数组的2倍。
ArrayList底层数据结构为数组,增删慢、查询快,线程不安全,效率高,每次扩容为原来数组的1.5倍。
(五) ArrayList的底层是数组,数组的名称是什么?类型是什么?
答:名称是elementData,类型是Object[],所以ArrayList里面可以存放任意类型的元素。
(六) ArrayList底层数组的默认初始化容量是多少?当超出这个大小时,每次扩容是多少?
答:默认初始化容量是10。当超出这个大小时,每次扩容1.5倍。
(七)当向ArrayList集合中添加元素时需要调用add()方法,add()方法的执行流程是怎样的?
答:调用add()方法时,add()方法首先调用ensureCapacityInternal()来判断elementData数组容量是否足够,ensureCapacityInternal()之所以能够判断,是因为它内部调用了ensureExplicitCapacity()方法,这个方法才是真正判断elementData数组容量是否够用的关键方法。如果容量足够,则直接将元素添加到ArrayList中;如果容量不够,则ensureExplicityCapacity()方法内部会调用grow()方法来对数组进行扩容。扩容成功之后,再将元素添加到ArrayList扩容之后的新数组中。
(八)在调用ArrayList的remove(int index)方法时,执行流程是怎样的?
答:首先判断index是否合理,如果合理的话,会调用System.arraycopy()方法把指定下标到数组末尾的元素向前移动一个单位,并且会把数组最后一个元素设为null。这样是为了方便GC回收。
(九) ArrayList在调用get(int index)方法查询的时候,执行流程是怎样的?
答:首先判断index是否合理,然后调用elementData()方法,elementData()方法返回根据index查到的具体的元素。注意:这里的返回值都经过了向下转型(Object -> E)。
(十)ArrayList的大小是如何自动增加的?你能分享一下你的代码吗?
答:当向ArrayList中增加一个对象的时候,Java首先会判断ArrayList的底层数组elementData是否还有足够的空间来存储这个对象,如果有,就直接存,如果没有,就会基于原有的数组扩容出一个1.5倍的新数组,并将数据全部复制到新数组中。
注意:新建了一个数组,旧数组的对象被复制到了新的数组中,并且现有的数组引用指向新的数组。
(十一)什么情况下你会使用ArrayList?什么情况下你会选择LinkedList?
答:当对数据的主要操作为索引或只在集合的末端增加、删除数据时,使用ArrayList效率比较高;当对数据的操作主要为指定位置的插入或删除操作时,使用LinkedList效率比较高。
(十二). ArrayList的时间复杂度是多少?
答:当修改、查询或者只在数组末尾增、删时,时间复杂度为O(1);对指定位置的元素进行增、删时,时间复杂度为O(n)。
(十三)HashSet中调用add()时不允许重复原理
答:HashSet底层由HashMap实现,插入的元素被当做是HashMap的key,value默认为一个固定的Object,根据hashCode值来确定在集合中的位置,在添加元素时,先判断key的hashCode值是否相同,如果相同,则调用equals()、==进行判断,若相同则覆盖原有元素;如果不同,则直接向Map中添加元素;
(十四)HashMap执行put()方法时候的原理
答:
情况一:调用该元素键的equals方法与该位置上元素的键进行比较,如果返回ture则视新键与已经存在的键相同,用新值去更新旧值,然后put方法返回旧值
情况二:调用该元素键的equals方法与该位置上元素的键进行比较,如果返回false则新键与已经存在的键不相同,任然可以将新的元素存储在该位置。
五、List、set、Map的遍历
(一)遍历形式
list可用for、forEach、Iterator遍历
Set只能用迭代器遍历
Map可用for、forEach、Iterator遍历
(二)数组转换为集合在用迭代器输出
Int [] ={1,2,3,4,5,6};
List list=new ArrayList(Arrays.asList(arr));
Iterator it=list.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
(三)遍历Map的方式
Map<Integer,String> map = new HashMap<Integer,String>();
方式一:只能遍历value,不能遍历key
// System.out.println(m.size());
// for(String s:m.values()) {
// System.out.println(s);
// }
方式二:(推荐使用,尤其在大量数据操作的时候)
// for(Map.Entry<Integer, String> v:m.entrySet()) {
// System.out.println("键:"+v.getKey()+","+"值:"+v.getValue());
// }
方式三:使用map中的entrySet()方式,将map转换为set利用迭代器迭代
// Iterator<Entry<Integer, String>> map1it=m.entrySet().iterator();
// while(map1it.hasNext())
// {
// Map.Entry<Integer, String> entry=(Entry<Integer, String>) map1it.next();
// System.out.println("Key: "+entry.getKey()+" Value: "+entry.getValue());
// }
方式四:根据key二次取值,
// for(Integer key:m.keySet()) {
// System.out.println("键:"+key+","+"值:"+m.get(key));
// }