Java的集合容器(上)
2015-10-25 16:59
477 查看
9月初的时候去了第一次面试,那时候Android基础才学到一半,Fragment刚看完没多久,service什么的看都没看过。
越是自知水平不够越是紧张,结果连Java集合这种简单的问题都说错了。
结果可想而知… 惨不忍睹。
所以关于Java,我想从这开始总结。
在分别总结Java集合容器前,我认为理清Java容器类与数组的区别是很有必要的:
数组:是Java提供的,能随机存储和访问对象引用序列的诸多方法中的,最高效的一种。数组就是一个简单的线性序列,它可以快速的访问其中的元素。
但是速度是有代价的:当你创建了一个数组之后,它的容量就固定了,而且在其生命周期里不能改变。
数组可以存储对象元素,也可以存储基本数据类型元素。数组的长度是固定的。
集合:当我们需要操作多个对象时,我们可以使用对象数组。但是由于数组在定义的时候要明确元素个数或者明确元素值,这些东西在很多时候我们是无法确定的。所以,Java就提供了集合来存储对象元素。
集合只能存储对象元素,长度可以变化。由于泛型和自动包装机制的出现,容器已经可以与数组几乎一样方便地用于基本类型中了。
如果我们要解决一般化的问题,为了避免受到过多的限制,在大部分情况下我们选择容器。
数组和容器都可以在一定程度上防止被滥用,如果越界,会得到运行时异常。
Set(集):集(Set)是最简单的一种集合,它的对象不按特定方式排序,只是简单的把对象加入集合中,就像往口袋里放东西。
对集中成员的访问和操作是通过集中对象的引用进行的,所以集中不能有重复对象。
集也有多种变体,可以实现排序等功能,如TreeSet,它把对象添加到集中的操作将变为按照某种比较规则将其插入到有序的对象序列中。
List(列表):列表(List)的主要特征是它的对象以线性方式存储,没有特定顺序,只有一个开头和一个结尾,当然,它与根本没有顺序的集是不同的。
列表在数据结构中分别表现为:数组和向量、链表、堆栈、队列。
Map(映射):映射与集或列表有明显区别,映射中每一项都是成对的。映射中存储的每个对象(value)都有一个相关的关键字(key),key决定了value在映射中的存储位置,检索对象时必须提供相应的关键字,就像在字典中查单词一样。关键字应该是唯一的。
关键字本身并不能决定对象的存储位置,它需要对过一种散列(hashing)技术来处理,产生一个被称作散列码(hash code)的整数值,散列码通常用作一个偏置量,该偏置量是相对于分配给映射的内存区域起始位置的,由此确定关键字/对象对的存储位置。
理想情况下,散列处理应该产生给定范围内均匀分布的值,而且每个关键字应得到不同的散列码。
所有实现Collection接口的类都必须提供两个标准的构造函数:无参数的构造函数用于创建一个空的Collection,有一个Collection参数的构造函数用于创建一个新的Collection,这 个新的Collection与传入的Collection有相同的元素。后一个构造函数允许用户复制一个Collection。
若想遍历Collection中的元素,不管它的实际类型是什么,它都支持iterator()方法,该方法返回一个迭代子,使用该迭代子即可逐一访问Collection中每一个元素。
关于Iterator
方法:
hasNext():判断集合中元素是否遍历完毕,如果没有,就返回true
next() :返回下一个元素
remove():从集合中删除上一个有next()方法返回的元素。
理解:其实,集合的对象通过iterator方法,返回的是迭代器的子类对象。每种集合获取元素的方式是不同的,
所以,他们会自己去实现如果获取元素。但是,他们都有共性内容:就是先判断是否有数据,然后获取数据。所以,就抽取出了一个接口Iterator。
注意点:
迭代器在Collcection接口中是通用的,它替代了Vector类中的Enumeration(枚举)。
迭代器的next方法是自动向下取元素,要避免出现NoSuchElementException。
迭代器的next方法返回值类型是Object,所以要记得类型转换。
用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。与Set不同,List允许有相同的元素。
除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个 ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素, 还能向前或向后遍历。
实现List接口的常用类有LinkedList,ArrayList,Vector,Stack。
注意:LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。
一种解决方法是在创建List时构造一个同步的List:
size,isEmpty,get,set方法运行时间为常数,但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。
每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。
和LinkedList一样,ArrayList也是非同步的(unsynchronized)。
底层数据结构是数组,而且是线程不安全的。
增删慢,查询快
LinkedList
底层数据结构是链表,而且是线程不安全的。
增删快,查询慢
Vector
底层数据结构是数组,而且是线程安全的。
增删慢,查询快,但是由于线程安全,所以,增删查询都慢。一般不选择。
它是一种不包含重复的元素的Collection,任意两个元素e1和e2都有e1.equals(e2)=false,最多有一个null元素。
Set的构造函数有一个约束条件,传入的Collection参数不能包含重复的元素。同时,Set里的元素无序,即存储顺序和取出顺序不一致。
HashSet类
底层数据结构是哈希表,线程不安全。
一般使用HashSet的时候,要重写hashCode和equals方法。
LinkedHashSet
底层数据结构是链表和哈希表。
链表用于保证元素有序,哈希表用于保证元素唯一。
如何保证元素唯一性:
首先根据hashCode值判断。
若hashCode值不同,那么,这就是不同的元素。直接存储。
若hashCode值相同,那么,会继续根据equals方法进行判断,根据自己的需求来决定元素是否相同。如果相同,就不存储。否则,存储。
TreeSet类
是Set的一种变体——可以实现排序等功能的集合。
底层数据结构是二叉树,线程不安全。
保证元素排序的方式:
让元素本身具备比较性
实现Compareable接口中的compareTo方法。
让集合具备比较性
实现Comparator接口中的compare方法。
如果元素唯一并且要排序用TreeSet,否则,用HashSet。
如果元素可重复,且考虑线程问题用Vector。否则用ArrayList或者LinkedList。
如果要求增删快,考虑使用LinkedList。
如果要求查询快,考虑使用ArrayList。
当什么要求都不清楚的时候,就使用ArrayList。
注:该篇博文部分知识点参考自以下博文
细说Java之util类
黑马程序员–javaSE–java集合容器总结
越是自知水平不够越是紧张,结果连Java集合这种简单的问题都说错了。
结果可想而知… 惨不忍睹。
所以关于Java,我想从这开始总结。
在分别总结Java集合容器前,我认为理清Java容器类与数组的区别是很有必要的:
数组:是Java提供的,能随机存储和访问对象引用序列的诸多方法中的,最高效的一种。数组就是一个简单的线性序列,它可以快速的访问其中的元素。
但是速度是有代价的:当你创建了一个数组之后,它的容量就固定了,而且在其生命周期里不能改变。
数组可以存储对象元素,也可以存储基本数据类型元素。数组的长度是固定的。
集合:当我们需要操作多个对象时,我们可以使用对象数组。但是由于数组在定义的时候要明确元素个数或者明确元素值,这些东西在很多时候我们是无法确定的。所以,Java就提供了集合来存储对象元素。
集合只能存储对象元素,长度可以变化。由于泛型和自动包装机制的出现,容器已经可以与数组几乎一样方便地用于基本类型中了。
如果我们要解决一般化的问题,为了避免受到过多的限制,在大部分情况下我们选择容器。
数组和容器都可以在一定程度上防止被滥用,如果越界,会得到运行时异常。
集合的类型
集合主要有三种类型:Set(集),List(列表),Map(映射)。Set(集):集(Set)是最简单的一种集合,它的对象不按特定方式排序,只是简单的把对象加入集合中,就像往口袋里放东西。
对集中成员的访问和操作是通过集中对象的引用进行的,所以集中不能有重复对象。
集也有多种变体,可以实现排序等功能,如TreeSet,它把对象添加到集中的操作将变为按照某种比较规则将其插入到有序的对象序列中。
List(列表):列表(List)的主要特征是它的对象以线性方式存储,没有特定顺序,只有一个开头和一个结尾,当然,它与根本没有顺序的集是不同的。
列表在数据结构中分别表现为:数组和向量、链表、堆栈、队列。
Map(映射):映射与集或列表有明显区别,映射中每一项都是成对的。映射中存储的每个对象(value)都有一个相关的关键字(key),key决定了value在映射中的存储位置,检索对象时必须提供相应的关键字,就像在字典中查单词一样。关键字应该是唯一的。
关键字本身并不能决定对象的存储位置,它需要对过一种散列(hashing)技术来处理,产生一个被称作散列码(hash code)的整数值,散列码通常用作一个偏置量,该偏置量是相对于分配给映射的内存区域起始位置的,由此确定关键字/对象对的存储位置。
理想情况下,散列处理应该产生给定范围内均匀分布的值,而且每个关键字应得到不同的散列码。
集合框架详解
Java集合框架的基本接口[I]/类[C]层次结构:java.util.Collection [I] +--java.util.List [I] +--java.util.LinkedList [C] +--java.util.ArrayList [C] +--java.util.Vector [C] +--java.util.Stack [C] +--java.util.Set [I] +--java.util.HashSet [C] +--java.util.SortedSet [I] +--java.util.TreeSet [C] java.util.Map [I] +--java.util.SortedMap [I] +--java.util.TreeMap [C] +--java.util.Hashtable [C] +--java.util.HashMap [C] +--java.util.LinkedHashMap [C] +--java.util.WeakHashMap [C]
Collection接口
Collection是最基本的集合接口,一个Collection代表一组Object的集合,这些Object即为Collection的元素(Elements)。Collection声明了适用于Java集合的通用方法,由上面的继承关系可以知道Set和List都继承了Collection,Map则没有。一些Collection允许相同的元素而另一些不行。一些能排序而另一些不行(下面会具体介绍)。Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的“子接口”如List和Set。所有实现Collection接口的类都必须提供两个标准的构造函数:无参数的构造函数用于创建一个空的Collection,有一个Collection参数的构造函数用于创建一个新的Collection,这 个新的Collection与传入的Collection有相同的元素。后一个构造函数允许用户复制一个Collection。
若想遍历Collection中的元素,不管它的实际类型是什么,它都支持iterator()方法,该方法返回一个迭代子,使用该迭代子即可逐一访问Collection中每一个元素。
Iterator it=collection.iterator(); //获取返回的迭代子 while(iterator.hasNext()){ Object obj=it.next(); //得到下个元素 }
关于Iterator
方法:
hasNext():判断集合中元素是否遍历完毕,如果没有,就返回true
next() :返回下一个元素
remove():从集合中删除上一个有next()方法返回的元素。
理解:其实,集合的对象通过iterator方法,返回的是迭代器的子类对象。每种集合获取元素的方式是不同的,
所以,他们会自己去实现如果获取元素。但是,他们都有共性内容:就是先判断是否有数据,然后获取数据。所以,就抽取出了一个接口Iterator。
注意点:
迭代器在Collcection接口中是通用的,它替代了Vector类中的Enumeration(枚举)。
迭代器的next方法是自动向下取元素,要避免出现NoSuchElementException。
迭代器的next方法返回值类型是Object,所以要记得类型转换。
List接口
List继承了Collection接口,是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。与Set不同,List允许有相同的元素。
除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个 ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素, 还能向前或向后遍历。
实现List接口的常用类有LinkedList,ArrayList,Vector,Stack。
LinkedList类
LinkedList实现了List接口,允许null元素。LinkedList提供额外的get,remove,insert方法在 LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。注意:LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。
一种解决方法是在创建List时构造一个同步的List:
List list = Collections.synchronizedList(new LinkedList(...));
ArrayList类
ArrayList就是动态数组,就是Array的复杂版本,它允许所有元素,包括null。size,isEmpty,get,set方法运行时间为常数,但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。
每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。
和LinkedList一样,ArrayList也是非同步的(unsynchronized)。
Vector类
Vector与ArrayList很是类似,有动态数组的功能,但是Vector是同步的。由Vector创建的Iterator,虽然和ArrayList创建的 Iterator是同一接口,但因为Vector同步,当一个Iterator被创建而且正在被使用,另一个线程改变了Vector的状态(例 如,添加或删除了一些元素),这时调用Iterator的方法时将抛出ConcurrentModificationException,因此必须捕获该异常。Stack类
由继承关系看出,Stack**继承自Vector**,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。List小结
ArrayList底层数据结构是数组,而且是线程不安全的。
增删慢,查询快
LinkedList
底层数据结构是链表,而且是线程不安全的。
增删快,查询慢
Vector
底层数据结构是数组,而且是线程安全的。
增删慢,查询快,但是由于线程安全,所以,增删查询都慢。一般不选择。
Set接口
继承了Collection接口,具有和Collection完全一样的接口,没有任何额外的功能。它是一种不包含重复的元素的Collection,任意两个元素e1和e2都有e1.equals(e2)=false,最多有一个null元素。
Set的构造函数有一个约束条件,传入的Collection参数不能包含重复的元素。同时,Set里的元素无序,即存储顺序和取出顺序不一致。
HashSet类
底层数据结构是哈希表,线程不安全。
一般使用HashSet的时候,要重写hashCode和equals方法。
LinkedHashSet
底层数据结构是链表和哈希表。
链表用于保证元素有序,哈希表用于保证元素唯一。
如何保证元素唯一性:
首先根据hashCode值判断。
若hashCode值不同,那么,这就是不同的元素。直接存储。
若hashCode值相同,那么,会继续根据equals方法进行判断,根据自己的需求来决定元素是否相同。如果相同,就不存储。否则,存储。
TreeSet类
是Set的一种变体——可以实现排序等功能的集合。
底层数据结构是二叉树,线程不安全。
保证元素排序的方式:
让元素本身具备比较性
实现Compareable接口中的compareTo方法。
让集合具备比较性
实现Comparator接口中的compare方法。
使用法则
如果要元素唯一,就用Set。否则,用List。如果元素唯一并且要排序用TreeSet,否则,用HashSet。
如果元素可重复,且考虑线程问题用Vector。否则用ArrayList或者LinkedList。
如果要求增删快,考虑使用LinkedList。
如果要求查询快,考虑使用ArrayList。
当什么要求都不清楚的时候,就使用ArrayList。
注:该篇博文部分知识点参考自以下博文
细说Java之util类
黑马程序员–javaSE–java集合容器总结
相关文章推荐
- Java 链接查询
- JAVA和JAVAC 命令行
- JAVAC 命令详解
- Java 组合查询
- 解决Java compiler level does not match the version of the installed Java project facet.问题
- java 小小计算器 显现了括号功能
- Java中新建数组
- Java 简单的数据查询
- Eclipse中10个最有用的快捷键组合
- Java 使用DML语句更改数据
- Java 保证数据的完整性
- Java nio
- Spring Security(Acegi)实现原理与应用一
- Java 管理数据库和表
- SpringMVC知识点汇总
- Ubuntu Java环境安装配置 简易版本
- 如何解决eclipse添加重载函数时参数为arg0,arg1的问题?
- Java设计模式12:装饰器模式
- Java实现先来先服务与短作业优先
- Java 关系型数据库管理系统简介