zoukankan      html  css  js  c++  java
  • 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 }

     输出结果:

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

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

  • 相关阅读:
    Node爬虫之初体验
    Angular中ui-router实现路由嵌套案例
    Angular路由与多视图综合案例
    面试一周10多家公司面试问题总结 ,react相关的并没多少
    单页面应用和多页面应用
    dangerouslySetInnerHTML 正常解析后台返回的html
    文件上传服务器跨域问题
    回流的触发方式
    antd 树的递归 循环展示部门组织架构
    日常杂项记录:(jquery弹出层插件、js判断是pc还是移动端)
  • 原文地址:https://www.cnblogs.com/shide/p/2982755.html
Copyright © 2011-2022 走看看