Java——(三)Collection之Set集合、HashSet类

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

一、Set集合

  Set集合不允许包含相同的元素,如果试图把两个相同的元素加入同一个Set集合中,则添加

操作失败,add方法返回false,而新元素不会被加入。

  Set判断两对象相同不是使用==运算符,而是根据equals方法。也就是说,只要两个对象用

equals方法比较返回true,Ser就不会接受这两个对象;反之,只要两个对象用equals方法比较

返回false,SEt就会接受这两个对象(甚至这两个对象是同一个对象,Set也可把它们当成两个对

象出来)。下面是使用普通Set的示例程序。

 1 import java.util.HashSet;
 2 import java.util.Set;
 3
 4
 5 public class SetTest {
 6
 7     public static void main(String[] args) {
 8         Set names = new HashSet<>();
 9         //添加一个字符串对象
10         names.add(new String("暨雪"));
11         //再次添加一个字符串对象
12         //因为两个字符串对象通过equals方法比较相等
13         //所以添加失败,返回false
14         boolean reasult = names.add(new String("暨雪"));
15         //从下面输出看到集合只有一个元素
16         System.out.println(reasult + "-->" + names);
17     }
18
19 }

运行结果:

false-->[暨雪]

  从上面程序中可以看出,names集合两次添加的字符串对象明显不是同一对象(因为两次都调

用了new关键字来创建字符串对象),这两个字符串对象使用==运算符判断肯定返回false,但通过

equals方法比较将返回true,所以添加失败。最后结果输出只有一个元素。

1.HashSet类

HashSet是Set接口的典型实现,大多数时候使用Set集合是就是使用这个实现类。HashSet按Hash

算法来存储集合中的元素,因此具有很好的存取和查找性能。

  HashSet具有以下特点:

  1)不能保证元素的排序顺序,顺序有可能发生变化。

  2)HashSet不是同步的,如果多线程同时访问一个HashSet,假设有两个或两个以上线程同时

修改了HashSet集合时,则必须通过代码来保证其同步。

  3)集合元素值可以是null。

  HashSet集合判断两个以上相等的标准是两个对象通过equals()方法比较相等,并且两个对象的

hashCode()方法返回值也相等。

下面程序分别提供了三个类A、B和C,它们分别重写了equals()、hashCode()两个方法的一个或全部

,通过此程序可以看到HashSet判断集合元素相同的标准。

 1 import java.util.HashSet;
 2
 3
 4 public class HashSetTest {
 5     //类A的equals方法总是返回true,但没有重写hashCode方法
 6     static class A {
 7         @Override
 8         public boolean equals(Object o) {
 9             return true;
10         }
11     }
12     //类B的hashCode总是返回1,但没有重写equals方法
13     static class B {
14         @Override
15         public int hashCode() {
16             return 1;
17         }
18     }
19     //类C重写了hashCode方法和equals方法
20     static class C {
21         @Override
22         public int hashCode() {
23             return 2;
24         }
25         @Override
26         public boolean equals(Object obj) {
27             return true;
28         }
29     }
30
31     public static void main(String[] args) {
32
33         HashSet names = new HashSet<>();
34         names.add(new A());
35         names.add(new A());
36         names.add(new B());
37         names.add(new B());
38         names.add(new C());
39         names.add(new C());
40
41         System.out.println(names);
42     }
43
44 }

运行结果:

[[email protected], [email protected], [email protected], [email protected], [email protected]]

从上面程序可以看出,即使两个A对象通过equals()方法比较返回true,但HashSet依然把它们当成

两个对象;即使两个B对象的hashCode()返回相同值(都是1),但HashSet依然把它们当成同一个

对象。

注意:

 当把一个对象放入HashSet中时,如果需要重写该方法对应类的equals()方法,则也应该重写

hashCode()方法。其规则是:如果两个对象通过equals()方法比较返回true,这两个对象的hashCode

()值也应该相同。

  重写hashCode()方法的基本规则:

  1)在程序运行中,同一对象多次调用hashCode()方法应该返回相同的值。

  2)当两个对象通过equals()方法比较返回true时,这两个对象的hashCode()方法应返回相等的值。

  3)对象中用作equals()方法比较标准的Filed,都应该用计算hashCode值。

hashCode返回值的计算:

return f1.hashCode() + (int) f2;

为了避免直接相加产生偶然相等(两个对象f1、f2Field并不相等,但他们的和恰好相等),

可以通过为个Field乘以任意一个质数后在相加。

return f1.hashCode() * 17 + (int) f2 * 13;

  如果向HashSet中添加一个可变对象后,后面出现修改了该可变对象的Field,则可能导致

它与集合中的其他元素相同(即两个对象通过equals()方法比较返回true,两个对象的

hashCode()值也相等)。这就有可能导致HashSet中包含两个相同的对象。下面程序演示了这

种情况。

 1 import java.util.HashSet;
 2 import java.util.Iterator;
 3
 4 class R {
 5     int count;
 6     public R (int count){
 7         this.count = count;
 8     }
 9     @Override
10     public String toString() {
11
12         return "R[count:" + count + "]";
13     }
14     @Override
15     public boolean equals(Object obj) {
16
17         if (this == obj) {
18             return true;
19         }
20         if (obj != null && obj.getClass() == R.class) {
21             R r = (R) obj;
22             if (r.count == this.count) {
23                 return true;
24             }
25         }
26         return false;
27     }
28     @Override
29     public int hashCode() {
30
31         return this.count;
32     }
33 }
34 public class HashSetTest2 {
35
36     public static void main(String[] args) {
37
38         HashSet hs = new HashSet<>();
39         hs.add(new R(5));
40         hs.add(new R(-3));
41         hs.add(new R(9));
42         hs.add(new R(-2));
43         //打印HashSet集合,集合没有重复
44         System.out.println(hs);
45         //取出第一个元素
46         Iterator it = hs.iterator();
47         R first = (R) it.next();
48         //为第一个元素的count实例变量赋值
49         first.count = -3;
50         //再次输出HashSet集合,集合元素有重复元素
51         System.out.println(hs);
52         //删除count为-3的R对象
53         hs.remove(new R(-3));
54         System.out.println(hs);
55         System.out.println("hs是否包count为-3的R对象?" + hs.contains(new R(-3)));
56         System.out.println("hs是否包count为5的R对象?" + hs.contains(new R(5)));
57         System.out.println("hs是否包count为9的R对象?" + hs.contains(new R(9)));
58     }
59
60 }

运行结果:

[R[count:5], R[count:9], R[count:-3], R[count:-2]]
[R[count:-3], R[count:9], R[count:-3], R[count:-2]]
[R[count:-3], R[count:9], R[count:-2]]
hs是否包count为-3的R对象?false
hs是否包count为5的R对象?false
hs是否包count为9的R对象?true

  上面程序中的first.count = -3;因为改变了HashSet集合中第一个R对象的count实例变量

的值,这将导致该R对象与集合中的其他对象相同。当试图删除count = -3的R对象时,HashSet

会计算出该对象的hashCode值,从而找出该对象在集合中保存位置,然后把此处的对象与count

为-3的R对象通过equals()方法进行比较,如果相等则删除该对象——HashSet只有第三元素才

满足该条件(第一个元素实际上保存在count为5的R对象对应的位置),所以第三个元素被删除

。至于第一个count为-3的R对象,他保持在count为5的R对象对应的位置,但是用equals()方法

拿它和count为5的R对象比较时有返回false——这将导致HashSet不可能准确访问该元素。

  

时间: 2024-10-26 09:04:25

Java——(三)Collection之Set集合、HashSet类的相关文章

Java之Collection接口(集合)

集合概述 集合到底是什么呢?集合:集合是java中提供的一种容器,可以用来存储多个数据 集合和数组既然都是容器,它们有啥区别呢? 区别1: 数组的长度是固定的. 集合的长度是可变的. 区别2:  数组中存储的是同一类型的元素,可以存储基本数据类型值,也可以存储引用类型: 集合存储的都是对象.而且对象的类型可以不一致.在开发中一般当对象多的时候,使用集合进行存储. 学习集合的目标 会使用集合存储数据 会遍历集合,把数据取出来 掌握每种集合的特性 学习集合的方式 学习顶层:学习顶层接口或者抽象类中共

Java——(四)Collection之Set集合TreeSet类

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- TreeSet类 TreeSet是SortedSet接口的实现类,正如SortedSet名字所暗示的,TreeSet可以确保集合 元素处于排序状态.与HashSet集合相比,TreeSet还提供了如下几个额外的方法. 1)Comparator comparator(): 2)Object first(): 3)Object last(): 4)Object lower(Object o): 5

Java中Collection、Map常用实现类研究分析

接口/实现类 描述 key是否可为null 为null是否报错 key是否重复 key重复是否报错 key是否和添加一致 是否线程安全 List 一组元素的集合 ArrayList 基于数组存储,读取快 是 否 是 否 是 否 LinkedList 基于双向链表存储,插入快 是 否 是 否 是 否 Vector 基于数组存储,效率较ArrayList慢 是 否 是 否 是 是 Map HashMap 是 否 否 否 否 否 TreeMap 基于红黑树实现 否 是 否 否 否 否 Concurre

Java中Collection和Collections的区别

1.java.util.Collection 是一个集合接口(集合类的一个顶级接口).它提供了对集合对象进行基本操作的通用接口方法.Collection接口在Java 类库中有很多具体的实现.Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List与Set. Collection   ├List   │├LinkedList   │├ArrayList   │└Vector   │ └Stack   └Set 2.java.util.Collectio

java类库 collection与collections (转)

http://www.cnblogs.com/dashi/p/3597937.html Java中Collection和Collections的区别 1.java.util.Collection 是一个集合接口(集合类的一个顶级接口).它提供了对集合对象进行基本操作的通用接口方法.Collection接口在Java 类库中有很多具体的实现.Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List与Set. Collection   ├List   │├

Java中Collection和Collections的区别(引用自:http://www.cnblogs.com/dashi/p/3597937.html)

1.java.util.Collection 是一个集合接口(集合类的一个顶级接口).它提供了对集合对象进行基本操作的通用接口方法.Collection接口在Java 类库中有很多具体的实现.Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List与Set. Collection   ├List   │├LinkedList   │├ArrayList   │└Vector   │ └Stack   └Set 2.java.util.Collectio

Java——(五)Collection之List集合、ArrayList和Vector实现类

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 一.List集合 List集合代表一个元素有序.客重复的集合,集合中每个元素都有其对应的顺序索引.List 集合允许使用重复元素,可以通过索引来访问指定位置的集合元素.List集合默认按元素的添加 顺序设置元素的索引,例如第一次添加的元素索引为0,第二次添加的元素索引为1...... 1.List接口和ListIterator接口 List作为Collection接口的子接口,所以可以使用Co

数据结构-List接口-LinkedList类-Set接口-HashSet类-Collection总结

一.数据结构:4种--<需补充> 1.堆栈结构:     特点:LIFO(后进先出);栈的入口/出口都在顶端位置;压栈就是存元素/弹栈就是取元素;     代表类:Stack;     其它:main方法最后一个出去; 2.数组结构:     特点:一片连续的空间;有索引,查找快;增删慢;     代表类:ArrayList;     应用场景:用于查询多的场景,如天气预报; 3.队列结构:     特点:FIFO(先进先出);入口/出口在两侧;     代表:Queue接口     应用场景

Java集合-HashSet

HashSet类,是存在于java.util包中的类 .同时也被称为集合,该容器中只能存储不重复的对象.底层是由HashMap来存储的,因为HashSet不能重复,你知道HashMap的键不能重复就明白了这一个原理了,所以对于HashMap很熟悉的话对于HashSet就能够很快的知道底层实现.HashMap传送门(HashMap源码分析). HashSet概述 此类实现了Set接口,由哈希表(实际上是HashMap实例)支持. 对集合的迭代次序不作任何保证; 特别是不会保证这种顺序会持久不变(不