集合技术疑难点总结

博客介绍了Java集合接口,集合接口不提供成员变量,只提供操作抽象方法。Collection和Map是直接父接口,List和Set是Collection子接口。List有序可重复,实现类用数组和链表;Set无序不可重复,实现类用哈希表和二叉树,还提及集合的API和工具类。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.集合的接口不提供任何成员变量(数据结构),只提供对数据操作的抽象方法,具体实现和数据结构的设计,要看具体子类

2.Collection,Map是直接父接口,他们定义的是最通用的接口方法

3.List,Set是 collection接口的子接口,他们在Collection的基础上 去提供自己独有的方法而set接口几乎完全 重新 声明 Collection接口,没增加任何接口方法,目的应该是 与 List共同 作为 Collection的子接口,便于开发把
4.List:有序,可重复,原因是它的实现类的数据结构是数组 和 链表
Set: 无序,不可重复,原因 它的实现类的数据结构是 哈希表和二叉树

5.Collection(单列数据)
	1.通用方法
		1、添加
			 add(Object obj)
			 addAll(Collection coll)

		2、清空,删除集合
			void clear()
			remove(Object obj) :通过元素的equals方法判断是否是要删除的那个元素。只会删除找到的第一个元素
			removeAll(Collection coll):取当前集合的差集

		3、查询(是否包含某个元素)
			boolean contains(Object obj):是通过元素的equals方法来判断是否是同一个对象
			boolean containsAll(Collection c):也是调用元素的equals方法来比较的。拿两个集合的元素挨个比较。

		4、获取有效元素的个数
			int size()

		5、是否是空集合 
			boolean isEmpty()

		
		6、取两个集合的交集
			retainAll(Collection c):把交集的结果存在当前集合中,不影响c 
		
		7、集合是否相等
			boolean equals(Object obj)

		8、转成对象数组
			Object[] toArray()

		9、获取集合对象的哈希值
			hashCode()

		10、遍历
			iterator():返回迭代器对象,用于集合遍历

	2.Iterator迭代器接口(只遍历 collection集合 )
		1.一个迭代器,会对应一个集合的状态,会在内存中虚拟化一个集合,对此集合进行remove结束后,实际集合会
		对应 迭代器集合,进行remove


		2.Collection接口继承了java.lang.Iterable接口,里面iterator()方法,返回iterator迭代器对象
		 由 其子类 实现的,返回的是Iterator的 实现类 private class Itr implements Iterator<E>
	
		3.集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前。

		3.iterator迭代器方法 hasNext ,next ,remove,

		4.在迭代的过程中,不能调用集合对象的remove方法删除对象,
		因为这时集合结构发生了改变,迭代器必须重新获取
		
		5.在迭代的过程中只能调用 迭代器的 remove方法
		如果还未调用next()或在上一次调用 next 方法之后已经调用了 remove 方法,
		再调用remove都会报IllegalStateException。

		6.foreach,遍历集合,底层是iterator
	
6.List
	1.多了一些 根据 索引来 操作数据的方法(因为底层是线性的)

	2.元素有序(不是大小顺序)、且可重复,集合中的每个元素都有其对应的顺序索引。

	3.List接口方法:List 集合里添加了一些根据索引来操作集合元素的方法。

		1.添加
			add(int index, Object ele):在index位置插入ele元素
			add方法是插入不是覆盖
			addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来

		2.查询
			get(int index):获取指定index位置的元素
			int indexOf(Object obj):返回obj在集合中首次出现的位置
			int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置

		3.删除
			Object remove(int index):移除指定index位置的元素,并返回此元素

		4.修改
			Object set(int index, Object ele):设置指定index位置的元素为else

		5.截取子集合
			List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合

	4.ArrayList 动态数组
			1.JDK1.7:ArrayList像饿汉式,直接创建一个初始容量为10的数组
			  JDK1.8:ArrayList像懒汉式,一开始创建一个长度为0的数组,当添加第一个元素时再创建一个始容量为10的数组

			2.Arrays.asList(…) 方法返回的 List 集合,既不是 ArrayList 实例,也不是
				Vector 实例。 Arrays.asList(…) 返回值是一个固定长度的 List 集合
		
		3.属性
			//默认容量的大小
			private static final int DEFAULT_CAPACITY = 10;

			//空数组常量
			private static final Object[] EMPTY_ELEMENTDATA = {};

			//默认的空数组常量
			private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}

			上面两个没啥区别,默认空数组常量 是调用无参构造的时候,赋给elementData的
			空数组常量是 调用有参ArrayList(0) 赋值给 elementData 
			

			//存放元素的数组,从这可以发现 ArrayList 的底层实现就是一个 Object数组
			transient Object[] elementData;

			//数组中包含的元素个数
			private int size;
			
			//数组的最大上限
			private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

		4.方法
			1.get(index) 
				rangeCheck(index) 先判断是否越界
				return elementData(index); 返回值

			2.public ArrayList() {
				this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
			} 无参构造 数组为 null

			3.public ArrayList(int initialCapacity) {
				if (initialCapacity > 0) {
					this.elementData = new Object[initialCapacity];
				} else if (initialCapacity == 0) {
					this.elementData = EMPTY_ELEMENTDATA;
				} else {
					throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
				}
			}

			构造方法,默认elementData是一个空数组,当我们指定初始容量大小就变成我们初始化大小


			4.public boolean add(E e) {
				ensureCapacityInternal(size + 1); // Increments modCount!!
				
				elementData[size++] = e;
				return true;
			} 这两部会导致线程安全问题



			private void ensureCapacityInternal(int minCapacity) {
				if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
					minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
				}
				ensureExplicitCapacity(minCapacity);
			}

			private void ensureExplicitCapacity(int minCapacity) {
				modCount++;
				if (minCapacity - elementData.length > 0)
				grow(minCapacity);
			}

			如果是空数组,那么会将capcity赋值10,然后调用grow(10)去扩容,扩容调用的是Arrays.copy(原数组,长度),扩容后的数组大小是10
			如果不是空数组,那么会调用 grow方法,判断是否要越界了,若是则扩容,扩容大小是1.5倍

			grow方法扩容 newCapacity = oldCapacity + (oldCapacity >> 1); 原长度+原长度左移一位,就是1.5倍 

		5.总结(Vetor 比 ArrayList 多一个 扩容量capacityIncrement;)
			1、ArrayList 创建时的大小为 0;当加入第一个元素时,进行第一次扩容时,默认容量大小为 10。
			2、ArrayList 每次扩容都以当前数组大小的 1.5 倍去扩容。
			3、Vector 创建时的默认大小为 10。
			4.Vector 每次扩容都以当前数组大小的 2 倍去扩容。当指定了 capacityIncrement 之 后,每次扩容仅在原先基础上增加 capacityIncrement 个单位空间。
			5、ArrayList 和 Vector 的 add、get、size 方法的复杂度都为 O(1),remove 方法的复杂度为 O(n)。
			6、ArrayList 是非线程安全的,Vector 是线程安全的。

	5.LinkedList:底层双向链表(add,remove特定元素用)

7.set
	1.Set接口是Collection的子接口,set接口没有提供额外的方法
	2.Set 集合不允许包含相同的元素,如果试把两个相同的元素加入同一个
	Set 集合中,则添加操作失败,因为 set接口的实现类 是 hash表 和 树
	
	*3.hashSet 
		1.底层是hash表
			hash表不是数组加链表,是为了 在 查询时不做比较,直接一步查出数据,
			那么就要给每个数据一个关键字,key对key做hash,然后存入数组,
			下次取数据时,直接计算元素hash,得到数组下标,就可以直接定位到具体元素,
			如果直接用数组,那么得一个一个比较,这里说的不是通过下标查元素

		2.散列函数得出数组下标 必须得根据数组的大小作为依据,使得计算出来的数组下标都在范围内

		2.hash冲突:
			元素在hash,散列函数计算后 得到的数组下标一样,就会导致一个桶无法存多个数据,那么
			就得解决例如加链表,但是这样的查询速率会降低,所以散列函数的设计非常重要,要尽量设计的
			分散一定,如果计算出来的hash地址越大,所谓的“冲突”就越少,查找起来效率也会提高。(减少冲突)

		2.HashSet 不是线程安全的

		3.HashSet 集合判断两个元素相等的标准:两个对象通过 hashCode() 方法比较相
		  等,并且两个对象的 equals() 方法返回值也相等

		*4.对应的类一定要重写equals()和hashCode(Objectobj)方法,以实现对象相等规则。 
		  即:“相等的对象必须具有相等的散列码”	,equals相同 hashcode必须相同
		  
		5.向HashSet中添加元素的过程:
			1.计算出hashcode值,通过散列函数,得到数组下标的位置

			2.这个散列函数会与底层数组的长度相计算得到在数组中的下标

			3.散列函数计算还尽可能保证能均匀存储元素,越是散列分布,该散列函数设计的越好

			4.哈希冲突:先比较hash值,再比较equals,(其实直接比较equals就能知道元素是否相同)
			因为hash值已经计算出来了,可以直接比较
				1.若hash值都不相同,那么这个对象跟链表里面的元素都不相同加在最后面即可
				2.若hash值相同,那么得比较equals(因为,equals相同,hashcode相同,所以hash相同,
				不一定equals相同),相同,则链表中有相同的元素,value覆盖即可
				若不同,则加在最后

	4.LinkedHashSet(可以看起来 让 hashset 有序)
		?1.LinkedHashSet 是 HashSet 的子类

		  2.LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,
		    但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入顺序保存的。

		  3.LinkedHashSet插入性能略低于 HashSet,但在迭代访问 Set 里的全部元素时有很好的性能。

		  4.LinkedHashSet 不允许集合元素重复

	5.TreeSet
		1.TreeSet 是 SortedSet 接口的实现类,TreeSet无序不可重复,但是可以排序
		2.TreeSet底层使用红黑树结构存储数据
		3.新增方法
			Comparator comparator()
			Object first()
			Object last()
			Object lower(Object e)
			Object higher(Object e)
			SortedSet subSet(fromElement, toElement) ?
			SortedSet headSet(toElement)
			SortedSet tailSet(fromElement)

		4.TreeSet 两种排序方法:自然排序和定制排序。默认情况下,TreeSet 采用自然排序

		5.TreeSet和TreeMap采用红黑树的存储结构

		6.有序,查询速度比List快

		7.

	
8.Map
	1.Map 中的 key 用Set来存放,不允许重复,即同一个 Map 对象所对应
	的类,须重写hashCode()和equals()方法

	2.添加、删除、修改操作: ?
		Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中
		void putAll(Map m):将m中的所有key-value对存放到当前map中 ? 
		Object remove(Object key):移除指定key的key-value对,并返回value
		void clear():清空当前map中的所有数据
		? 
	3.元素查询的操作:
		Object get(Object key):获取指定key对应的value
		boolean containsKey(Object key):是否包含指定的key
		boolean containsValue(Object value):是否包含指定的value
		int size():返回map中key-value对的个数
		boolean isEmpty():判断当前map是否为空
		boolean equals(Object obj):判断当前map和参数对象obj是否相等

	4.元视图操作的方法:
		Set keySet():返回所有key构成的Set集合 遍历集合
		Collection values():返回所有value构成的Collection集合
		Set entrySet():返回所有key-value对构成的Set集合 遍历集合 Set<Map.entry<K,V>>

	5.
		1.一个key-value构成一个entry

		2.所有的entry构成的集合是Set:无序的、不可重复的

		3.HashMap 判断两个 key 相等的标准是:两个 key 通过 equals() 方法返回 true,hashCode 值也相等。

		4.底层是数组加链表,不是hash表(hash表是一种数据结构,不是具体的实现),
		  hashmap是按 哈希表 的 一种实现

		5.hashmap怎么解决hash冲突的:
			1.如果产生hash冲突(add的时候,桶的位置已经有元素了)
				链表的方式解决,循环比较,
					1.若hash值都不相同,那么这个对象跟链表里面的元素都不相同加在最后面即可
					2.若hash值相同,那么得比较equals(因为,equals相同,hashcode相同,所以hash相同,
					不一定equals相同),相同,则链表中有相同的元素,value覆盖即可
					若不同,则加在最后

			2.没有出现hash冲突的元素,一定equals不同,存在不同桶中(hash值相同,数组下标一定相同)

		6.JDK 7及以前版本:HashMap是数组+链表结构(即为链地址法)
		  JDK 8版本发布以后:HashMap是数组+链表+红黑树实现
			解决的问题:数组长度太小没有扩容而链表太长导致的查询速度变慢
			终究是一个 数组大小和链表长度的比例问题

		7.HashMap的扩容
			1.为什么要进行扩容
				不是数组大小不够了,是数组的桶被占的越来越多,出现hash冲突的次数越来越多,
				导致查询速率越来越小

			2.扩容为什么是性能消耗最大的点:
				原数组中的数据必须重新计算其在新数组中的位置,并放进去,这就是resize

			3.扩容是越大越好吗
				不是,当达到64了就得考虑是否把链表中的元素转换成红黑数了,是8就转
				因为扩容越大,性能消耗越大

			4.hashmap什么时候进行扩容
	     (1.8前后都是这样)  1.当数组元素达到 默认数组长度(一般为16)*0.75 = 12时 ,扩容一倍
				然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作

				所以如果我们已经预知HashMap中元素的个数,那么预设元素的个数能够有效的提高HashMap的性能
				
			5.1.8之后什么时候链表树形化
				1.为什么要转红黑树
					1.红黑树查找的时间复杂度是 O(logn) ;而链表查找元素的时间复杂度为 O(n),远远大于红黑树的 O(logn),
					尤其是在节点越来越多的情况下,O(logn) 体现出的优势会更加明显;简而言之就是为了提升查询的效率。

				2.为什么不一开始就用红黑树
					1.TreeNode 需要占用的空间大约是普通 Node 的两倍
					由 TREEIFY_THRESHOLD 的值(默认值8)决定的

					2.当桶中节点数由于移除或者 resize 变少后,又会变回普通的链表的形式,
					以便节省空间,这个阈值是 UNTREEIFY_THRESHOLD(默认值6)。

				3.转换阈值 8 是怎么来的
					1.反正体现了空间和时间的平衡,一般情况下由于 hash 计算的结果离散好的话,
					那么红黑树这种形式是很少会被用到的,因为各个值都均匀分布,很少出现链表很长的情况。
					千万分之一的概率

				4.什么时候转红黑数
					1.当HashMap中的其中一个链的对象个数如果达到了8个,此时如果capacity没有达到64,
					那么HashMap会先扩容解决,如果已经达到了64,那么这个链会变成树,结点类型由Node变成TreeNode类型

					2.因为:数组没有达到64之前,可以通过扩容,达到效率提升,减少哈希冲突,而且扩容后,
					出现哈希冲突的几率变低,那么链表的长度就不会再增,但是扩容的大小>=64了,就不会扩容了
					因为再扩容,重新计算其在新数组中的位置,并放进去的消耗太大

		8.JDK1.8相较于之前的变化
			1.HashMap map = new HashMap();//默认情况下,先不创建长度为16的数组
			2.当首次调用map.put()时,再创建长度为16的数组
			3.数组为Node类型,在jdk7中称为Entry类型
			4.形成链表结构时,新添加的key-value对在链表的尾部(七上八下)
			5.当数组指定索引位置的链表长度>8时,且map中的数组的长度> 64时,此索引位置
			上的所有key-value对使用红黑树进行存储。

		9.HashMap源码中的重要常量
			1.HashMap的默认容量,16
			2.HashMap的最大支持容量,2^30
			3.HashMap的默认加载(扩充)因子 0.75
			4.扩充因子  可以自己设置
			5.扩容的临界值,=容量*填充因子
			6.TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值 8 ,转化为红黑树
			7.UNTREEIFY_THRESHOLD:Bucket中红黑树存储的Node小于该默认值 6 ,转化为链表
			8.MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量。64

	6.LinkedHashMap
		hashmap的子类,在HashMap存储结构的基础上,使用了一对双向链表来记录添加元素的顺序

	7.TreeMap 底层是红黑树
		1.自然排序:类内部必须实现,comparable排序接口里面的int compareto(T t) 方法
		2.定制排序:在构造方法时,得传入comparator接口的实现类
		3.TreeMap判断两个key相等的标准:两个key通过compareTo()方法或者compare()方法返回0

	8.Hashtable
		Hashtable是线程安全的
		与HashMap不同,Hashtable 不允许使用 null 作为 key 和 value
	9.Properties
		1.Hashtable 的子类,该对象用于处理属性文件

		2.Properties 里的 key 和 value 都是字符串类型

		3.存取数据时,建议使用setProperty(String key,String value)方法和
		getProperty(String key)方法

		4.有和流技术相结合的方法


		5.读取文件中的数据,并保存到集合
			load(Reader) 

	1.list-set的相互转换(list去重)
		利用set集合特性保持顺序一致去重;
			 1.List<String> listNew = new ArrayList<String>(new TreeSet<String>(list));
  
			 2.List<String> listNew2 = new ArrayList<String>(new LinkedHashSet<String>(list))

		



9. Collections工具类
	1.Collections 是一个操作 Set、List 和 Map 等集合的工具类
	2.中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,
	还提供了对集合对象设置不可变、对集合对象实现同步控制等方法

	3.排序操作:(均为static方法)
		?reverse(List):反转 List 中元素的顺序
		?shuffle(List):对 List 集合元素进行随机排序
		?sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
		?sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
		?swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换

	4.查找、替换
		?Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
		?Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素
		?Object min(Collection)
		?Object min(Collection,Comparator)
		?int frequency(Collection,Object):返回指定集合中指定元素的出现次数
		?void copy(List dest,List src):将src中的内容复制到dest中
		  boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换List 对象的所有旧值

	5.Collections 类中提供了多个 synchronizedXxx() 方法,该方法可使将指定集
	  合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题

集合的所有API
Collection

		1.添加
			 add(Object obj)
			 addAll(Collection coll)

		2、清空,删除集合
			void clear()
			remove(Object obj) :通过元素的equals方法判断是否是要删除的那个元素。只会删除找到的第一个元素
			removeAll(Collection coll):取当前集合的差集

		3、查询(是否包含某个元素)
			boolean contains(Object obj):是通过元素的equals方法来判断是否是同一个对象
			boolean containsAll(Collection c):也是调用元素的equals方法来比较的。拿两个集合的元素挨个比较。

		4、获取有效元素的个数
			int size()

		5、是否是空集合 
			boolean isEmpty()

		
		6、取两个集合的交集
			retainAll(Collection c):把交集的结果存在当前集合中,不影响c 
		
		7、集合是否相等
			boolean equals(Object obj)

		8、转成对象数组
			Object[] toArray()

		9、获取集合对象的哈希值
			hashCode()

		10、遍历
			iterator():返回迭代器对象,用于集合遍历

List(具备collection API)

		List接口方法:List 集合里添加了一些根据索引来操作集合元素的方法。

		1.添加
			add(int index, Object ele):在index位置插入ele元素
			add方法是插入不是覆盖
			addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来

		2.查询
			get(int index):获取指定index位置的元素
			int indexOf(Object obj):返回obj在集合中首次出现的位置
			int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置

		3.删除
			Object remove(int index):移除指定index位置的元素,并返回此元素

		4.修改
			Object set(int index, Object ele):设置指定index位置的元素为else

		5.截取子集合
			List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合

Set(和 collection 没有区别)
TreeSet

			Comparator comparator()
			Object first()
			Object last()
			Object lower(Object e)
			Object higher(Object e)
			SortedSet subSet(fromElement, toElement) ?
			SortedSet headSet(toElement)
			SortedSet tailSet(fromElement)

map

	2.添加、删除、修改操作: 
		Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中
		void putAll(Map m):将m中的所有key-value对存放到当前map中 ? 
		Object remove(Object key):移除指定key的key-value对,并返回value
		void clear():清空当前map中的所有数据
		
	3.元素查询的操作:
		Object get(Object key):获取指定key对应的value
		boolean containsKey(Object key):是否包含指定的key
		boolean containsValue(Object value):是否包含指定的value
		int size():返回map中key-value对的个数
		boolean isEmpty():判断当前map是否为空
		boolean equals(Object obj):判断当前map和参数对象obj是否相等

	4.元视图操作的方法:
		Set keySet():返回所有key构成的Set集合 遍历集合
		Collection values():返回所有value构成的Collection集合
		Set entrySet():返回所有key-value对构成的Set集合 遍历集合 Set<Map.entry<K,V>>

Collections工具类

	3.排序操作:(均为static方法)
		?reverse(List):反转 List 中元素的顺序
		?shuffle(List):对 List 集合元素进行随机排序
		?sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
		?sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
		?swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换

	4.查找、替换
		?Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
		?Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素
		?Object min(Collection)
		?Object min(Collection,Comparator)
		?int frequency(Collection,Object):返回指定集合中指定元素的出现次数
		?void copy(List dest,List src):将src中的内容复制到dest中
		  boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换List 对象的所有旧值
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值