您的位置:首页 > 编程语言 > Java开发

java集合类总结

2015-03-16 15:31 113 查看
    最近将整个集合类框架复习了一遍,发现自己以前对集合类的了解真的非常不足,这次将整个集合类整理一遍,方便以后进行查阅。

 

    Java的集合类(Collection)是一种非常有用的工具类,可以存储数量不等的多个对象,并且可以实现常用的数据结构,比如队队列和栈。Java中的集合类主要分为Set,List和Map三种体系:Set代表的是无序的,不能重复的集合;List代表的有序,可以重复的集合;Map代表的具有映射关系的集合。

    Java集合类只要由两个接口类派生而来,Collection和Map,Collection类下面又Set,Queue,List三个接口,Map下面派生出了HashMap,Hashtable等类,这里就不再一一列举,我们重点整理一下一些常用的集合类。

 

一、Set集合

Set集合类似于一个罐子,将对象丢进Set里面之后不一定会保持原有的顺序,Set集合里面不允许包含两个相同的对象,判断两个对象是否相等的规则是调用将要加入的对象的equals方法,如果返回true,则判定为两个对象相同,不会接受后来存进来的对象,上面是Set的通用知识,下面介绍Set的两个子类HashSet和TreeSet,这两个类非常的常用。

HashSet算是最常用的Set类,它是按照Hash算法来存储集合里面的元素,因此具有很好的存取和查找性能。当一个元素要存储到HashSet里面时,首先利用该对象的hashCode()算法运算出该对象的hash值,该hash值决定了该元素在HashSet中存储的位置,如果在该位置已经存在了对象,则利用equals方法比较两个对象,如果返回值为true,则在该位置会存储多个对象,这样会影响HashSet的性能。所以我们要将对象存入HashSet时,如果要重写equals方法,也因重写hashCode方法,当量对象equals方法返回值为true时,hashCode的返回值也应该相同。

package com.collection.set;
import java.util.HashSet;

public class Test_HashSet {
public static void main(String[] args) {
Person a=new Person("a");
Person b=new Person("b");
Person c=new Person("a");
HashSet<Person> h=new HashSet<>();
h.add(a);
h.add(b);
h.add(c);
System.out.println(h);   //输出的值是[myname:b, myname:a]
System.out.println(h.size());//输出的内容是2,因为当name成员变量值相同的时候就被认定为相同的对象
}
}

class Person{
String name;
@Override
public String toString(){
return "myname:"+name;
}
public Person(String name){
this.name=name;
}
@Override
public boolean equals(Object o){
Person p=(Person)o;
if(p.name.equals(this.name)){
return true;
}
return false;
}
@Override
public int hashCode() {
return name.length();
}
}


 

TreeSet类是Set的子类中可以对元素进行排序的类,既然要进行排序,那么则需要有排序的规则。TreeSet提供了两种排序,一种是自然排序,一种是定制排序。自然排序的状况下,应该使加入集合的对象的类实现Comparable接口,重写compareTo方法,值得注意的是,当compareTo方法返回值为0时,equals方法也必须返回true,这直接决定了加入的对象按照什么样的规则进行排序。定制排序的状况下,在创建TreeSet对象的时候传入一个Comparator类型的参数就可以了,如TreeSet t=new TreeSet(Comparator c);这样在Comparator里面定义比较规则即可。

package com.collection.set;

 

import java.util.Comparator;
import java.util.SortedSet;
import java.util.TreeSet;

public class Test_TreeSet {
public static void main(String[] args) {
//1.测试可排序的Set类TreeSet的自然排序
System.out.println("-----------自然排序-----------");
TreeSet<A> t=new TreeSet<>();
A a1=new A(1);
A a2=new A(100);
A a3=new A(30);
A a4=new A(20);
A a5=new A(100);
t.add(a1);
t.add(a2);
t.add(a3);
t.add(a4);
t.add(a5);
System.out.println(t);
SortedSet<A> t1=t.headSet(new A(50));   //得到new A(50)之前的所有元素的set
System.out.println(t1);
//定制排序
System.out.println("------------定制排序-------------------");
TreeSet<A> tt=new TreeSet<>(new Comparator<A>() {
@Override
public int compare(A o1, A o2) {
return o1.a>o2.a?-1:(o1.a<o2.a?1:0);   //降序排列
}
});
tt.add(a1);
tt.add(a2);
tt.add(a3);
tt.add(a4);
tt.add(a5);
System.out.println(tt);
SortedSet<A> t2=tt.headSet(new A(50));   //得到new A(50)之前的set
System.out.println(t2);
}
}

class A implements Comparable{
int a;
public String toString(){
return "value:"+this.a;
}
public A(int a){
this.a=a;
}
@Override
public int compareTo(Object o){
A oa=(A)o;
return this.a>oa.a?1:(this.a<oa.a?-1:0);  //按照升序排列
}
}


 

二、List集合

    List集合里面存储的都是有顺序、可以重复的对象,访问List集合里面的元素的时候可以使用get(int index);方法利用元素的位置索引进行访问,也可以利用迭代器进行访问。

package com.collection.list;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Test_List {
public static void main(String[] args) {
List<String > list=new ArrayList<>();
list.add("string1");
list.add("String2");
list.add("string3");
list.add("string4");
System.out.println("利用元素位置索引进行遍历:");
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
System.out.println("利用迭代器进行遍历:");
Iterator it=list.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}


输出为

利用元素位置索引进行遍历:

string1

String2

string3

string4

利用迭代器进行遍历:

string1

String2

string3

string4

 

ArrayList和Vector都是实现了List接口,他们都是基于数组实现的List类他们都可以用initialCapacity参数来设置该数组的长度,如果存储元素的个数超出了这个长度则会对数组进行重新分配,通常情况下都不用去设置这个值,除非在编程的时候已经知道要一次性存储大量的元素到集合中,这样可以分配一个合适的初始长度,可以减少重新分配的次数。同时Vector和ArrayList都提供了两个方法来调整它们的Object[]数组长度,

1.void ensureCapacity(int minCapacity),将ArrayList或者Vector的Object[]数组的长度增加minCapacity.

2.void trimtoSize():调整ArrayList或者Vector的Object[]数组长度为当前元素的个数。

ArrayList和Vector两个类的操作方法基本上都是一致的,但是值得注意的是ArrayList一个线程不安全的集合,Vector是一个线程安全的集合,因此Vector的性能要比ArrayList稍微低一点。但是通常情况下即使是要使用线程安全的集合也不会使用Vector,而是用Collections工具类的synchronizedList(List<T> list)将ArrayList转换成线程安全的集合。

 

三、Queue集合

Queue很明显是属于List体系的,Queue用于模拟队列这种数据结构,队列具有“先进先出”的性质,首先介绍几个Queue接口的几个重要的方法.

boolean offer(Object e)将指定元素加入这个队列的尾部

Object peek()获取队列头部的元素,但是不删除该元素

Object poll();获取队列头部的元素并且删除该元素

boolean isEmpty();返回该队列是否为空的布尔值

package com.collection.list;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Test_List {
public static void main(String[] args) {
List<String > list=new ArrayList<>();
list.add("string1");
list.add("String2");
list.add("string3");
list.add("string4");
System.out.println("利用元素位置索引进行遍历:");
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
System.out.println("利用迭代器进行遍历:");
Iterator it=list.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}


 

下面介绍优先队列PriorityQueue,在该队列中用poll()取出元素的时候得到的是队列中最小的元素,该队列能按照指定的规则进行排序,一共有两种排序方式:

1.自然排序,加入优先队列的对象必须实现了Comparable接口,否则会抛出异常。

2.定制排序,在创建优先队列的时候传入Comparator对象,该对象负责对队列中所有元素进行排序。

如果对象实现了Comparable接口而且创建优先队列的时候也传入了Comparator对象,则以Cmparator里面的排序规则为主。

package com.collection.queue;
import java.util.PriorityQueue;
public class Test_PriorityQueue {
public static void main(String[] args) {
PriorityQueue<Person> pq=new PriorityQueue<>();
Person p1=new Person(10);
Person p2=new Person(60);
Person p3=new Person(30);
Person p4=new Person(1);
pq.offer(p1);
pq.offer(p2);
pq.offer(p3);
pq.offer(p4);

while(!pq.isEmpty()){
System.out.println(pq.poll());   //弹出队列最前面的元素
}
}

}

class Person implements Comparable{
int age;
public String toString(){
return "age:"+age;
}
public Person(int age){
this.age=age;
}
public int compareTo(Object o){
Person p=(Person) o;
return this.age-p.age;   //按照age的大小排序
}
}


四、Map集合

Map集合保存的是具有映射关系的元素,因此Map里面需要保存两组值,一组是Map的key,一组是Map的value,key和value都可以是任意类型的值,key和value具有一一映射的关系,通过一个key能够找到唯一的一个value,但是不同的key也可能拥有相同的value,所以Map集合读取值得方法是通过提供元素的key来获取value。

如果只看Map的key,这就是一个Set集合,里面没有重复的元素,如果只看Map的key,这就是一个List集合。但是Map里面存储的是key-value对,Map内部提供了一个Entry类来封装key-value.

HashMap和Hashtable都是典型的Map实现类,他们的关系类似于ArrayList和Vector类,HashMap和Hashtable有两个很明显的区别,HashMap是线程不安全的集合,而Hashtable是线程安全的,HashMap可以使用null作为key或value,但是key只能使用一次null作为值,Hashtable则不能使用null作为key和value。一般情况下,即使在需要线程安全的Map集合的时候也不使用Hashtable,而是使用Collections里面的方法将HashMap封装成线程安全的集合。

下面示范利用HashMap统计词频

package com.collection.map;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;

public class Test_HashMap {
public static void main(String[] args) {

//利用HashMap统计词频
String words[]={"你","我","他","她","我","你"};
HashMap<String,Integer> map=new HashMap<>();//键为词,值为词出现的频率
for (int i = 0; i < words.length; i++) {
if(map.containsKey(words[i])){
int count=map.get(words[i]);
count++;
map.put(words[i], count);
}else{
map.put(words[i], 1);
}
}
Iterator<Entry<String, Integer >> it=map.entrySet().iterator();
while(it.hasNext()){
Entry<String,Integer> e=it.next();
System.out.println(e.getKey()+"  出现了   "+e.getValue() +"  次");
}
}
}


 

输出的结果为:

他  出现了   1  次

我  出现了   2  次

她  出现了   1  次

你  出现了   2  次

 

Properites类是Hashtable的子类,该类在处理属性文件的时候特别方便,他可以直接把属性文件里面的”key=value”格式的数据加载到内存中,读取配置文件等非常方便,也可以将内存中的键值对存入文件中,下面演示利用Properties读取并且修改ini文件

文件修改之前:

 


package com.collection.map;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Properties;

public class TestProperties {
public static void main(String[] args) throws Exception {

//Properties类是Hashtable的子类,这里用来测试读取属性文件
Properties pro=new Properties();
FileInputStream fis=new FileInputStream(new File("config.ini"));
pro.load(fis);
fis.close();  //关闭输入流
/************修改前的配置信息************/
System.out.println("------------修改前");
Iterator it=pro.entrySet().iterator();   //获取Entry的迭代器
while(it.hasNext()){
Entry<String,String> e=(Entry<String,String  >) it.next();
System.out.println(e.getKey()+":"+e.getValue());
}
pro.setProperty("age", Integer.parseInt(pro.getProperty("age"))+1+"");//将年纪增加
/************修改后的配置信息************/
System.out.println("---------------修改后");
Iterator it1=pro.entrySet().iterator();   //获取Entry的迭代器
while(it1.hasNext()){
Entry<String,String> e=(Entry<String,String  >) it1.next();
System.out.println(e.getKey()+":"+e.getValue());
}
FileOutputStream fos=new FileOutputStream("config.ini");
pro.store(fos, "");  //将pro里面的信息加入fos文件流
fos.close();
}
}


输出的结果:

------------修改前

age:22

name:xiao

school:cqupt

---------------修改后

age:23

name:xiao

school:cqupt

 

文件修改后

 


 

在前面讲Set集合时讲到了TreeSet,是可以排序的Set,Map里面同样有可以排序的集合TreeMap,这和TreeSet的实现非常类似,他们都有两种排序方式:自然排序和定制排序,不过TreeMap的排序需要的是key必须是先Comparable接口或者是在Comparator中指定key的排序规则,如果使用自定义类作为TreeMap的key,且想让TreeMap良好地工作,则重写该类的equals()方法的时候和compareTo应该保持一致的返回结果,当equals()方法比较返回true的时候,compareTo的方法应该返回0。

package com.collection.map;

import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

public class Test_TreeMap {
public static void main(String[] args) {
TreeMap<Person,String> map=new TreeMap<>();
Person p1=new Person(10);
Person p2=new Person(1);
Person p3=new Person(88);
Person p4=new Person(22);
map.put(p1, "age:"+p1.age);
map.put(p2, "age:"+p2.age);
map.put(p3, "age:"+p3.age);
map.put(p4, "age:"+p4.age);
Set<Map.Entry<Person, String>> s=map.entrySet();
Iterator<Map.Entry<Person, String>> it=s.iterator();
while(it.hasNext()){
System.out.println(it.next().getValue());
}
}
}

class Person implements Comparable<Person>{

int age;
public Person(int age){
this.age=age;
}
@Override
public int compareTo(Person o) {
return this.age-o.age;
}
public boolean equals(Object o){
Person p=(Person)o;
return this.age==p.age;
}
}


输出的结果为:

age:1

age:10

age:22

age:88

 

五 线程安全的集合

1.CopyOnWriteArraySet

CopyOnwriteArraySet是一个线程安全的Set,它的原理和它的名字很像,在有写入的时候,首先会将set锁定,然后将set里面的元素数组复制一份,然后在副本上面修改数据,修改完成之后将修改写入set,解锁,由于每次写都要将数组里面的元素复制一次,所以写,修改数组元素相关的操作是很耗费性能的。因为修改操作是在副本里面执行的,所以读的时候直接读取即可,不用加锁操作。所以CopyOnWriteArraySet适合使用的场景是读的次数较频繁,写的次数较少的时候。

2.CopyOnWriteArrayList

CopyOnWriteArrayList是线程安全的List,CopyOnWriteArrayList就是基于它实现的。它的各操作如下:

写入元素:锁住表,开辟一个新的数组,大小为原List大小+1,然后将所有元素复制到新数组,最后加入要添加的元素,将值赋给原List的数组。

读取操作:直接读取List元素,不需要加锁,即可线程安全。

3.Collections.synchronized(Collection .) 方法

使用该方法产生的线程安全的集合实际是在内部创建了一个新的集合并且返回,在新的集合的每个方法内都会利用synchronized方法进行加锁,然后操作数据,如

public boolean add(E e) {
synchronized (mutex) {return c.add(e);}
}
public boolean remove(Object o) {
synchronized (mutex) {return c.remove(o);}
}
public E get(int index) {
synchronized (mutex) {return list.get(index);}
}
这使得读和写错做都会加锁,不能同时进行。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 编程 集合类