集合框架不用多说,大家都是经常用到的,也许我们只是简单的停留在使用方面,也许我们已经深入分析,并理解其性能差异的原因。
基本的操作我就不说了,查API都会用,下面我来介绍一下我所理解的集合框架。
java集合框架主要是由2个接口派生而出的:Collection和Map接口。我们经常用到的List和Set接口就是继承了Collection接口并扩充的。Iterator接口也是java集合框架的成员,不过只是用来遍历。
1.List接口
List代表一个元素有序,可重复的集合,集合中每个元素都有对应的顺序索引。在List集合的实现类中,主要有3个实现类:ArrayList,Vector和LinkedList。
先看看ArrayList和Vector的区别:
其实ArrayList和Vector本质上没太大的区别,都是实现了List接口,而且底层都是基于java数组来存储元素的。但是他们最显著的区别就是:ArrayList是线程不安全的,Vector是线程安全的,由于Vector不管在什么情况下都要保证线程安全,所以其性能要低于ArrayList。是不是在多线程程序中就要用Vector呢?其实则不然,就算要保证线程安全也不推荐使用Vector,后面介绍Collection工具类保证线程安全。
再看看ArrayList和LinkedList的区别:
ArrayList是基于数组的顺序存储的线性表,LinkedList是基于链的链式存储的线性表。因为数组以一块连续内存来保存数组的元素,所以数组在随即访问时性能最好,也就是查询方面性能很好,但是如果添加和删除元素的时候,ArrayList都要对底层数组进行整体的移动,这会使性能很差。而链表在添加和删除元素的时候,只要添加和删除相应的节点就可以了,其他的根本不用管,所以添加和删除的操作很快,但是遍历方面,LinkedList必须一个元素一个元素的搜索,所以在查找方面性能不是很好。
大部分情况下,ArrayList的性能总是优于LinkedList,因此绝大多数情况下都应当考虑用ArrayList。但是在经常要执行添加和删除的操作应该优先选择LinkedList。
2.Set接口
Set代表一个无序,不可重复的集合。当添加元素的时候,根据equals()方法,如果返回true则添加失败,否则添加成功。在Set集合的实现类中,主要有2个实现类:HashSet和TreeSet。HashSet还有个子类LinkedHashSet。
①.HashSet
是按hash算法来存储集合中的元素,因此具有很好的存取和查找性能。而且HashSet中允许null元素。
当向Hash集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到对象的hashCode值,然后根据hashCode值决定该对象在HashSet中的存储位置。这里值得说一下,HashSet和Set有点出入,HashSet判断2个元素是否一样是通过equals()和hashCode()2个方法决定的,当equals()返回true,而且2个元素的hashCode值相等,HashSet才认为这2个元素一样。
如果需要把某个类对象加入到HashSet中,重写equals()方法和hashCode()方法时,要尽量保证两个对象通过equals()方法比较返回true时,他们的hashCode()方法返回值也相等。如果两个对象通过equals()方法返回true,但是hashCode()方法返回不同hashCode值时,HashSet会将两个对象存在Hash表的不同位置。如果两个对象通过hashCode()方法返回值相同,但是equals()方法返回false,HashSet就会试图把这两个对象保存在同一位置,但是又不行,所以就会在这个位置使用链式结构来存储,虽然能添加成功,但是HashSet是根据元素hashCode值来快速定位的,同意位置有几个元素的话,性能就会降低。
代码1:
public class HashSetTest {
//分别向集合set中添加2个A对象,2个B对象和2个C对象
public static void main(String [] args){
HashSet set = new HashSet();
A a1 = new A();
A a2 = new A();
System.out.println("a1的hashCode值:"+a1.hashCode());
System.out.println("a2的hashCode值:"+a2.hashCode());
B b1 = new B();
B b2 = new B();
//b1和b2通过equals()方法的返回值
System.out.println(b1.equals(b2));
set.add(a1);
set.add(a2);
set.add(b1);
set.add(b2);
set.add(new C());
set.add(new C());
System.out.println(set);
}
}
class A {
//类A只重写equals()方法,而且只返回true
public boolean equals(Object obj){
return true;
}
}
class B {
//类B只重写hashCode()方法,而且只返回1
public int hashCode(){
return 1;
}
}
class C {
//类C重写equals()方法返回true,重写hashCode()方法返回2
public boolean equals(Object obj){
return true;
}
public int hashCode(){
return 2;
}
}
结果:
a1的hashCode值:29094346
a2的hashCode值:33492446
false
[Collection.B@1, Collection.B@1, Collection.C@2, Collection.A@1ff0dde, Collection.A@1bbf1ca]
显然A的hashCode值不一样,所以HashSet中添加了2个A对象,B的equals()方法返回false,所以添加了2个B对象,但是C的equals()方法返回true,hashCode值也一样,总为2,所以只添加了一个C对象。
②.LinkedHashSet
是HashSet的子类,也是根据元素的hashCode值来决定元素的存储位置,但它也用链表维护元素的次序,使得LinkedHashSet遍历起来会按添加顺序来访问集合里的元素。
LinkedHashSet因为需要维护元素的插入顺序,所以性能相对于HashSet要略低,但是在迭代访问set里的全部元素时有很好的性能,以为它以链表维护内部顺序。
代码2:
public class LinkedHashSetTest {
public static void main(String [] args){
LinkedHashSet set = new LinkedHashSet();
set.add("面向对象");
set.add("数据结构");
set.add("操作系统");
set.add("数据库");
set.add("计算机组成原理");
System.out.println(set);
}
}
结果:
[面向对象, 数据结构, 操作系统, 数据库, 计算机组成原理]
可见是按添加顺序进行输出的。
③.TreeSet
可以确保集合处于排序状态。TreeSet是采用红黑树的数据结构来存储集合元素的(没学过红黑树的同学不要紧,我也只是在数据结构书上看见过这个词,后面花点时间学学就好了),它支持自然排序和定制排序方法。
自然排序,就是TreeSet根据元素的compareTo(Object obj)方法来比较元素之间的大小关系,然后按升序排列。因为在实现compareTo(Object obj)方法时,都要将被比较对象强制转型成相同类型,所以TreeSet中只能添加同一类型的元素,否则就会报ClassCastException类转型异常。
对于TreeSet而言,判断两个元素是否一样,是通过调用元素compareTo(Object obj)方法,如果返回0,则认为2个对象一样,否则不一样。因此在添加自定义对象的时候,要尽量保证compareTo(Object obj)方法和equals()方法有一直的结果,否则会引发Set规则的冲突。
定制排序,就是自定义顺序进行排序,这里需要用到Comparator接口,接口中有个compare()方法,通过比较来确定排列顺序。如果需要实现定制排序,在创建集合对象时就要提供一个Comparator对象与该TreeSet集合关联,由该Comparator对象负责集合元素的排列逻辑。
代码3:
public class TreeSetTest {
public static void main(String [] args){
TreeSet set = new TreeSet();
//向set中添加5个Integer对象
set.add(5);
set.add(-3);
set.add(10);
set.add(0);
set.add(-9);
//打印集合
System.out.println(set);
}
}
结果:
[-9, -3, 0, 5, 10]
可以看出只要添加进去就已经排好序了。
如果向HashSet或者TreeSet中添加可变对象,后面程序又修改了该对象,就有可能导致该对象和集合中其他对象相等,从而导致HashSet和TreeSet无法准确访问该对象了。因此为了让程序更加健壮,应该HashSet和TreeSet集合中只放入不可变对象。
HashSet性能总是比TreeSet好,特别是添加和查询元素方面,因为TreeSet需要红黑树算法来维护集合元素中的顺序,只有当需要一个排序的Set时才使用TreeSet。而在遍历方面,因为有链表,所以LinkedHashSet要快于HashSet。
当然HashSet,LinkedHashSet和TreeSet都不是线程安全的,后面介绍Collection工具类保证线程安全。
3.Map接口
Map用来保存具有映射关系的数据,因此Map集合里保存了两组值,一组是key,一组是value,key是不允许重复的,即两个元素通过equals()方法来判断,value是允许重复的。key和value是一对一关系,通过key总是能找到唯一的value。在Map中,主要有3个实现类:HashMap,Hashtable,TreeMap。HashMap还有个子类LinkedHashMap。有没有觉得和Set集合惊人的相似。没错,java是先实现了Map,然后通过包装一个所有value都为null的Map就实现了Set集合。
其实Map就是对key进行的操作,而key的集合就是Set,我们都知道Map中有个方法keySet()得到key的Set集合,下面就对比Set介绍一下Map,具体的方法就不介绍了,查下API都会。
①.HashMap和Hashtable
的关系就像ArrayList和Vector的关系。Hashtable是线程安全的,而HashMap不是,所以HashMap的性能要优于Hashtable,Hashtable不允许出现为null的key,而HashMap允许出现一对。
HashMap和Hashtable对key的要求与HashSet对集合元素的要求完全一样,key是通过equals()和hashCode()方法来判断一致性,这些就不再做解释了。
②.LinkedHashMap
是HashMap的子类,和LinkedHashSet一样,也是使用了链表来维护key-value对的次序,这里也不多做解释。
③.TreeMap
也是和TreeSet一样,也是采用的红黑树的数据结构来存储key-value对,而且对key进行排序,这个和TreeSet一样,不多解释。
HashMap性能比Hashtable好,因为Hashtable需要线程同步。TreeMap通常比HashMap和Hashtable要慢,特别是在插入,删除key-value对的情况下,因为TreeMap采用红黑树来管理key-value对。LinkedHashMap要比HashMap慢,因为LinkedHashMap要维护链表来保持Map中的key-value的添加顺序。这些性能比较起来发现和Set集合的是一样的。
Iterator接口
Iterator接口也是java集合框架的成员,只不过Collection和Map用于装对象,而Iterator(迭代器)是用来遍历Collection集合中的元素的。List集合中的元素都有索引,可以根据索引来遍历,当然也能用Iterator来遍历,不过优先选择get(int index)方法遍历,因为用索引可以随机访问而Iterator是一个一个来遍历的,很慢。但是Set就不一样了,Set集合中的元素木有索引,只能用Iterator来遍历。
Iterator仅仅用来遍历,Iterator迭代变量不是集合元素本身,而是集合元素的赋值,因此修改迭代变量是不可能改变集合元素本身,是不是想到了值传递啊。在用Iterator遍历的时候,Collection集合中的元素不能被改变,否则会抛出ConcurrentModificationException异常。
当然也可以使用foreach循环遍历集合元素,这种情况和Iterator迭代遍历差不多。
代码4:
public class ForeachIteratorTest {
public static void main(String [] args){
//创建一个集合
Collection books = new HashSet();
books.add("数学");
books.add("语文");
books.add("英语");
//这是迭代器遍历
// Iterator it = books.iterator();
// while(it.hasNext()){
// String book = (String) it.next();
// if(book.equals("语文")){
// book = "计算机";
// }
// }
//这是foreach循环遍历
for(Object obj:books){
String book = (String) obj;
if(book.equals("语文")){
book = "计算机";
}
}
System.out.println(books);
}
}
结果:
[语文, 英语, 数学]
用Iterator和foreach遍历都是一样的结果。
操作集合的工具:Collections
Collections提供了很多操作集合的方法,其中有排序操作,查找替换操作和同步控制操作等操作。排序和查找替换我就不多介绍了。前面提到了ArrayList,LinkedList,HashSet,TreeSet,HashMap,TreeSet等等都不是线程安全,而Collections提供了多个synchronizedXxx()方法,将指定集合包装成线程同步的集合,解决多线程并发访问集合使线程安全的问题。
Collection c = Collections.synchronizedCollection(new ArrayList());
List list = Collections.synchronizedList(new ArrayList());
Set set = Collections.synchronizedSet(new HashSet());
Map map = Collections.synchronizedMap(new HashMap());
这是4种方法,这样可以直接获取线程安全的List,Set和Map了。
没事多看看源代码吧!
分享到:
相关推荐
这个"深入理解Java集合框架.zip"文件很可能是包含了一系列关于Java集合框架的详细资料,比如源码分析、设计模式以及最佳实践。下面将详细探讨Java集合框架的关键知识点。 1. **集合接口**:Java集合框架的核心接口...
适合人群:适用于初学者及有一定经验的Java开发者,尤其是那些需要深入理解Java集合框架的内部机制和优化技巧的人。 使用场景及目标:①学习Java集合框架的基础知识和使用方法;②深入了解集合类的底层实现和优化...
Java集合框架是Java编程语言中的核心部分,它提供了一组高效、灵活的数据结构,使得开发者可以方便地存储和管理各种类型的...无论是在面试中还是实际开发中,深入理解Java集合框架的知识都能帮助开发者更好地解决问题。
通过学习和练习这些内容,你可以深入理解Java集合框架的核心概念和泛型的应用,从而在编程实践中更加灵活和高效地处理数据。同时,不断的学习和实践是提升技能的关键,希望你能在Java世界中不断进步。
5. **源码分析**:深入理解Java集合框架的源码,有助于优化代码性能。例如,了解HashMap的扩容机制、LinkedList的节点操作、ArrayList的动态数组管理等,能够帮助开发者在设计和实现自己的数据结构时避免常见陷阱。 ...
在深入理解Java集合框架时,我们需要掌握以下几个关键知识点: 1. **接口**: - `Collection`:这是所有集合类的顶级接口,包括List、Set和Queue等子接口。 - `List`:有序的集合,允许重复元素,并保持插入顺序...
本文将深入解析Java集合框架的各个方面,包括Collection、List、Set和Map,以及它们的相关实现和使用原理。 **1. 集合框架概述** 集合框架是一个统一的数据结构和算法的集合,它提供了对数据进行高效处理的工具。...
内容概要:本文详细介绍了Java集合框架的重要性和在Android开发中的应用。首先,阐述了集合框架的基本概念,包括接口(Collection、Set、List、Map)和其实现类(ArrayList、LinkedList、HashSet、TreeSet、HashMap...
源码阅读对于深入理解Java集合框架至关重要。通过分析`ArrayList`、`HashMap`等类的实现,我们可以了解它们如何利用数据结构优化性能,以及如何处理并发问题。例如,`ArrayList`的扩容机制,`HashMap`的哈希冲突解决...
这份"Java集合思维导图.xmind.zip"压缩包文件,显然旨在帮助学习者深入理解Java集合框架的核心原理以及不同版本间的差异。以下是关于Java集合类,特别是HashMap、CurrentHashMap、ArrayList和LinkedList的详细知识点...
Java集合框架是Java编程语言中一个至关重要的组件,它提供了一组高级数据结构,用于存储、管理和操作对象。集合框架的定义包括多个接口、类和实现,这些元素...因此,深入理解Java集合框架是每个Java开发者必备的技能。
学习这部分内容对于Java开发者至关重要,因为理解并熟练掌握Java集合框架和泛型,能有效地提升代码质量和效率,减少运行时错误。此外,了解枚举类型有助于编写更安全、更整洁的代码。通过深入研究和实践,开发者能够...
在Java编程领域,电话本...分析和理解这些代码,可以帮助我们深入理解Java集合框架的应用以及如何构建一个实际的电话本应用程序。同时,这也是一个很好的实践机会,可以提升我们对面向对象设计、数据结构和算法的理解。
在实际开发中,理解和熟练运用Java集合框架和泛型能够大大提高代码的可维护性和安全性,减少类型转换的麻烦,并使得代码更易于理解和复用。通过以上讲解,相信你已经对这两个主题有了更深入的理解。通过练习和应用,...
9. **源码分析**:深入理解Java集合框架的源码可以帮助开发者优化代码,了解其内部实现,比如哈希算法、链表结构或红黑树的工作原理。 综上所述,Java集合框架是Java编程中的基础,它提供的接口和类覆盖了日常开发...
总的来说,Java集合框架是一个强大的工具箱,为开发者提供了丰富的数据结构和算法实现,是理解和掌握Java编程的关键。通过深入学习和实践,我们可以更加熟练地运用这些工具,解决各种复杂的问题。
首先,我们要理解Java集合框架的基本层次结构。它主要包括接口(如List、Set、Map)和实现这些接口的类(如ArrayList、HashSet、HashMap等)。这些接口定义了通用的操作方法,而类则提供了具体的实现。 1. 散列集合...
这个“一个讲解很清晰的Java集合框架PPT”显然是一个对外公开的教育资源,旨在帮助学习者深入理解Java集合的概念、结构以及实际应用。 在Java中,集合框架主要包括四大接口:List、Set、Queue和Map。每个接口都有...