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 }

     输出结果:

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

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

  • 相关阅读:
    leetcode教程系列——Binary Tree
    《Ranked List Loss for Deep Metric Learning》CVPR 2019
    《Domain Agnostic Learning with Disentangled Representations》ICML 2019
    Pytorch从0开始实现YOLO V3指南 part5——设计输入和输出的流程
    Pytorch从0开始实现YOLO V3指南 part4——置信度阈值和非极大值抑制
    Pytorch从0开始实现YOLO V3指南 part3——实现网络前向传播
    Pytorch从0开始实现YOLO V3指南 part2——搭建网络结构层
    Pytorch从0开始实现YOLO V3指南 part1——理解YOLO的工作
    让我佩服的人生 文章
    win8.1配置cordova+ionic等一系列东西
  • 原文地址:https://www.cnblogs.com/shide/p/2982755.html
Copyright © 2011-2022 走看看