Java学习之容器上(Collection接口常用方法,Iterator接口,使用foreach循环遍历Collection集合元素,Set集合通用知识(Hashset类,hashcode()与LinkedHashSet类))

1.容器API的类图结构如下:

 

 

JAVA的集合类是一种特别有用的工具类,它可以用于存储数量不等的多个对象,并可以实现常用数据结构,如栈,队列等,除此之外,JAVA集合还可用于保存具有映射关系的关联数组。

 

JAVA的集合大致上可分为:Set,List和Map三种体系,其中Set代表无序,不可重复的集合;List代表有序,重复的集合,而Map则代表具有遇敌关系的集合。Queue体系集合,代表一种队列集合实现。

 

JAVA集合概述:

 

 JAVA提供集合类主要负责保存盛装其他数据,因此集合类也被称为容器类。所有集合类都位于java.util包下。

 

JAVA集合类主要由两个接口派生而出:Collection和Map,也是根接口。

Map保存的每项数据都是key-value对,也就是由key和value两个值组成。

 

下面给个比喻,再给个图就非常容易理解了。

 

JAVA的所有集合分成三大类,其中Set 集合类似于一个罐子,把一个对象添加到Set集合时,Set集合无法记住添加这个元素的顺序,所以Set里的元素不能重复(否则系统无法准确识别这个元素)

 

List集合非常像一个数组,它可以记住每次添加元素的顺序,只是List长度可变。

 

Map集合也像一个罐子,只是它里面的每项数据都由两个值组成。

如果访问List集合中的元素,可以直接根据元素的索引来访问,如果需要访问Map集合中的元素,可以根据每项元素的key来访问其value,如果希望访问Set集合中的元素,则只能根据元素本身来访问(这也是Set集合里元素不允许重复的原因)

 

对于Set,List和Map三种集合,最常用的实现类,分别是HashSet,ArrayList  ,,HashMap  三个实现类。

 

1.1 Collection接口:

Collection接口——定义了存取一组对象的方法,其子接口Set和List分别定义了存储方式。

(1)Set中的数据对象没有顺序且不可以重复。

(2)List中的数据对象有顺序且可以重复。

Collection接口所定义的方法:

方法

描述

Boolean add(Object o)

向集合中添加一个对象的引用

Void clear()

删除集合中的所有对象,即不再持有这些对象的引用

Boolean contains(Object o)

判断在集合中是否持有特定对象的引用

Boolean isEmpty()

判断集合是否为空

Iterator iterator()

返回一个Iterator对象,可用它来遍历集合中的元素

Boolean remove(Object o)

从集合中删除一个对象的引用

 Boolean retainAll(Collection<?> c)

保留集合中的指定内容 

Int size()

返回集合中元素的数目

Object[] toArray()

返回一个数组,该数组包含集合中的所有元素

 

Boolean equals(Object o)

对象比较

Int hashCode() 

返回hash码

 

 注意:相等的对象应该具有相等的 hash codes。容器类对象在调用remove,contains等方法时需要比较对象是否相等,这回涉及到对象类型的equals方法和hasCode方法;对于自定义的类型,需要重写equals和hashCode方法以实现自定义的对象相等规则。

 Set接口和list接口都继承了Collection接口,而map接口没有继承Collection接口,因此可以对set对象和list对象调用以上方法,但是不能对Map对象调用以上方法。

Collection接口的iterator()和toArray()方法都用于获取集合中的所有元素,前者返回一个Iterator对象,后者放回一个包含集合中所有元素的数组。

 

增加Name类的equals和hashCode方法如下:

1 public boolean equals(Object obj){
2     if(obj instanceof Name){
3         Name name=(Name)obj;    return(firstName.equals(name.firstName)&&lastName.equals(name.lastName));
4 } 5 return super.equals(obj); 6 } 7 public int hashCode(){ 8 return firstName.hashCode(); 9 }

 

方法应用例子:

 1 import java.util.ArrayList;
 2 import java.util.Collection;
 3 import java.util.HashSet;
 4 public class TestCollection{
 5     public static void main(String[] args){
 6         Collection c= new ArrayList();
 7         c.add("孙悟空");
 8         c.add(6);
 9         System.out.println("c集合的元素的个数为:"+c.size());
10         System.out.println("c集合里面是否包含孙悟空字符"+" "+c.contains("孙悟空"));
11         c.add("世界你好");
12         System.out.println("c集合里面的元素:"+c);
13         Collection books=new HashSet(); //HashSet不允许元素重复。
14         books.add("世界你好");
15         books.add("你好世界");
16         System.out.println("c集合里面是否完全包含books:"+c.contains(books));
17         c.removeAll(books);
18         System.out.println("c集合中的元素:"+c);
19         c.clear();
20         System.out.println("c集合里面的元素:"+c);
21         books.retainAll(c);
22         System.out.println("books集合的元素:"+books);
23         
24     }
25 }

 

 输出结果:

c集合的元素的个数为:2
c集合里面是否包含孙悟空字符 true
c集合里面的元素:[孙悟空, 6, 世界你好]
c集合里面是否完全包含books:false
c集合中的元素:[孙悟空, 6]
c集合里面的元素:[]
books集合的元素:[]

 

 1.2 Iterator接口:

使用Iterator接口遍历集合元素,Iterator接口是JAVA集合框架的成员,也被称为迭代器。

Iterator接口中声明了如下方法:

 boolean hashNext():如果迭代的集合元素还没被遍历,则返回true

Object next():返回集合里下一个元素。

Void remove():删除集合里上一次next方法返回的元素。

 

 1 import java.util.Collection;
 2 import java.util.HashSet;
 3 import java.util.Iterator;
 4 public class TextIterator {
 5     public static void main(String[] args){
 6     Collection books=new HashSet();            //无序序列,元素不可重复
 7     books.add("世界你好");
 8     books.add("你好世界");
 9     books.add("你好Java");
10     System.out.println("现在结合的元素:"+books);
11     Iterator it=books.iterator();        //获取books集合对应的迭代器。
12     while(it.hasNext()){
13         String book=(String)it.next();    //it.next()方法返回的数据类型是object类型,需要强制类型转换。
14         System.out.println(book);
15         if(book.equals("你好世界")){
16             it.remove();                //从集合中删除上一次next方法返回的元素。
17         }
18         book="测试字符串";                    //对book变量赋值,不会改变集合元素本身。
19     }
20     System.out.println(books);
21     
22     
23     }

 

 

 输出结果:

现在结合的元素:[世界你好, 你好Java, 你好世界]
世界你好
你好Java
你好世界
[世界你好, 你好Java]

Iterator仅用于遍历集合,Iterator本身并不提供盛装对象的能力,如果需要创建Iterator对象,则必须有一个被迭代的集合。

Iterator必须依附于Collection对象,有一个Iterator对象,则必然有一个怀之关联的Collection对象。Iterator提供了2个方法来迭代访问Collection集合里的元素。

结论:当使用Iterator对集合元素进行迭代时,Iterator并不是把集合元素本身传给了迭代变量,而是把集合元素的值传给了迭代变量,所以修改迭代变量的值对集合元素本身没有任何改变。

 

1.3使用foreach循环遍历集合元素

我们知道,除了可以使用Iterator类迭代访问Collection集合里的元素之外,现在还可以使用foreach循环来迭代访问集合元素会更加便捷。

foreach的语句格式:
for(元素类型t 元素变量x : 遍历对象obj){
     引用了x的java语句;
}

 

 1 import java.util.Collection;
 2 import java.util.HashSet;
 3 
 4 public class TextForeach {
 5     public static void main(String[] args){
 6         Collection books=new HashSet();
 7         books.add("世界您好");
 8         books.add("您好世界");
 9         books.add("您好JAVA");
10         for(Object obj:books){
11             String book=(String)obj;            //此处的book变量也不是集合元素本身
12             System.out.println(book);
13             if(book.equals("您好世界")) {
14                 books.remove(book);
15             }
16 
17          }
18 
19           System.out.println(books);
20 
21     }        

 

输出结果:

现在结合的元素:[世界你好, 你好Java, 你好世界]
世界你好
你好Java
你好世界
[世界你好, 你好Java]

 

1.4 Set集合的通用知识:

1.4.1HashSet类:

HashSet是Set接口的典型实现,大多数时候使用Set集合时就是使用这个实现类。HashSet按Hash算法一存储集合中的元素,因此具有很好的存取和查找性能。

特点:

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

》HashSet来是同步的,如果多个线程同时访问一个Set集合,如果多个线程同时访问一个HashSet,如果有2条或者2条以上线程同时修改了HashSet集合,必须通过代码来保证其同步。

》集合元素值可以是Null

我们可以简单的认为:HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode()方法返回值也相等。

 

 1 import java.util.*;
 2 class A{                    //类A的equals方法总是返回true,但没有重写其hashCode()方法
 3     public boolean equals(Object obj){
 4         return true;
 5     }
 6 }
 7 class B{                    //类B的hashCode()方法总是返回l,但没有重写equals()方法
 8     public int hashCode(){
 9         return 1;
10      }
11 }
12 class C{                    //类C的hashCode()方法总是返回2,重写其equals()方法
13     public int hashCode(){
14         return 2;
15     }
16      public boolean equals(Object obj){
17          return true;
18      }
19  }
20 public class TextHashset{
21         public static void main(String[] args){
22         HashSet books = new HashSet();
23         books.add(new A());
24         books.add(new A());
25         books.add(new B());
26         books.add(new B());
27         books.add(new C());
28         books.add(new C());
29 30         }
31 }

 

 

我们从编译结果可以看出,即使2个A对象通过equals比较返回true,但HashSet依然把它们当成2个,即使2个B对象的hashCode()返回相同值(都是1)但,HashSet依然把它们当成2个对象。

 

简单的提醒点:

如果需要某个类的对象保存到HashSet集合中,重写这个类的equals()方法和hashCode()方法时,应该尽量保证两个对象通过equals比较返回true时,它们的hashCode方法返回值也相等。

HashSet采用每个元素的hashCode作为其索引,从而可以自由增加HashSet的长度,并可以根据元素的hashCode值来访问该元素。

(我们可以理解为相当于数组里的下标索引)。

 

因为hashCode()方法很重要,看看重写hashCode()方法的基本规则。

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

》对象中用作equals比较标准的属性,都应该用来计算hashCode值。

 

 

 

 1 import java.util.Iterator;
 2 
 3 class R{
 4     int count;
 5     public R(int count){
 6         this.count=count;
 7     }
 8     public String toString(){
 9         return "R[count"+count+"]";
10     }
11     public boolean equals(Object obj){
12         if(obj instanceof R){
13             R f=(R)obj;
14             if(f.count==this.count){
15                 return true;
16             }
17         }
18         return false;
19     }
20     public int hashCode(){
21         return this.count;
22     }
23 }
24 public class TestHashSet2{
25     public static void main(String[] args){
26         HashSet hs=new HashSet();
27         hs.add(new R(5));
28         hs.add(new R(-3));
29         hs.add(new R(9));
30         hs.add(new R(-2));
31         System.out.println("第1个hs,集合中的元素:"+hs+"\n");            //打印HashSet集合,集合元素没有重复
32         Iterator it=hs.iterator();
33         R first = (R)it.next();
34         first.count = -3;                                        //为第一个元素的count实例变量赋值
35         System.out.println("第2个hs,集合中的元素:"+hs+"\n");
36         hs.remove(new R(-3));                                    //删除count为-3的R对象
37         System.out.println("第3个hs,集合中的元素:"+hs+"\n");            
38         System.out.println("hs是否包含count为-3的R对象?"+hs.contains(new R(-3)));        //输出false        
39           System.out.println("hs是否包含count为5的R对象"+hs.contains(new R(5)));         //输出false    
40     }
41 }

 

 

输出结果:

第1个hs,集合中的元素:[R[count5], R[count9], R[count-3], R[count-2]]

第2个hs,集合中的元素:[R[count-3], R[count9], R[count-3], R[count-2]]

第3个hs,集合中的元素:[R[count-3], R[count9], R[count-2]]

hs是否包含count为-3的R对象?false
hs是否包含count为5的R对象false

 

ps:

(1)object类中的hashcode()方法比较的是对象的地址(引用地址),使用new方法创建对象,两次生成的当然是不同的对象,造成的结果就是两个对象的hashcode()返回的值不一样。所以hashset会把new方法创建的两个它们当作不同的对象对待,

(2)在java的集合中,判断两个对象是否相等的规则是: 
a),判断两个对象的hashCode是否相等 
      如果不相等,认为两个对象也不相等,完毕 
      如果相等,转入2) 
(这一点只是为了提高存储效率而要求的,其实理论上没有也可以,但如果没有,实际使用时效率会大大降低,所以我们这里将其做为必需的。) 
b),判断两个对象用equals运算是否相等 
      如果不相等,认为两个对象也不相等 
      如果相等,认为两个对象相等(equals()是判断两个对象是否相等的关键) 

 

LinkedHashSet类:

HashSet还有一个子类LinkedHashSet,它也是根据元素hashCode值来决定元素存储位置,它是需要维护元素的插入顺序,因此性能略低于HashSet的性能,但在迭代访问Set里的全部元素时将有很好的性能,因为它以链表来维护内部顺序。

 1 import java.util.LinkedHashSet;
 2 public class LinkedHashSetTest{
 3     public static void main(String[] args){
 4         LinkedHashSet  books = new LinkedHashSet();
 5         books.add("世界您好");
 6         books.add("您好世界");
 7         System.out.println(books);     
 8         books.remove("世界您好");            //删除世界您好   
 9         books.add("世界您好");            //重新添加世界您好
10         System.out.println(books);
11     }
12 }

 

 输出结果:

[世界您好, 您好世界]
[您好世界, 世界您好]

编译结果相信大家都理解,因为元素的顺序正好与添加顺序一致。

 

 

 

 

posted @ 2013-03-30 23:20  佬D  阅读(6227)  评论(0编辑  收藏  举报