史上最全的Java集合类解析

本文仅分析部分原理和集合类的特点,不分析源码,旨在对java的集合类有一个整体的认识,理解各个不同类的关联和区别,让大家在不同的环境下学会选择不同的类来处理。

Java中的集合类包含的内容很多而且很重要,很多数据的存储和处理(排序,去重,筛选等)都需要通过集合类来完成。


首先java中集合类主要有两大分支:

(1)Collection (2)Map

先看它们的类图:

(1)Collection

Collection

(2)Map
Map

可以看到它们之间的关系纷繁复杂,如果不系统的学习一下,还真是不知道有什么区别,该怎么选择。由于HashSet的内部实现原理是使用了HashMap,所以我们的学习路线为先学习Map集合类,然后再来学习Collection集合类。


(1)HashMap和Hashtable ( 注意table是小写的t,搞不懂为什么要这样,老是会写错。。。)

首先来看HashMap和HashTable,这两兄弟经常被放到一起来比较,那么它们有什么不一样呢?

a.HashMap不是线程安全的;HashTable是线程安全的,其线程安全是通过Sychronize实现。

b.由于上述原因,HashMap效率高于HashTable。

c.HashMap的键可以为null,HashTable不可以。

d.多线程环境下,通常也不是用HashTable,因为效率低。HashMap配合Collections工具类使用实现线程安全。同时还有ConcurrentHashMap可以选择,该类的线程安全是通过Lock的方式实现的,所以效率高于Hashtable。

好,比较了他们的不一样后,来讲讲它们的原理。

数组,链表,哈希表。各有优劣,顺便提一下,数组连续内存空间,查找速度快,增删慢;链表充分利用了内存,存储空间是不连续的,首尾存储上下一个节点的信息,所以寻址麻烦,查找速度慢,但是增删快;哈希表呢,综合了它们两个的有点,一个哈希表,由数组和链表组成。假设一条链表有1000个节点,现在查找最后一个节点,就得从第一个遍历到最后一个;如果用哈希表,将这条链表分为10组,用一个容量为10数组来存储这10组链表的头结点(a[0] = 0 , a[1] = 100 , a[2] = 200 …)。这样寻址就快了。

HashMap实现原理就是上述原理了,当然其具体实现还有很多其他的东西。Hashtable同理,只不过做了同步处理。

Hash碰撞,不同的key根据hash算法算出的值可能一样,如果一样就是所谓的碰撞。

优化措施:

(1) HashMap的扩容代价非常大,要生成一个新的桶数组,然后要把所有元素都重新Hash落桶一次,几乎等于重新执行了一次所有元素的put。所以如果我们对Map的大小有一个范围的话,可以在构造时给定大小,一般大小设置为:(int) ((float) expectedSize / 0.75F + 1.0F)。

(2) key的设计尽量简洁。


HashMap一些功能实现:

a.按值排序

HashMap按值排序通过Collections的sort方法,在实现排序之前,我们先看看HashMap的几种遍历方式:

	//Collection And Map
	public static void testCM(){
		//Collection
		Map<Integer , String> hs = new HashMap<Integer , String>();
		int i = 0;
		hs.put(199, "序号:"+201);
		while(i<50){
			hs.put(i, "序号:"+i);
			i++;
		}
		hs.put(-1, "序号:"+200);
		hs.put(200, "序号:"+200);
		
		//遍历方式一:for each遍历HashMap的entryset,注意这种方式在定义的时候就必须写成
		//Map<Integer , String> hs,不能写成Map hs;
		for(Entry<Integer , String> entry : hs.entrySet()){
			System.out.println("key:"+entry.getKey()+"  value:"+entry.getValue());
		}
		//遍历方式二:使用EntrySet的Iterator
		Iterator<Map.Entry<Integer , String>> iterator = hs.entrySet().iterator();
		while(iterator.hasNext()){
			Entry<Integer , String> entry =  iterator.next();
			System.out.println("key:"+entry.getKey()+"  value:"+entry.getValue());
		};
		//遍历方式三:for each直接使用HashMap的keyset
		for(Integer key : hs.keySet()){
			System.out.println("key:"+key+"  value:"+hs.get(key));
		};
		//遍历方式四:使用keyset的Iterator
		Iterator keyIterator = hs.keySet().iterator();
		while(keyIterator.hasNext()){
			Integer key = (Integer)keyIterator.next();
			System.out.println("key:"+key+"  value:"+hs.get(key));
		}	
	}
(1)使用keyset的两种方式都会遍历两次,所以效率没有使用EntrySet高。
(2)HashMap输出是无序的,这个无序不是说每次遍历的结果顺序不一样,而是说与插入顺序不一样。

接下来我们看按值排序,注释比较详细就不赘述过程了。

	//对HashMap排序
	public static void sortHashMap(Map<Integer , String> hashmap){
		
		System.out.println("排序后");
		
		//第一步,用HashMap构造一个LinkedList
		Set<Entry<Integer , String>> sets = hashmap.entrySet();
		LinkedList<Entry<Integer , String>> linkedList = new LinkedList<Entry<Integer , String>>(sets);
		
		//用Collections的sort方法排序
		Collections.sort(linkedList , new Comparator<Entry<Integer , String>>(){

			@Override
			public int compare(Entry<Integer , String> o1, Entry<Integer , String> o2) {
				// TODO Auto-generated method stub
				/*String object1 = (String) o1.getValue();
				String object2 = (String) o2.getValue();
				return object1.compareTo(object2);*/
				return o1.getValue().compareTo(o2.getValue());
			}
			
		});
		
		//第三步,将排序后的list赋值给LinkedHashMap
		Map<Integer , String> map = new LinkedHashMap();
		for(Entry<Integer , String> entry : linkedList){
			map.put(entry.getKey(), entry.getValue());
		}
		for(Entry<Integer , String> entry : map.entrySet()){
			System.out.println("key:"+entry.getKey()+"  value:"+entry.getValue());
		}
	}

b.按键排序

HashMap按键排序要比按值排序方法容易实现,而且方法很多,下面一一介绍。

第一种:还是熟悉的配方还是熟悉的味道,用Collections的sort方法,只是更改一下比较规则。

第二种:TreeMap是按键排序的,默认升序,所以可以通过TreeMap来实现。

	public static void sortHashMapByKey(Map hashmap){
		
		System.out.println("按键排序后");
		
		//第一步:先创建一个TreeMap实例,构造函数传入一个Comparator对象。
		TreeMap<Integer , String> treemap = new TreeMap<Integer , String>(new Comparator<Integer>(){

			@Override
			public int compare(Integer o1,Integer o2) {
				// TODO Auto-generated method stub
				return Integer.compare(o1, o2);
			}
			
		});
		//第二步:将要排序的HashMap添加到我们构造的TreeMap中。
		treemap.putAll(hashmap);
		for(Entry<Integer , String> entry : treemap.entrySet()){
			System.out.println("key:"+entry.getKey()+"  value:"+entry.getValue());
		}
	}

第三种:可以通过keyset取出所有的key,然后将key排序,再有序的将key-value键值对存到LinkedHashMap中,这个就不贴代码了,有兴趣的可以自己去尝试一下。

c.value去重

对于HashMap而言,它的key是不能重复的,但是它的value是可以重复的,有的时候我们要将重复的部分剔除掉。

方法一:将HashMap的key-value对调,然后赋值给一个新的HashMap,由于key的不可重复性,此时就将重复值去掉了。最后将新得到的HashMap的key-value再对调一次即可。

d.HashMap线程同步

第一种:

Map<Integer , String> hs = new HashMap<Integer , String>();
		hs = Collections.synchronizedMap(hs);

第二种:

ConcurrentHashMap<Integer , String> hs = new ConcurrentHashMap<Integer , String>();

(2)IdentifyHashMap

IdentityHashMap与HashMap基本相似,只是当两个key严格相等时,即key1==key2时,它才认为两个key是相等的 。IdentityHashMap也允许使用null,但不保证键值对之间的顺序。


(3)WeakHashMap

WeakHashMap与HashMap的用法基本相同,区别在于:后者的key保留对象的强引用,即只要HashMap对象不被销毁,其对象所有key所引用的对象不会被垃圾回收,HashMap也不会自动删除这些key所对应的键值对对象。但WeakHashMap的key所引用的对象没有被其他强引用变量所引用,则这些key所引用的对象可能被回收。WeakHashMap中的每个key对象保存了实际对象的弱引用,当回收了该key所对应的实际对象后,WeakHashMap会自动删除该key所对应的键值对。


接下来是Collection接口及其子类:

(4)ArrayList , LinkedList , Vector

(1)首先,说说它们的关系和区别。ArrayList和Vector本质都是用数组实现的,而LinkList是用双链表实现的;所以,Arraylist和Vector在查找效率上比较高,增删效率比较低;LinkedList则正好相反。ArrayList是线程不安全的,Vector是线程安全的,效率肯定没有ArrayList高了。实际中一般也不怎么用Vector,可以自己做线程同步,也可以用Collections配合ArrayList实现线程同步。

(2)Tips

前面多次提到扩容的代价很高,所以如果能确定容量的大致范围就可以在创建实例的时候指定,注意,这个仅限于ArrayList和Vector哟:

ArrayList arrayList = new ArrayList(100);
arrayList.ensureCapacity(200);
Vector vector = new Vector(100);
vector.ensureCapacity(200);

(3)其他功能实现

a.排序

List的排序的话就是使用Collections的sort方法,构造Comparator或者让List中的对象实现Comparaable都可以,这里就不贴代码了。

b.去重

第一种:用Iterator遍历,遍历出来的放到一个临时List中,放之前用contains判断一下。

第二种:利用set的不可重复性,只需三步走。

//第一步:用HashSet的特性去重
	HashSet tempSet = new HashSet(arrayList);
    //第二步:将arrayList清除
	tempSet.clear();
	//第三步:将去重后的重新赋给List
	arrayList.addAll(tempSet);

(5)Stack

Stack呢,是继承自Vector的,所以用法啊,线程安全什么的跟Vector都差不多,只是有几个地方需要注意:

第一:add()和push(),stack是将最后一个element作为栈顶的,所以这两个方法对stack而言是没什么区别的,但是,它们的返回值不一样,add()返回boolean,就是添加成功了没有;push()返回的是你添加的元素。为了可读性以及将它跟栈有一丢丢联系,推荐使用push。

第二:peek()和pop(),这两个方法都能得到栈顶元素,区别是peek()只是读取,对原栈没有什么影响;pop(),从字面上就能理解,出栈,所以原栈的栈顶元素就没了。


(6)HashSet和TreeSet

Set集合类的特点就是可以去重,它们的内部实现都是基于Map的,用的是Map的key,所以知道为什么可以去重复了吧。
既然要去重,那么久需要比较,既然要比较,那么久需要了解怎么比较的,不然它将1等于2了,你怎么办?

比较是基于hascode()方法和equals()方法的,所以必要情况下需要重新这两个方法。


好了,到了总结的时候了,其实你会发现集合类虽然看起来多,但是都是很有规律的。HashSet,TreeSet一个无序,一个有序;HashMap,LinkedHasmMap,一个无序,一个有序;Vector和HashTable,Stack是线程安全的,但是效率低;线程不安全的类都可以配合Collections得到线程安全的类。
  • 70
    点赞
  • 244
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
### 回答1: "Java自学教程(史上最全)文库"是一个提供丰富资料的自学教程文库,旨在帮助那些希望自学Java编程的人们。这个文库包含了大量的Java知识和编程技巧,从基础知识到高级应用涵盖了各个方面。 首先,Java自学教程提供了一个系统而完整的学习路径。它以浅显易懂的方式介绍了Java的核心概念,如变量、数据类型、控制结构和面向对象编程等。然后,它逐渐深入到更复杂的主题,例如异常处理、线程编程和网络编程等。这样的学习路径使初学者能够逐步掌握Java的知识,并逐渐提高他们的编程技能。 其次,Java自学教程提供了大量的实例和实践项目。这些实例展示了Java在实际应用中的使用,包括图形用户界面、数据库连接和Web开发等领域。通过实际的实例,读者可以更好地理解和掌握Java的应用,并能够将所学知识应用到自己的项目中。 此外,Java自学教程还提供了丰富的学习资源。除了文字教程外,它还包括了视频教程、在线编程环境和交流社区等。这样的学习资源不仅能够满足不同类型学习者的需求,还能够帮助读者更好地与其他学习者互动交流,加深对Java编程的理解和掌握。 总之,Java自学教程(史上最全)文库是一个宝贵的学习资源,适合想要自学Java编程的初学者或者进一步提升Java技能的开发者。它提供了全面的教程,丰富的实例和实践项目,以及多样化的学习资源,帮助读者深入学习和应用Java编程。 ### 回答2: Java自学教程是一个为那些希望自学Java编程的人提供的宝贵资源。这个文库被称为史上最全,是因为它提供了从基础知识到高级概念的全面指导,并涵盖了Java编程的各个方面。 首先,该教程首先介绍了Java的背景和发展历程,帮助读者了解Java的起源和其在软件开发中的重要性。接下来,它详细解释了Java的基本语法、数据类型、运算符和控制流程,帮助读者建立起对Java编程的基础理解。 随后,教程深入讲解了Java面向对象编程的核心概念,如类、对象、封装、继承和多态。此外,它还介绍了Java的异常处理机制和文件输入/输出操作,提供了有关如何处理潜在错误和管理文件的重要指导。这些内容有助于读者构建健壮的Java应用程序。 该教程还包括关于Java集合框架、多线程编程和网络编程的详细指南。这些主题涉及到Java编程中的高级概念和技术,对于希望进一步扩展他们的Java知识的读者来说,是必不可少的。 此外,这个文库提供了大量的实例和练习,帮助读者巩固他们所学的知识,并帮助他们实践和掌握Java编程技巧。教程还介绍了一些常见的Java开发工具和技术,如Eclipse和Maven,以帮助读者优化他们的Java开发环境。 总之,Java自学教程(史上最全)文库是一个为那些希望自学Java编程的人提供全面指导的宝贵资源。它涵盖了Java编程的各个方面,从基础知识到高级概念,为读者提供了一个全面而系统的学习路径。无论是初学者还是有经验的开发者,都可以从中受益,并在Java编程领域取得成功。 ### 回答3: Java自学教程(史上最全)文库是一个综合性的教程资源库,提供了全面且详细的Java学习资料。以下是对其的一些介绍: Java自学教程(史上最全)文库是由一群经验丰富的Java开发者所创建的,他们将自己多年的经验和知识总结、整理,并以此为基础编写了一系列适合自学的教程。 这个文库首先从Java的基础知识开始讲解,包括Java语言的特点、基本语法、数据类型、流程控制等内容。然后逐步深入,介绍了Java面向对象的特性、类和对象、继承、多态等,并通过大量的实例演示和练习来帮助读者更好地理解和运用。 此外,文库还涵盖了Java核心技术,包括异常处理、多线程、网络编程、IO流、集合框架等,它们是Java开发中必备的技能。 除了核心技术,文库还介绍了一些常用的Java开发工具和框架,如Eclipse、Maven、Spring等,以帮助读者更好地进行Java项目的开发与管理。 最重要的是,文库中的内容都经过仔细筛选和验证,确保了教程的准确性和实用性,因此它可以作为一个可靠的学习资料供读者参考。 总之,Java自学教程(史上最全)文库是一个内容丰富、体系完整的学习资源库,通过它你可以系统地学习并掌握Java开发所需的知识和技能。无论你是初学者还是有一定经验的开发者,都可以从中获得帮助和提升。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值