JAVA常用集合框架用法详解基础篇三之Colletion子接口Set

这一篇我们来介绍Collection接口的另一个子接口,Set接口。Set是个接口,元素不可以重复,是无序的。Set接口中的方法和Collection的一致。

A、Set的子类:

1、HashSet:此类实现的Set接口,由哈希表(实际上是一个HashMap)实例支持,它不保证Set的迭代顺序,但是允许使用null元素。内部结构是哈希表,是不同步的。

哈希表确定元素是否相同的步骤如下:

一、    判断的是两个元素的哈希值(hashcode)是否相同。如果相同,再判断两个对象的内容是否相同,如果内容相同就表示两个元素是相同的。

二、    判断哈希值是否相同,其实判断的是对象的hashCode()的方法。判断内容是否相同,用的是equals()方法。

三、    如果哈希值不同,那就不需要进行equals判断。

HashSet集合数据结构是哈希表,所以存储元素的时候,使用元素的hashCode()方法来确定位置,如果位置相同,再通过元素的equals方法来确定是否相同。

 

练习题:定义功能去除ArrayList的重复元素?

注意:像ArrayList集合,在判断元素是否相同的时候,仅仅需要判断equals()方法。因为数据结构不同,对元素的判断依据也就一样。

代码如下:

package com.wq.person;

public class Person extends Object {
		private String name;
		private int age;
		
		public Person() {
			super();
			// TODO Auto-generated constructor stub
		}
		public Person(String name, int age) {
			super();
			this.name = name;
			this.age = age;
		}
		public String getName() {
			return name;
		}
		public void setName(String name) {
			this.name = name;
		}
		public int getAge() {
			return age;
		}
		public void setAge(int age) {
			this.age = age;
		}
	
		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + age;
			result = prime * result + ((name == null) ? 0 : name.hashCode());
			return result;
		}
		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (!(obj instanceof Person))
				return false;
			Person other = (Person) obj;
			if (age != other.age)
				return false;
			if (name == null) {
				if (other.name != null)
					return false;
			} else if (!name.equals(other.name))
				return false;
			return true;
		}
		@Override
		public String toString() {
			// TODO Auto-generated method stub
			return name+"...."+age;
		}
		
}





//hashset测试程序
package com.wq.hashset;

import java.util.HashSet;
import java.util.Iterator;

import com.wq.person.Person;

public class testHashSet {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		HashSet hs=new HashSet();//实例化一个hashset对象
		hs.add(new Person("lisi1",21));
		hs.add(new Person("lisi2",22));
		hs.add(new Person("lisi3",23));
		hs.add(new Person("lisi4",24));
		hs.add(new Person("lisi4",24));//如果想在迭代中不出现相同的元素,需要通过覆写Person类的hashcode和equals方法
		//使用迭代器进行迭代
		
		for(Iterator it=hs.iterator();it.hasNext();){
			Person pe=(Person)it.next();
			System.out.println(pe.getName()+"---"+pe.getAge());
		}
	}

}


运行结果如下(由于无序这只是一种可能):
lisi4---24
lisi3---23
lisi2---22
lisi1---21

特别注意:如果希望元素不重复,但是要有序,这时可以考虑使用LinkedHashSet,它是HashSet的子接口。


 2、TreeSet接口,可以对Set集合的元素进行排序,不是同步的。它底层的数据结构是红黑树。

判断元素唯一性的方式就是根据比较方法compareTo()的返回结果,判断返回的结果是否为0,是0就表示相同的元素且不会存储在Set中的(Set是不允许存储重复的元素的)。如果返回的结果不是0,则表示的是不相同。

TreeSet对元素进行排序的方式一:让元素具备比较功能,元素需要实现comparable接口,覆盖CompareTo()方法。

 

如果不要按照对象具备的自然顺序进行排序,如果对象不具备自然顺序。(即对象没有比较性,或者具备的比较性不是我们所需要的)这个时候怎么办???

TreeSet对元素进行排序的方式二:(生成一个比较器)让集合自身具备比较的功能,定义一个类,实现Comparator接口,覆盖compare方法,将该类的对象作为参数传递给TreeSet集合的构造函数。

   

代码演示如下:

//Person类
package com.wq.person;
//需要实现Comparable接口
public class Person extends Object implements Comparable{
		private String name;
		private int age;
		
		public Person() {
			super();
			// TODO Auto-generated constructor stub
		}
		public Person(String name, int age) {
			super();
			this.name = name;
			this.age = age;
		}
		public String getName() {
			return name;
		}
		public void setName(String name) {
			this.name = name;
		}
		public int getAge() {
			return age;
		}
		public void setAge(int age) {
			this.age = age;
		}
	
		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + age;
			result = prime * result + ((name == null) ? 0 : name.hashCode());
			return result;
		}
		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (!(obj instanceof Person))
				return false;
			Person other = (Person) obj;
			if (age != other.age)
				return false;
			if (name == null) {
				if (other.name != null)
					return false;
			} else if (!name.equals(other.name))
				return false;
			return true;
		}
		@Override
		public String toString() {
			// TODO Auto-generated method stub
			return name+"...."+age;
		}
		//需要覆写的compareTo方法
		//这里我的思路是先比较age的大小,如果age相等,则
		//接着比较name的字典顺序大小
		@Override
		public int compareTo(Object o) {
			// TODO Auto-generated method stub
			Person per=(Person) o;//实现强制类型转换
			if(this.age>per.age){
				return 1;
			}else if(this.age<per.age){
				return -1;
			}else{//如果年纪相等就比较名字的字典顺序大小
				int temp=this.name.compareTo(per.name);
				return temp;
			}
			
			
		}
		
}


//自己写的Comparator接口
package com.wq.comparator;

import java.util.Comparator;

import com.wq.person.Person;

public class ComparatorByName implements Comparator {

	/**
	 * 覆写compare方法,生成一个比较器。这里我们用name来作为比较
	 */
	@Override
	public int compare(Object o1, Object o2) {
		// TODO Auto-generated method stub
		Person p1=(Person)o1;
		Person p2=(Person)o2;
		
		int temp=p1.getName().compareTo(p2.getName());//获取两个对象的name比较的结果
		return temp==0?p1.getAge()-p2.getAge():temp;
	}

}


//测试类。用于测试CompareTo()方法和Comparable接口
package com.wq.TreeSet;

import java.util.Iterator;
import java.util.TreeSet;

import com.wq.comparator.ComparatorByName;
import com.wq.person.Person;

public class testTreeSet {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		//测试实现Comparable接口的比较功能
		System.out.println("测试实现Comparable接口的比较功能");
		testComparableDemo();
		
		System.out.println("测试实现Comparator接口,作为一个比较器来进行比较的功能");
		//测试实现Comparator接口,作为一个比较器来进行比较的功能
		testComparatorDemo();
		
	}

	public static void testComparatorDemo() {
		TreeSet ts=new TreeSet(new ComparatorByName());//实例化对象,并且确定比较器
		//添加自定义对象
		//在添加元素时,自定义的对象必须实现Comparator接口,同时要覆盖compare方法
		//否则就会报错
		ts.add(new Person("zhangsan",22));
		ts.add(new Person("zhaoliu",24));
		ts.add(new Person("zhouqi",25));
		ts.add(new Person("wuba",28));
		ts.add(new Person("lisi",26));
		ts.add(new Person("wangwu",25));
		
		for(Iterator it=ts.iterator();it.hasNext();){
			Person per=(Person)it.next();
			System.out.println(per);
		}
	}

	public static void testComparableDemo() {
		TreeSet ts=new TreeSet();//实例化对象,
		//添加自定义对象
		//在添加元素时,自定义的对象必须实现Comparable接口,同时要覆盖compareTo方法
		//否则就会报错
		ts.add(new Person("zhangsan",22));
		ts.add(new Person("zhaoliu",24));
		ts.add(new Person("zhouqi",25));
		ts.add(new Person("wuba",28));
		ts.add(new Person("lisi",26));
		ts.add(new Person("wangwu",25));
		
		for(Iterator it=ts.iterator();it.hasNext();){
			Person per=(Person)it.next();
			System.out.println(per);
		}
	}

}

运行结果如下:

测试实现Comparable接口的比较功能(以年龄大小)

zhangsan....22

zhaoliu....24

wangwu....25

zhouqi....25

lisi....26

wuba....28

测试实现Comparator接口,作为一个比较器来进行比较的功能(以名字的字典顺序)

lisi....26

wangwu....25

wuba....28

zhangsan....22

zhaoliu....24

zhouqi....25


练习:TreeSet集合练习,字符串长度的排序

    分析:Java代码中。TreeSet默认的会为字符串进行字典顺序大小的排序,但这不符合我们的要求。这个时候怎么办,我们必须要用到比较器。即comparator接口来实现。

具体代码实现如下:

//自定义的比较器
package com.wq.comparator;

import java.util.Comparator;

import com.wq.person.Person;
/*
 * 生成一个比较器,用来比较字符串的长度大小,如果长度相等则比较字符的字典顺序大小
 */
public class ComparatorByLength implements Comparator {

	@Override
	public int compare(Object o1, Object o2) {
		// TODO Auto-generated method stub
		String str1=(String)o1;
		String str2=(String)o2;
		int temp=str1.length()-str2.length();
		return temp==0?str1.compareTo(str2):temp;
	}

}

//测试类
package com.wq.TreeSet;

import java.util.Iterator;
import java.util.TreeSet;

import com.wq.comparator.ComparatorByLength;
import com.wq.person.Person;

/**
 * 这个练习是对TreeSet集合的练习
 * 主要是进行字符串的长度的排序
 * @author LULEI
 *
 */
public class testTreeSetDemo {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		TreeSet ts=new TreeSet(new ComparatorByLength());//在构造函数中添加相应的构造器
		
		ts.add("asdfdf");
		ts.add("dds");
		ts.add("fghf");
		ts.add("as");
		ts.add("t");
		ts.add("fdsfsg");
		
		for(Iterator it=ts.iterator();it.hasNext();){
			Object o=(Object)it.next();
			System.out.println(o);
		}
	}

}

运行结果如下:

t

as

dds

fghf

asdfdf

fdsfsg

--------------------------------------------------------------------*--------------------------------------------------------------------------------------------------

我们已经介绍了List和Set接口,接下来总结一下。

总结:List:       1---ArrayList     2---LinkedList

            Set         1---HashSet    --LinkedHashSet

                           2---TreeSet

注意,后缀名就是该集合所属的体系,前缀名就是该集合的数据结构。

    1、 看到array:就要想到数组,就要想到查询快,有角标。如果不希望出现重复的元素则需要覆盖equals()方法。

     2、看到link就要想到链表,就要想到增删快,就要想到add/get/remove+first(last)的方法。

     3、看到hash就要想到哈希表,就要想到唯一性,就要想到元素需要覆盖hashCode()和equals()方法。

     4、看到tree就要想到二叉树,就要想到排序,就要想到两个接口:comparator和comparable。

 并且通常这些常用的容器都是不同步的。

你可能感兴趣的:(java)