配色: 字号:
part3-java集合基础
2018-06-23 | 阅:  转:  |  分享 
  
java集合基础集合框架java集合概述集合和数组的差别集合适用于存储元素个数变化的情况,而数组不行,因为数组在定义的时候要么是程序员指
定长度,要么是虚拟机指定长度。数组可以存对象和基本数据类型,而集合不能存基本数据类型。(虽然我们之前将1,2,3都存到集合中,但事
实上这些基本类型会自动的转化为Integer类型,然后实现存储)数组不能保存具有映射关系的数据。而map集合可以。集合常用的两大
框架集合中有两个跟接口:Collection和MapCollection接口的框架图其中set集合为无序不可重复的集合;List集
合是有序,可重复的集合Map接口的框架图集合中的线程安全集合框架中的各个集合有的是线程安全的如vector、hashtable、
set。有些是线程不安全的如hashmap、Arraylist。线程不安全的:优点:性能高缺点:当多线程访问该集合时,无法自动保
证同步,因此需要手动编写代码来保证该集合的同步。Map、set、list示意图该图表明了获取上述的3中集合中的元素的方法是不
同的:获取List集合的元素可以通过索引值来获取获取Set集合的元素只能通过元素值来获取获取Map集合的元素可以通过key来获取C
ollection和iterator接口Collection中常用的方法注:(1)Collecion中重写了toString方法。
因此coll.toString()输出的格式为一个数组的格式。(2)无论什么类型的数据,当我们放入到没有指定范型的集合中,系统都
会认为所放入的元素为Object类型。使用Iterator遍历集合中的元素Iterator与Collection集合是密不可分的
。Collection集合具有存储元素的能力,而Iterator没有存储元素的能力,只有遍历能力。Itertor的设计就是为了遍历
Collection的,也就是说Iterator与Collection集合是密不可分的,没有Collection,则Iterato
r就失去了意义。从Iterator的源码上可以看出常用的只有3种方法:hashNext()、next()、remove()hash
Next():若Collection中没有可遍历的元素,则返回false,反之返回truenext():获取Collection
中下一个元素。返回类型为Objectremove():移除Collection里上一次next返回的元素。注:(1)通过迭代过程确
实能将Collection中的元素移除,但若是想通过迭代过程改变Collection中的元素,那是不可能的。如下:在迭代的过程中,
是不能对Collection集合进行操作的否则会报错:这种情况一般发生在编写多线成的时候,当一个线程正在遍历该Collection
集合,而另一个线程却在对Collection进行相关的操作。用foreach遍历集合中的元素注:与迭代器一样:不能在遍历的时候,
对Collection集合进行操作也就是说不能实现通过遍历的形式将集合中的元素删除。(2)同样在遍历的过程若想改变Collect
ion集合中的元素也是实现不了的。Set接口Set接口特点set集合中的元素是无序的(这个根据元素输出的顺序与元素添加的顺序不一致
就可以看出来)Set集合不能够添加已经存在的相同的元素(这里是添加不成功,一旦添加已经存在的元素,则返回的是false。输出时仅显
示添加成功的部分,从表面上看是重复元素覆盖了,实际上是跟本没添加进去)注:Set集合在判断所添加的元素是否重复,是根据字面值来判定
的(.equals()方法)而不是根据地址值来判定的(==),如下:显然我们虽然向Set集合中添加了两个地址值不同的元素,但由于这
两个元素的字面值相同,因此第二次添加元素失败。HashSet类HashSet类HashSet类是Set接口的典型实现,它是根据
Hash算法给添加其中的元素分配存储位置的。当向HashSet集合中存入一个元素时,HashSet会调用该对象的HashCode(
)方法来得到该对象的hashCode值,然后根据该HashCode值来决定该对象在HashSet中存储位置。正常情况下,只要两个元
素的equals的结果为true,那么hashset就会将这两个元素视为同一个元素,进而只能成功存储其中的一个。但若是我们对两个元
素对象重写equals()方法和Hashcode()方法。将其写成如下形式:则还会出现其他的结果:equals()返回true,
而HashCode()返回不同值,则hashset也会将这两个元素视为不同的元素,将其存储在hashset集合中不同的位置。但这样
与hashset的原理有点不符。equals()返回false,而HashCode()返回相同值,则这样就很复杂,HashCod
e()返回相同值将会导致hashset的性能下降。HashCode对HashSet的作用Hash算法的功能:它能保证通过一个对象
(HashSet)快速查找到另一个对象(HashSet中的元素)。HashSet存储元素:当程序向hashset中添加元素时,H
ashSet会根据该元素的HashCode值来决定它的存储位置,也就是说每个元素的HashCode就是它的索引。HashSet读
取元素:hash算法可以直接根据该元素的值得到该元素保存在何处。也就是说存的时候是根据hashcode,而读的时候是根据元素的字面
值。为什么不使用数组:(1)数组的长度固定(2)数组的索引值连续。为什么使用HashSet:(1)集合的长度不固定(2)索引值不连
续,访问速度快。为什么说HashSet存储字面不等但hashcode相等则性能下降HashSet中每个能存储元素的“槽位(slo
t)”通常称为“桶(bucket)”如果有多个字面不同但hashcode相同,就需要一个“桶”里存多个元素,从而导致性能下降。
重写Hashcode方法的基本原则当equals返回true时,则hashcode应该相同当equals返回false时,则ha
shcode不应该相同注:事实上字面值与hashcode值没有什么关系,但时基于hashset的基本规则,我们若是重写了hashc
ode的方法,那就应该遵循上面的两个原则,同时还有重写equals方法。TreeSet类虽然我们说Set集合是无序的集合,但这里所
说的无序指的是添加顺序与输出顺序不一致。但TreeSet类虽然添加顺序与遍历输出的顺序不一致,但在TreeSet集合内部已经对所添
加的元素进行了升序排序,这一点是与HashSet不同的。也正因如此,基于TreeSet内部自动排序的规则,使得TreeSet增加了
如下方法:TreeSet中的元素识别在HashSet中元素是通过.equal和hash值来判定元素是否相同。其中.equals(
)方法用来判定元素是否重复,hash值用来判定元素的存储位置。在treeset中,是通过.equal()和comparaTo方法来
判断的。其中.equals()方法是用来决定元素是否重复,而comparaTo方法是用来判断元素的存储位置的。同样当我们重写.e
qual()和comparaTo方法时,要求若.equal()返回true则comparaTo返回0。表示元素相同。注:a.com
paraTo(b)方法返回0时表示相同,返回正数时表示a大于b,返回负数时表示a小于b.关于TreeSet所支持的内部排序Ha
shSet通过hash算法来确定元素的位置。而TreeSet通过红黑树数算法确定元素的存储位置。TreeSet支持两种排序算法:自
然排序定制排序1)自然排序自然排序指的是实现Comparable接口,并重写ComparaTo方法。自然排序是从小到大升序排序。
事实上很多类都继承了Comparable接口,因此它们都具有可排序的特性。对于treeSet而言,因为其内部使用了这种排序规则,因
此对所添加的元素有如下要求:(1)所添加的元素具有可比较的性质。若没有可比较的性质则添加元素时会报错。例如我们添加自定义的对象,若
不让自定义类去实现Comparable接口则该元素就无法添加。(2)所添加元素的类型需要一致。因为若不一致则无法进行比较。注:Tr
eeSet就有对所添加的元素进行排序的功能,但是若在添加完元素之后,对其中的某个元素进行了修改,则treeset不会对修改后的元素
进行排序,同时若修改的元素与集合中的某个元素相同则,treeset也不会理会。2)定制排序本质就是利用排序的另一种方法,即实现co
mparator接口,重写compara方法。这里可以通过匿名内部类来实现。使用匿名内部类,事实上需要在创建treeSet集合时使
用。Set中几种实现的比较HashSet的性能总是比TreeSet好,这是因为treeSet涉及到了排序。除非我们要求排序,否者
我们选用Hashset.LinkedHashSet:是HashSet的一个子类,对于普通的插入、删除操作,Linked要慢,这是
因为它要维护一个链表。但它查询的时候就会很快。既然是链表那么就会是有顺序的。EnumSet是所有Set实现类中性能最好的,但它只能
保存同一个枚举类的枚举值作为结合的元素。注:HashSet、TreeSet、EnumSet都是线程不安全的,因此在面临多个线程同时
访问一个Set集合的时候,需要手动地保持Set集合的同步,一般我们使用:SortedSets=Collections.s
ynchronizedSortedSet(newTreeSet())的方法来设定集合,如:五、List接口1、List接口
提供的常用的方法:List集合仅仅通过.equals()方法来识别是否是同一个元素set集合提供了iterator方法(正向迭代)
,而List集合提供了listIterator方法(逆向迭代)。其中ListIterator类是Iterator类的子类。因此Li
stlterator类具有Iterator类的全部方法,同时还提供了如下方法。hasPrevious()方法Previous()方
法add()方法ArrayList和Vector------>stock实现类ArrayList和Vector作为List的俩
个典型的实现,完全支持List接口的全部功能。ArrayList和Vector是基于数组的实现类,因此ArrayList和vect
or都封装了一个动态的数组Object[]。每个ArrayList或Vector对象有一个capacity属性,这个capacit
y表示它们所封装的Object[]数组的长度。当向ArrayList或Vector中添加元素时,它们的capacity会自动增加。
CapacityCapacity表示的是ArrayList和Vector中动态数组的长度。每向这两个集合中添加一个元素时,Capa
city都会增加一次。若是有多个元素添加,则显然Capacity会增加多次。造成效率低下,因此在得知我们需要添加的元素的个数的时候
,可以通过设置Capacity的值等于添加元素的个数,这样就可以一次性的增加capacity的值了。默认情况下capacity=1
0;Capacity的值过大也会占用空间。因此有如下两种方法来调节capacity的值:(2)ArrayList是线程不安全的,即
当有多个线程访问arrayList时,需要手动的将同步arraylist而vector是线程安全的。但即使它是安全的我们也不用v
ector。(3)Stack是vector的实现类Stack模拟了栈的数据结构,即先入后出的原则。Stack提供了3种方法:po
p()方法:出栈,相当于剪切,一旦出栈,则stack中的出栈元素就会消失。peek()方法:获取元素但不出栈,相当与复制。pus
h()方法:入栈,固定长度的List对于工具类Arrays其中有个静态方法asList()它是将一个数组或一组数据转化
为一个集合,这个集合就是List集合,但这里的list集合并不是我们之前所说的List。这里的list是Arrays中的Array
List的实现类,一定注意这里所说的ArrayList也不是我们之前所说的ArrayList集合。这个List集合的特点是只能遍历
,不能增加或删除元素。六、Queue(单向队列)接口Queue接口模拟了队列这种数据结构,即满足先近先出原则。在访问Queue接口
中的元素时,都是从队列头部开始访问,向尾部添加元素。结合集合框架可知,Queue的一个实现类为LinkList和PriorityQ
ueue。Queue常用的方法LinkedList实现类LinkedList从集合框架中可以看出它的数据结构是十分特殊的。Lin
kedList即是List的实现类,也是Deque接口(双向队列)的实现类,这就导致了LinkedList即有双向队列的性质,又有
链表的性质,同时由于双向队列可以转换为栈结构,因此LinkedList也具有栈结构的性质。LinkedList常用的方法:Li
nkedList方法做链表使用方法名说明list.add(9)向集合中添加元素LinkedList做双向队列的使用list
.addFirst(12)向双向队列的头部添加元素list.addLast(14)向双向队列的尾部添加元素list.offerFi
rst(11)向双向队列的头部添加元素,返回boolean类型list.offerLast(15)向双向队列的尾部添加元素,返回b
oolean类型Iteratoriterator=list.descendingIterator();返回迭代器,该迭代器
是逆向遍历Objectfirst=list.getFirst()获取双向队列的头元素,但不删除,若队列为空则报错Object
last=list.getLast()获取双向队列的尾元素,但不删除,若队列为空则报错Objecto=list.pee
kFirst();获取双向队列的头元素,但不删除,若队列为空则返回nullObjecto1=list.peekLast();
获取双向队列的尾元素,但不删除,若队列为空则返回nullObjecto2=list.pollFirst()取双向队列的头元素
,同时删除,若队列为空则返回nullObjecto3=list.pollLast()获取双向队列的尾元素,同时删除,若队列为
空则返回nullObjecto4=list.removeFirst()获取双向队列的头元素,同时删除,若队列为空则报错Obj
ecto5=list.removeLast()获取双向队列的尾元素,同时删除,若队列为空则报错booleanb=lis
t.removeFirstOccurrence(14);删除集合中首次出现的指定的元素list.removeLastOccurre
nce(9)删除集合中最后出现的指定的元素LinkList做栈使用Objectpop=list.pop();list.
push(20);名称底层模拟ArrayList动态数组数组LinkList双向队列、栈、链表Queue单向队列Dequ
e双向队列Vector数组栈PriorityQueue队列非常规队列PriorityQueue实现类PriorityQue
ue是Queue的一个实现类,也是队列,但这个实现类违反了FIFO的原则,它并不是先进先出,而是对添加到其内部的元素进行了由小到大
的排序,当我们通过peek或poll方法获取PriorityQueue中的元素时,该集合会将最小的元素先返还出来。注:1)Prio
rityQueue要求不能添加null元素2)PriorityQueue会对添加的元素进行排序,与treeset类似,也涉及到了自
然排序和定制排序。MapMap集合中存储两组值,一组值用于保存map中的Key,一组值用于保存map中的value。从上图中可以得
出map集合有如下特点。map中的key数据组类似于set集合。同样是无序,且不能重复(这里不能重复指的是若是重复则发生覆盖,这一
点与Set不同,set是若重复则不添加元素失败返回false),事实上key的存储格式与set集合是一样的。map中的value数
据组类似于List集合。同样都是靠索引来获取,同样都可以存储相同的值。(3)通过map集合中的entrySet()方法,我们可以将
Map集合理解为Set集合的特例,即Map集合是Set集合存储Entry类型元素的情况。Map集合中常用的方法注:对于集合中的元
素在没有指定范型的情况下,默认存储元素的类型都是Object类型。因此即便是,map.entrySet()产生的集合set,其内
部元素是Map.Entry类型,但在遍历该set集合时,传出来的元素也是被认为是Object类型,若向用Entry类中的getKe
y方法和getValue方法,则必须进行强制类型转换。这个很好理解,例如将一个String类型的元素添加到集合中,在遍历该集合时
,你就不能写成for(Stringx:k)而应该写成for(Objectx:k).同时无论元素是什么类型,只要是在集合没有指
定范型的情况下,向该集合中添加的元素最终都会被默认为Object类型。HashMap与Hashtable实现类HashMap是
线程不安全的,Hashtable是线程安全的。但仍然提倡使用HashMap,对于其线程安全问题可以通过Collections提供的
方法来完善。Hashtable不允许添加空值的即key不能为null,value也不能为null,例如(“ss”,null)和(n
ull,“as”)都会报空指针异常。HashMap可以放入空值。HashMap和HashTable仍然使用hashcode和.eq
uals()方法来存储元素。要求hashcode相等、.equals()返回true的时候,则说明两个key相同。HashMa
p的子类LinkedHashMapHashMap本身是无序的,即插入元素的顺序与遍历元素的顺序不一样。而LinkedHashMap
由于其数据结构是一个双向链表,因此其插入元素得顺序与遍历元素的顺行一致。这个与hashset和linkedhashset是一样的。
Hashtable的子类PropertiesProperies类可以把Map对象和属性文件(windows中的.ini文件)关联起
来,从而可以把Map对象中得Key-value写入属性文件中;也可以把属性文件中的键值对形式得内容加载到Map对象中。注:由于属性
文件里的属性名,属性值只能是字符串类型,所以Properties里得key、value都是字符串类型。将Properties集合中
的属性添加到a.ini属性文件中(store)注:若a.ini文件最开始不存在,则执行程序后,该文件就会在指定的路径下被创建;若该
文件以及存在,则该程序仍然会正常执行。结果:将b.ini文件中的键值属性添加到Properities集合中(load)编写b.in
i文件如下:将内容下载到Properties集合中:注:1)虽然我们已经将b.ini文件中的内容添加到了Properties集合中
,但是在输出prop2对象中的内容时,不能通过prop2.getProperties(“bb”);的方法来获取相对应的值。getP
roperties()方法使用得前提是prop2中的值是通过setProperties(“key”,”value”)方法添加键值信
息才行。2)在Prop1向a.ini添加元素的时候,所添加得元素只能是(String,String)不能含null。否者报错。3)
在编写b.ini文件时,当我们向该文件中写了一个字符串,而没有写成键值对的形式,则该文件向Properties导入内容时,会将文件
中的那个字符串识别为键值对,并将该字符串作为键来传入priperties中。SortedMap接口和其实现类TreeMapTr
eeMap与TreeSet几乎就是一样的。TreeMap会对所添加得元素进行排序(因此要求所添加的元素比需是同一类型,且具有比较性
)TreeMap排序方式也有两种:1)自然排序2)定制排序TreeMap在添加元素时也是通过.equals()和compara
To()两个方法确定所填加的元素是否重复,以及添加元素所存放得位置。TreeMap常用的方法:WeakHashMapWeak
HashMap与HashMap一样也是Map的一个子类。所不同的是WeakHashMap对key为弱引用类型的键值对会进行垃圾回收
。WeakHashMap的这个特点是有用的,因此我们在向WeakHashMap中添加元素的时候不要添加key为强引用类型的键值对,
因为那样就失去了WeakHashMap的意义。所谓强引用类型就是有变量去引用这个实例,如Studentstu=newS
tudent();IdentityHashMapIdentityHashMap与HashMap基本相同,所不同的是Identi
tyHashMap在存储元素的时候,只能根据严格的相等判断(==)来判定两个元素是否相同。而不是由.equal和hashcode来
判定。HashSet和HashMap的性能选项Hash表里可以存放元素的位置称为“桶”,通常一个桶中存放一个元素其性能最好当发生H
ash冲突的时候,一个桶中可能放入多个不同元素,那么此时Hash表会使用链表得结构来存储这些值。Hash冲突:对于HashSet
来说,Hash冲突指的是.equals()判断元素为相同的元素,但Hash算法判断该元素得hashcode确不同。这样就导致了存进
相同的元素,但这两个元素需要放在不同的位置上。HashSet、HashMap、HashTable都是基于Hash算法的。因此就有
一些相同的特性:容量:Hash表中桶的个数。初始化容量:创建Hash表时桶的数量。尺寸:当前散列表中记录的数量(当前集合中元素的个)负载因子:容量/尺寸;例如负载因子为0,说明该散列表中没有元素;负载因子为50%,说明该散列表为半满的散列表。负载极限:负载极限决定了该散列表最大填满程度。负载极限得取值范围在0~1之间当hash表中的负载因子达到了负载极限,hash表会自动扩容(增加桶的个数)并将原来得全部存储对象重写分配空间。默认HashSet、HashMap、HashTable的负载极限为75%Collections集合操作的工具类(1)排序操作(2)查找替换操作(3)同步控制之前说过线程不安全的集合有:ArrayList、HashSet、HashMap等。也就是说当遇到多线程去访问他们的时候,他们是无法保证同步的,但尽管如此我们也不使用线程安全的集合。如vector、Hashtable等。我们可以通过Collections工具类提供的synchronizeXxx()方法将非线程安全的集合传化为线程安全的集合。创建不可变集合不可变集合就是不能向集合中添加元素、修改元素、删除元素等。一旦执行修改集合的操作,则会报错。Collections.emptyXxx()创建一个不可变的空集合Collections.singletonXxx()创建一个仅含有一个指定对象的不可改变的集合Collections.unmodifiableXxx()反回指定集合对象的不可变试视图。
献花(0)
+1
(本文系实习生101首藏)