java集合----------Set

来源:互联网 发布:浏览器打开手机淘宝 编辑:程序博客网 时间:2024/06/06 03:21

集合只是实现了接口的实现类。

集合没有个数要求,可以存储不同类型的数据,但是只能存储引用类型。

集合的名称就是底层实现的数据结构+集合特点。

JDK1.5之前放入基本类型数据会报错,但1.5之后可以在add()方法中直接放入基本类型数据,如:

HashSet hs=new HashSet();

hs.add(1);此处将自动转换成对应的包装类:new Integer(1)

集合的方法:

HashSet hs=new HashSet();

System.out.println(hs.size());打印集合元素个数

遍历集合:

HashSet hs=new HashSet();

hs.add(1);

hs.add(2);

①循环foreach forin

for(Object obj:hs){

//此处注意不管当初往集合里方的数据是什么类型的,拿出来一定是Object类型的,如果要做别的对象只能强制转换

System.out.println(obj);

}

②迭代器方法

Iterator it=hs.iterator();//从卡车上取下小推车

//通过集合的iterator()方法获得一个迭代器,形象的说这是一个小推车,当获得这个迭代器时就获得了集合中所有数据的引用,而不是集合中的数据,所以可以对集合中的数据进行任意操作

while(it.hasNext()){

//相当于一个游标,在下一个对象的上方,判断下一个值是否存在,有则返回true,没有则返回false

System.out.println(it.next());

//取出下一个值

}

it.remove()清除下一个对象,只要此处清除了,那集合中也没有这个对象了。

Collection(单值集合)接口

子接口:Set

常规协定:

用equals比较返回true的两个对象各自调用自己的hashCode() 必须返回相同的数

1.HashSet(实现了set接口)

特点:无序、唯一

HashSet hs=new HashSet();

hs.add();

hs.add();

向集合中添加数据:

当向HashSet中存放对象时,会默认先调用HashCode()方法,如果比较不出来再调用“==”比较(即比较两个对象的内存地址,这是为了提高比较的效率),如果相等说明是一个对象如果不等,再调用equals()方法对两个对象的属性进行挨个比较,相等说名是相同对象,不等说明对象不重复。

HashSet的工作原理:




因为Object类的equals()方法比较的是内存地址而hashCode()方法返回的是对象的哈希码值,我如果不覆盖Object类的这两个方法,那么比较的还是内存地址,那么在开发中我我可能把两个属性完全相同的对象放到了集合中这造成了空间的浪费,和使用的不便;

所以我们想向集合中放引用类型时,像放基本类型一样,能够根据内存地址和内容来判断是不是一个对象如果是的话,则不再放入集合中,不是则放入。而equals()方法和hashCode()方法是程序员自己制定的,可以根据自己的规则来判断是否是一个对象。

一般覆盖equals()方法和hashCode()方法时如下所写:

@Override

public boolean equals(Object obj){

if(obj == null)return false;

if(!(obj instanceof Student)) return false;

//以上两句是为了增加程序的可读性,健壮性

if(obj == this) return true;

//这句是为了提高程序执行的效率

Student stu1 = this;

Student stu2 = (Student)obj;

return stu1.age == stu2.age && stu1.name.equals(stu2.name);

}

@Override

public int hashCode(){

/* 参与equals比较的是 String name int age */

return name.hashCode()[*31或17]+age;

//此处乘以一个较大的质数,来降低重码的概率


}


TreeSet(实现了SortedSet接口,SortedSet接口是Set的子接口)


底层采用的数据结构是树状结构(红黑树)。

TreeSet ts = new TreeSet();

ts.add(1);

ts.add(2):

基本数据类型的数据放入集合时,由于基本数据类型已经继承了Comparable接口,并重写了compareTo()方法,所以没有任何问题。

但当放入引用类型数据时,定义的类必须继承Comparable接口,并覆盖compareTo()方法,因为向TreeSet集合中放入数据时,会调用它的compareTo()方法,来进行比较,具体写法如下:

class Teacher implements Comparable{

@Override

public int compareTo(Object obj){

//此处注意,覆盖方法时参数类表必须相同,所以此处定义的参数是Object类型的

Teacher tea1 = this;//新来的

Teacher tea2 = (Teacher)obj;//集合当中已经存在的

return tea1.name.equals(tea2.name)?tea1.age - tea2.age:tea1.name.compareTo(tea2.name);

/*返回值为:

负数 代表当前元素小

0代表当前元素和老元素相同(为了保证唯一 舍弃

正数 代表当前元素大

*/

}

}

由于TreeSet底层采用树状结构,所以当新来对象调用compareTo()方法返回值为负数时,将新来对象放到节点的左子树上;返回值为正数时,放到节点的右子树上;返回值为0则是相同对象,不放入集合。此节点不是叶子节点时(即此节点还有子节点时),还要跟其子节点进行比较,确定对象存放到左子树还是右子树上。

而红黑树在进行比较时并不是总是从根节点进行比较,而是从最接近根节点的红色节点开始比较(就是从中间开始比较),这有利于提高比较效率,这在大多数情况下效率都是比较高的。

但在实际开发中会遇到这种情况,定义了一个类,但是这个类并没有继承Comparable接口,但是又不允许更改,这时只能写一个比较器类使集合能够遵循比较器定义的规范,来实现放入数据的有序和唯一

引用类型向集合中放入时实现有序唯一的另一种实现方式:

class Teacher {

String name;

int age;

public Teacher(String name,int age){

this.name = name;

this.age = age;

}

}

class TeacherNameComparator implements Comparator{ //比较器 比较规则

@Override

public int compare(Object o1,Object o2){

Teacher t1 = (Teacher)o1;//新来的元素

Teacher t2 = (Teacher)o2;//已有元素

return t1.name.compareTo(t2.name);

}

}

比较器可以写很多种,分别代表不同的规范和比较规则,但是集合使用时只能同时使用一种比较器,不能同时使用多种比较器;而且当同时使用Comparable和Comparator(比较器)时,以Comparator为准。

使用方式如下:

TeacherNameComparator tnc = new TeacherNameComparator();

把这杆秤放在集合入口,从而使每个要进入集合的元素,按照这杆秤的比较规则进行排序!作用:放进集合的Teacher类型就可以不用实现Comparable了!

TreeSet ts = new TreeSet(tac);

//注意此处只能放Comparator类型的变量,所以传参数应注意类型,所以前面定义的比较器类型必须继承Comparator接口


最后总结TreeSet比较机制如下:

第一个元素放入,什么比较都没有,直接放入,第二个元素放入 TreeSet会先判断一下自己(TreeSet)在构造的时候程序员有没有在构造方法当中提供一个Comparator

1.提供了 Comparator compare(1,2)

如:TreeSet ts = new TreeSet(new StuComparator());

调用比较器对象的compare(Object o1,Object o2)去比较新老对象 从而确定关系 比较器.compare(新来的,之前的)

a> 返回正数:新来的大 新来的走向右分支看是否需要再比

b> 返回0:代表新来的和之前的是"相同"对象 方法结束

c> 返回负数:新来的小 新来的向左分支看是否需要再比

2.没提供 则使用Comparable compareTo(1)

如:TreeSet ts = new TreeSet();

将会把新来的对象强制类型转换成为Comparable类型,并且调用它的compareTo()把之前存在于集合内的对象当作参数传入,从而确保能够比较出新老对象的大小关系 具体流程同上:

a> 返回正数:新来的大 新来的走向右分支看是否需要再比

b> 返回0:代表新来的和之前的是"相同"对象 方法结束

c> 返回负数:新来的小 新来的向左分支看是否需要再比



原创粉丝点击