JAVA集合

1、Collection接口

常用方法:

add() 添加元素、size() 获取集合中元素的个数、addAll() 把一个集合的元素添加到一个新的集合
isEmpty() 判断当前集合是否为空,判断的是集合中是否有元素、contains() 判断是否包含
containsAll() 判断集合coll1中是否包含coll集合中的所有元素、remove() 删除指定元素
removeAll() 删除当前集合中包含另一个集合中的所有元素
clear() 清空集合中的元素、retainAll() 获取两个集合的交集、equels() 比较两个集合是否相等

hashCode() 返回当前对象的哈希值、toArray() 集合转成数组、Arrays.asList() 数组转成集合

特殊方法:iterator()迭代器和foreach

Collection coll = new ArrayList();
        //add() 添加元素
        coll.add("AA");
        

        //iterator()   迭代器
        Iterator iterator = coll.iterator();
        

        //hasNext() 判断是否有下一个元素
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
        
        //foreach
        //for(集合中的每一个元素 : 集合对象)
        for (Object o : coll) {
            System.out.println(o);
        }

2、Collection -> List 接口(ArrayList、LinkedList、Vector)

3、Collection接口 -> Set接口(TreeSet、HashSet

(1)不包含重复元素的集合接口,list包含重复元素,

(2)最多包含一个null,而list可以包含多个

(3)set保存的元素无序(具体是否有序由实现子类决定),list接口保存的元素有序

HashSet类:

底层是用hashmap实现,并实现了set接口,用key值来存放数据,value值存放虚值。

当hashset用来保存自定义对象时,如果要保证hashset保存的元素无序且不重复,需要对象类实现equals方法和hashcode方法。

【解释:使用HashSet来保存自定义对象时,HashSet会使用对象的hashCode()方法来计算元素的hash值,并根据该值来将元素存储在内部的哈希表中。在判断元素是否已经存在时,HashSet会使用equals()方法来比较元素是否相等。

如果我们没有在自定义对象中实现equals()和hashCode()方法,HashSet会默认使用Object类中的equals()和hashCode()方法,这样就会导致HashSet无法正确地判断元素是否相等。因为Object类中的equals()和hashCode()方法只是简单地比较对象的引用地址和hash值,而不是比较对象的内容。】

TreeSet类:

1、保存字符串的时候是有序的,调用了String类的比较接口实现的CompareTo方法定义的规则

2、保存的数据不能为空、保存的数据不能重复

3、有序的话需要实现Comparable接口,或者定义一个实现Comoarator接口的比较器类

存储String类字符串:有序是需要一个比较器,这个比较器需要自己根据排序需求构造,定义比较器类实现Comoarator接口,当然在存储String类型的时候,String类实现了Comparable接口,重写的CompareTo方法是遵循字典排序的。

【这里说一下Comparable接口和Comparator接口的区别】

迭代器:正向迭代器(Iterator)、正反向迭代器(ListIterator)

数据容器很多,有数组、链表、树等等,对数据容器中的元素进行遍历也对应的有各自的方法,对不同数据遍历时核心思想是寻找下一个元素,但要实现两套不同的代码,显然这是不优秀的。

有了迭代器,我们可以将算法和特定的容器分离开来。它可以遍历并选择序列中的对象,而我们不需要了解该序列的底层结构。也就是说功能代码我们只需要实现一套就行。

迭代器只能用在Collection类

正反向迭代器只能用在Collection下的List类

1、正向迭代器:通过iterator() 方法返回得到一个迭代器,注意:iterator() 方法java.lang.Iterable 接口,被 Collection 继承。

    (1) 使用next() 获得序列中的下一个元素。

    (2) 使用hasNext() 检查序列中是否还有元素。

    (3) 使用remove() 将迭代器新返回的元素删除。

2、正反向迭代器是为List类设计的,set接口和map接口都没有实现此接口,支持反向迭代,但反向迭代之前需执行正向迭代,相当于指针要移动到最后一个,才可以反向迭代。

(1)hasPrevious()方法   (2)previous()方法组合

Map<K,V>接口

Map类没有迭代器,所以对Map类的遍历是一个问题,主要通过两种方式,利用set类的迭代器来实现遍历。

(1)将Map中所有的key值存到set集合中,然后通过set的迭代器器加上getValue方法实现遍历。

                        Set keySet():

(2)将Map集合中的映射关系存入到了Set集合中,而这个映射关系的数据类型就是 Map.Entry。Map.Entry:其实Entry也是一个接口,它是Map接口中的一个内部接口。

                        Set<Map.Entry<K,V>> entrySet():

/*// 方法一:通过keySet()获取Map集合元素
	// 先获取Map集合的所有键的Set集合,通过keySet()方法获取到
	Set<String> keySet=map.keySet();
	
	// 有了键的Set集合,就可以获取其迭代器
	Iterator<String> it=keySet.iterator();
	
	
	// 方法二:通过entrySet()获取Map集合元素
	// 先获取Map集合中的映射关系的Set集合,通过entrySet()方法获取到
	Set<Map.Entry<String,String>> entrySet=map.entrySet();
	
	// 有了键的Set集合,就可以获取其迭代器
	Iterator<Map.Entry<String,String>> it=entrySet.iterator();
	

Map<K,V>的实现类主要有HashMap,HashTable两类

HashMap、HashTable、ConCurrentHashMap区别对比:

HashMap详解:

  • 1、get()和put()的具体执行过程和原理?
  • 2、equals()和hashnode()都有什么作用?
  • 3、hash()【取key的hashcode(),高位运算,取模运算】的实现是什么样的?为什么要这样实现?
  • 4、数组扩容的时机和具体流程是什么样的?
  • 5、数组长度为什么要是2的幂次方?
  • 6、链表树化的时机?
  • 7、什么时候会进行红黑树拆分?
  • 8、为什么一定要用红黑树,而不是其他树,比如AVL树

HashMap会导致什么样的并发问题?(为什么线程不安全)

 (1)put的时候导致多线程数据不一致。

        线程A通过hashcode计算得到最终的存储位置时,时间片用完,线程B刚好得到了同样的位置索引并成功存放,线程A重新执行时对此一无所知,会覆盖掉线程B存储的内容。

(2)HashMap的get操作可能因为resize引起死循环。

线程1和线程2在resize过程中形成了循环链表,而刚好get方法获取时造成了死循环。

 

HashMap:JDK1.8之后,内部实现是数组+链表(红黑树),结构图如下:

HashMap的插入原理:put()方法流程 

HashMap初始化怎么设定初始容量的大小

答:一半初始化不传值默认为16,负载因子是0.75,如果穿值,则会初始化为大于K的2的整数次方,例如穿传的是10,大小则会初始化为16。

HashMap中如何确定数组索引位置:

答:对key取哈希值,高位运算,取模运算,得到数组索引位置,HashMap的扩展为什么一定要是2的次方?目的是为了减少哈希冲突,使table中的数据分布更均匀,取模运算设计为(n-1)&hash   

HashMap的hash函数是怎么设计的:                                                                                               答:HashMap使用的是自定义的算法,当我们进入put方法查看时,看见put方法中return了一个叫putVal的方法,此方法前面把这个key传进去又调用了一个叫hash的方法,这个是它自己的方法,点进去之后会发现,他又将这个key获取了一个hashcode,然后把它右移了16位,跟当前的hashcode做了一个异或。 

HashMap和Hashtable的共同点:

  • HashMap和Hashtable都是java.util包下的类
  • HashMap和Hashtable都实现了Map接口,存储方式都是key-value形式
  • HashMap和Hashtable同时也都实现了Serializable和Cloneable接口
  • HashMap和Hashtable的负载因子都是0.75

HashMap和Hashtable的区别:

(1)HashMap是非线程安全的,Hashtable是线程安全的。HashTable的方法都是synchronized关键字修饰,所以导致并发效率较低,而HashMap虽然线程不安全,但并发效率较高。

(2)HashMap允许null作为键或值,Hashtable不允许,运行时会报NullPointerException

(3)HashMap添加元素使用的是自定义的hash算法,HashTable使用的是key的hashcode

(4)HashMap在数组+链表中引入了红黑树,HashTable没有

(5)HashMap的初始容量为16,而HashTable为11

问题:Hashmap的初始化容量如何确定

(6)HsahMap扩容是当前容量翻倍,Hashtable是当前容量翻倍+1

问题:HashMap的扩展为什么一定要是2的次方?

(7)HashMap只支持Iterator遍历,Hashtable支持Iterator和Enumeration

遍历HashMap只能借助Collection的Iterator迭代器,用keyset还有一个叫Entryset的两个方法得到

(8)HashMap和HashTable的部分方法不同,HashTable有contains方法,而HashMap有containsKeys和containsValue

ConcurrentHashMap

为什么需要ConcurrentHashMap?

上面的比较可以看出,HashMap和HashTable都不能很好地实现多线程下的高效操作,HashMap本身线程不安全,而HashTable由于同步代码块的设置导致效率较低。所以ConcurrentHashMap的优点就是HashMap和HashTabel的缺点。

实现多线程的方式:(jdk1.7)

1、分段锁,在ConcurrentHashMap中进行了分段(Segment),在并发操作时,只需要对数据所在的Segment加锁即可,不像HashTable是对整个HashTable加锁。里面则是HashEntry的数组和HashMap类似。

2、并行度,决定并行度的其实就是分段的多少,分段越多,理论上同时支持的并发数越高,concurrencyLevel默认为16,如果输入的类似15这种非2的整次幂,会自动调整为大于此数的整次幂。默认16,则在ConcurrentHashMap内部分了16个Segment,最多同时支持16个线程并发写。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

智博的自留地

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值