List集合
List集合的特点有:有序,有索引,可重复.
list接口下面有三个实现类:LinkedList,ArrayList,Vector
ArrayList集合的特点:有序有索引可重复
基于数组实现,每次增删都要创建新的数组,但是数组有索引,数组查找快,增删慢.轻量级(线程不安全)
Vector集合:基于数组,线程安全,效率低,重量级(使用少)
LinkedList:基于双向链表实现,每个元素存储本身内存地址的同时还存储下一个元素的地址,链表增删快.查找慢(线程不安全).
ArrayList集合
底层是Object数据,所以Arraylist具有数组的查询速度快的特点但是增删慢的缺点.
其中ArrayList有一个自动扩容的机制:
实现机制:ArrayList.ensureCapacity(int minCapacity)
首先得到当前elementData 属性的长度为oldCapacity
然后通过判断oldCapacity和minCapacity参数谁大来决定是否需要扩容,如果minCapacity大于oldCapacity,那么我们就对当前的List对象进行扩容.
扩容的策略为:取(oldCapacity*3)/2+1和minCapacity之间更大的那个,然后使用数组拷贝的方法,把以前存放的数据转移到新的数组对象中,.
如果minCapacity不大于oldCapacity那么就不进行扩容.
简单的说就是,ArrayList底层是使用数组进行实现的,然后,创建一个新的ArrayList集合时候,就相当于创建了一个新的数组,数组长度是固定的,然后,进行自动扩容时候,会创建一个新的数组,用来就老的数组进行替换掉.
LinkedList集合
底层是一种双向循环链表,在此链表上每一个数据节点都由三部分组成:前指针(前面节点的位置),数据,后指针(指向后面的节点的位置),最后一个节点的后指针指向第一个节点的前指针,形成一个循环.
双向循环链表的查询效率低但是增删效率高.
利用LinkedList实现栈(stack).队列(queue),双向队列(double-ended queue)
它具有方法addFirst(),addLast().getFirst().getLast().removeFirst().removeLast()等
经常用在增删操作较多而查询操作很少的情况下:
队列和堆栈.
队列:先进先出的数据结构
栈:后进先出的数据结构.
用LinkedList实现队列:
队列(Queue)是限定所有的插入只能在表的一端进行,而所有的删除都在表的另一端进行的线性表.
表中允许插入的一段称为队尾(Rear),允许删除的一端称为队头(Front)
队列的操作是按先进先出(FIFO)的原则进行的.
队列的物理存储可以用顺序存储结构,也可以用链式存储结构.
注意:使用栈的时候一定不能提供方法让最后一个元素的元素得出栈的机会,
用LinkedList实现栈:
栈(stack)也是一种特殊的线性表,是一种后进先出(LIFO)的结构,
栈是限定仅在表尾进行插入和删除运算的线性表,表尾称为栈顶(top),表头称为栈底(bottom)
栈的物理存储可以用顺序存储结构,也可以用链表存储结构.
Vector
(与ArrayList相似,区别Vector是重量级的组件,使用使得消耗资源比较多.)
结论:在考虑并发的情况下用Vector(保证线程的安全)
在不考虑并发的情况下使用ArrayList(不能保证线程的安全)
并发:在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,
且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行。
List集合中的常用方法为:
void add(int index, Object element) :添加对象element到位置index上
boolean addAll(int index, Collection collection) :在index位置后添加容器collection中所有的元素
Object get(int index) :取出下标为index的位置的元素
int indexOf(Object element) :查找对象element 在List中第一次出现的位置
int lastIndexOf(Object element) :查找对象element 在List中最后出现的位置
Object remove(int index) :删除index位置上的元素
ListIterator listIterator(int startIndex) :返回一个ListIterator 跌代器,开始位置为startIndex
List subList(int fromIndex, int toIndex) :返回一个子列表List ,元素存放为从 fromIndex 到toIndex之前的一个元素
Set集合:
扩展Collection接口
无序集合,不允许存放重复的元素;允许使用null元素
对 add()、equals() 和 hashCode() 方法添加了限制
HashSet和TreeSet是Set的实现
Set—》hashSet linkedHashSet
SortedSet —》 TreeSet
HashSet 的后台有一个HashMap;初始化后台容量;只不过生成一个HashSet的话,系统只提供key的访问; 如果有两个Key重复,那么会覆盖之前的;
实现类 :
HashSet:equals返回true,hashCode返回相同的整数;哈希表;存储的数据是无序的。
LinkedHashSet:此实现与 HashSet 的不同之外在于,后者维护着一个运行于所有条目的双重链接列表。存储的数据是有序的。
HashSet类
HashSet类直接实现了Set接口, 其底层其实是包装了一个HashMap去实现的。HashSet采用HashCode算法来存取集合中的元素,因此具有比较好的读取和查找性能。
HashSet的特征
-
不仅不能保证元素插入的顺序,而且在元素在以后的顺序中也可能变化(这是由HashSet按HashCode存储对象(元素)决定的,对象变化则可能导致HashCode变化)
-
HashSet是线程非安全的
-
HashSet元素值可以为NULL
HashSet常用方法:
public boolean contains(Object o) :如果set包含指定元素,返回true
public Iterator iterator()返回set中元素的迭代器
public Object[] toArray() :返回包含set中所有元素的数组public Object[] toArray(Object[] a) :返回包含set中所有元素的数组,返回数组的运行时类型是指定数组的运行时类型
public boolean add(Object o) :如果set中不存在指定元素,则向set加入
public boolean remove(Object o) :如果set中存在指定元素,则从set中删除
public boolean removeAll(Collection c) :如果set包含指定集合,则从set中删除指定集合的所有元素
public boolean containsAll(Collection c) :如果set包含指定集合的所有元素,返回true。如果指定集合也是一个set,只有是当前set的子集时,方法返回true
实现Set接口的HashSet,依靠HashMap来实现的。
我们应该为要存放到散列表的各个对象定义hashCode()和equals()。
实现Set接口的HashSet,依靠HashMap来实现的。
我们应该为要存放到散列表的各个对象定义hashCode()和equals()。
上面注释了hashCode方法,所以你将会看到下面的结果。
false
[R[count:9 # hashCode:14927396], R[count:5 # hashCode:24417480], R[count:-2 # hashCode:31817359], R[count:-3 # hashCode:13884241]]
取消注释,则结果就正确了 copy
true
[R[count:5 # hashCode:5], R[count:9 # hashCode:9], R[count:-3 # hashCode:-3], R[count:-2 # hashCode:-2]]
如何达到不能存在重复元素的目的?
“键”就是我们要存入的对象,“值”则是一个常量。这样可以确保,我们所需要的存储的信息
之是“键”。而“键”在Map中是不能重复的,这就保证了我们存入Set中的所有的元素都不重复。
HashSet如何过滤重复元素
调用元素HashCode获得哈希码--》判断哈希码是否相等,不相等则录入
---》相等则判断equals()后是否相等,不相等在进行 hashcode录入,相等不录入
LinkedHashSet的特征
LinkedHashSet是HashSet的一个子类,LinkedHashSet也根据HashCode的值来决定元素的存储位置,但同时它还用一个链表来维护元素的插入顺序,插入的时候即要计算hashCode又要维护链表,而遍历的时候只需要按链表来访问元素。查看LinkedHashSet的源码发现它是样的,
在JAVA7中, LinkedHashSet没有定义任何方法,只有四个构造函数,它的构造函数调用了父类(HashSet)的带三个参数的构造方法,父类的构造函数如下,
由此可知,LinkedHashSet本质上也是从LinkedHashMap而来,LinkedHashSet的所有方法都继承自HashSet, 而它能维持元素的插入顺序的性质则继承自LinkedHashMap.
下面是一个LinkedHashSet维持元素插入顺序的例子,
输入如下
TreeSet类的特征
TreeSet实现了SortedSet接口,顾名思义这是一种排序的Set集合,查看jdk源码发现底层是用TreeMap实现的,本质上是一个红黑树原理。 正因为它是排序了的,所以相对HashSet来说,TreeSet提供了一些额外的按排序位置访问元素的方法,例如first(), last(), lower(), higher(), subSet(), headSet(), tailSet().
TreeSet的排序分两种类型,一种是自然排序,另一种是定制排序。
自然排序(在元素中写排序规则)
TreeSet 会调用compareTo方法比较元素大小,然后按升序排序。所以自然排序中的元素对象,都必须实现了Comparable接口,否则会跑出异常。对于TreeSet判断元素是否重复的标准,也是调用元素从Comparable接口继承而来额compareTo方法,如果返回0则是重复元素(两个元素I相等)。Java的常见类都已经实现了Comparable接口,下面举例说明没有实现Comparable存入TreeSet时引发异常的情况。
运行程序会抛出如下异常
将上面的Err类实现Comparable接口之后程序就能正常运行了
还有个重要问题是,因为TreeSet会调用元素的compareTo方法,这就要求所有元素的类型都相同,否则也会发生异常。也就是说,TreeSet只允许存入同一类的元素。例如下面这个例子就会抛出类型转换异常
运行结果
定制排序(在集合中写排序规则)
TreeSet还有一种排序就是定制排序,定制排序时候,需要关联一个 Comparator对象,由Comparator提供排序逻辑。下面就是一个使用Lambda表达式代替Comparator对象来提供定制排序的例子。 下面是一个定制排序的列子
当然将Comparator直接写入TreeSet初始化中也可以。如下。
TreeSet是依靠TreeMap来实现的。
TreeSet是一个有序集合,TreeSet中元素将按照升序排列,缺省是按照自然顺序进行排列,意味着TreeSet中元素要实现Comparable接口
我们可以在构造TreeSet对象时,传递实现了Comparator接口的比较器对象。
Comparable和Comparator
Comparable 接口以提供自然排序顺序。
对于那些没有自然顺序的类、或者当您想要一个不同于自然顺序的顺序时,您可以实现
Comparator 接口来定义您自己的排序函数。可以将Comparator传递给Collections.sort或Arrays.sort。
Comparator接口
当一个类并未实现Comparable,或者不喜欢缺省的Comaparable行为。可以实现Comparator接口
直接实现Comparator的compare接口完成自定义比较类。
例:Arrays.sort(results, new Comparator<RepDataQueryResultVO>() 数组排序 RepDataQueryExecutor
例:Collections.sort(lst,new Comparator<TaskPrintSchemeVO>()
EnumSet特征
EnumSet顾名思义就是专为枚举类型设计的集合,因此集合元素必须是枚举类型,否则会抛出异常。 EnumSet集合也是有序的,其顺序就是Enum类内元素定义的顺序。EnumSet存取的速度非常快,批量操作的速度也很快。EnumSet主要提供以下方法,allOf, complementOf, copyOf, noneOf, of, range等。注意到EnumSet并没有提供任何构造函数,要创建一个EnumSet集合对象,只需要调用allOf等方法,下面是一个EnumSet的例子。
执行结果
几种Set的比较:
HashSet外部无序地遍历成员。
成员可为任意Object子类的对象,但如果覆盖了equals方法,同
时注意修改hashCode方法。
TreeSet外部有序地遍历成员;
附加实现了SortedSet, 支持子集等要求顺序的操作
成员要求实现Comparable接口,或者使用Comparator构造
TreeSet。成员一般为同一类型。
LinkedHashSet外部按成员的插入顺序遍历成员
成员与HashSet成员类似
HashSet是基于Hash算法实现的,其性能通常都优于TreeSet。我们通常都应该使用HashSet,在我们需要排序的功能时,我们才使用TreeSet。
HashSet的元素存放顺序和我们添加进去时候的顺序没有任何关系,而LinkedHashSet 则保持元素的添加顺序。TreeSet则是对我们的Set中的元素进行排序存放。
一般来说,当您要从集合中以有序的方式抽取元素时,TreeSet 实现就会有用处。为了能顺利进行,添加到 TreeSet 的元素必须是可排序的。 而您同样需要对添加到TreeSet中的类对象实现 Comparable 接口的支持。一般说来,先把元素添加到 HashSet,再把集合转换为 TreeSet 来进行有序遍历会更快。
各种Set集合性能分析
-
HashSet和TreeSet是Set集合中用得最多的I集合。HashSet总是比TreeSet集合性能好,因为HashSet不需要额维护元素的顺序。
-
LinkedHashSet需要用额外的链表维护元素的插入顺序,因此在插入时性能比HashSet低,但在迭代访问(遍历)时性能更高。因为插入的时候即要计算hashCode又要维护链表,而遍历的时候只需要按链表来访问元素。
-
EnumSet元素是所有Set元素中性能最好的,但是它只能保存Enum类型的元素