zoukankan      html  css  js  c++  java
  • day14_集合框架1(ArrayList,LinkedList,HashSet)

    1.集合框架大致示意图:

    collections体系

    2.集合概述:

    /*
    数据 -封装在-> 对象 -存储在-> 集合
    集合类:
    1.为什么出现集合类?
     
         面向对象语言对事物的体现都是以对象的形式,所以为了方便对
         多个对象的操作,就对 对象进行存储,集合就是存储对象最常用的
         一种方式
    
    2.数组和集合类同是容器,有何不同?
          
          数组虽然也可以存储对象,但长度是固定的,只能存储同一类型对象
          集合长度是可变的.
          数组中可以存储基本数据类型,集合只能存储对象
    集合类的特点:
      集合只用于存储对象
      集合长度是可变的
      集合可以存储不同类型的对象
    关于数组:
     例如:
     Person p=new int[3];
     p[0]=new Person("zhangsan");
     ...
     
     为什么有这么多容器?(集合)
          因为每一个容器对数据的存储方式都有不同
          这个存储方式称为数据结构.
    
    */

    3.Collection中的一些方法:

    /*
    1.add方法的参数类型是Object,以便于接收任意类型的对象
    
    2.集合中存储的都是对象的地址(引用)
    */
    import java.util.*;
    class CollectionDemo
    {
     public static void sop(Object obj)
     {
      System.out.println(obj);
     
     }
     
     public static void base_method()
     {
      ArrayList a1=new ArrayList();
      a1.add("java1");
      a1.add("java2");
      a1.add("java3");
      a1.add("java4");
      ArrayList a2=new ArrayList();
      a2.add("java5");
      a2.add("java2");
      a2.add("java3");
      a2.add("java6");
      
      a1.retainAll(a2);
      sop("取交集后a1为: "+a1);//[java2,java3]
      sop("取交集后a2为: "+a2);//不变
      //a1中存放的是a1∩a2(如果为空集则a1为空)
       a1.removeAll(a2)  
      sop(“a1-a1∩a2: ”+a1);
      //a1中存放的是a1-a1∩a2
     }
    
    
     public static void main(String[] args)
     {
      
    
       base_method();
      //创建一个集合容器.使用Collection接口的子类(ArrayList) 
      ArrayList a1=new ArrayList();
      
     
      //1.添加元素.
      a1.add("java1");//添加字符串对象
      a1.add("java2");
      a1.add("java3");
      a1.add("java4");
      
      
      //2.获取个数,集合长度
      sop(a1.size()); 
      
      
      //打印集合
      sop(a1);//继承了AbstractCollection中的toString()方法
              //返回此 collection 的字符串表示形式,
              //通过 String.valueOf(Object) 可以将元素转换成字符串。 
              //并用"[]"括起来
      
      
      
      //3.删除元素
      a1.remove("java2");
      sop(a1);
      //a1.clear();//清空集合
    
      //4.判断元素
      sop("java3是否存在:"+a1.contains("java3"));
      sop("集合是否为空:"+a1.isEmpty());
    
      
     }
     
    
    }

    Collection中方法

    4.迭代器(Iterator):

    /*
    什么是迭代器?
     其实就是集合的取出元素的方式.
    
    把元素的取出方式定义在集合的内部(内部类)
    这样取出方式就可以直接访问集合内部的元素
    那么取出方式就被定义成了内部类.
    
    
    而每一个容器的数据结构不同,
    所以取出的动作细节(代码)也不一样.
    但是都有共性内容->判断和取出->进行向上抽取描述
    
    这些内部类都符合一个规则.该规则是Iterator.
    
    如何获取集合的取出对象呢?
     通过一个对外提供的方法iterator();
     (简单示意图)
     该方法是Collection接口继承Java.lang.Iterable接口中的iterator()方法
    */
    import java.util.*;
    class CollectionDemo3
    {
      public static void sop(Object obj)
      {
      
       System.out.println(obj);
      }
     
     
     //获取元素
      public static void method_get()
      {
       ArrayList a1=new ArrayList();
       a1.add("java1");
       a1.add("java2");
       a1.add("java3");
       a1.add("java4");
    
       Iterator it=a1.iterator();//返回一个Iterator接口的子类对象
       
       while(it.hasNext())
          sop(it.next());
       
       //另一种写法:迭代器对象随着for循环结束变成垃圾,节约内存.
       for(Iterator it2=a1.iterator();it2.hasNext(); )
        sop(it2.next());
      }
      
      
      
      public static void main(String[] args)
      {
       method_get();
      
      
      }
    
    }

    迭代器由来(获取元素)

    IteratorDemo

    5.List集合中的方法:

    /*
    Collection<-List:元素是有序的(存入和取出顺序一致,因为有角标的缘故),元素可以重复.因为该集合体系有索引.
              <-Set:元素是无序的(存入顺序和取出顺序不一定一致),元素不可以重复.
    List集合特有方法:
        凡是可以操作角标的方法都是该体系特有的方法
    
    增
     boolean add(index,element)
     boolean addAll(index,Collection)
    删
      remove(index)
    改
      set(index,element)
    查
      get(index)
      sublist(fromIndex,toIndex)
      listIterator()
      indexOf(Object o)
    
    
    List集合特有的迭代器:
    ListIterator是Iterator的子接口:可以实现在遍历过程中的对集合的增 删 改 查
    
    当对一个集合在迭代时,不可以通过集合对象的方法操作(增,删)该集合中的元素.
    因为会发生ConcurrentModificationException(并发修改异常)异常.
    
    所以,在迭代时,只能用迭代器的方法操作元素,
    可是Iterator的方法是有限的,
    只能对元素进行判断,取出,删除的操作
    如果想要其他的操作:添加,修改等,就需要使用其子接口:ListIterator
    
    该接口只能通过List集合的listIterator方法获取
    
    
    */
    import java.util.*;
    class ListDemo
    {
      
      public static void sop(Object obj)
      {
      
       System.out.println(obj);
      }
      //
      public static void method_add(ArrayList a1)
      {
        a1.add(1,"java增");
        sop("插入后: "+a1);
      
      }
      //
      public static void method_remove(ArrayList a1)
      {
        a1.remove(2);
        sop("删后: "+a1);
      
      }
      //
      public static void method_set(ArrayList a1)
      {
        a1.set(2,"java改");
        sop("改后: "+a1);
        
      }
      //
       public static void method_get(ArrayList a1)
      {
       sop("get(1): "+a1.get(1)); 
       
       //取出全部
       for(Iterator it=a1.iterator();it.hasNext();)
         sop(it.next());
       sop("\n");
       
       //或通过遍历
       for(int i=0;i<a1.size();++i)
         sop(a1.get(i));
    
      //通过indexOf获取对象位置
       sop("indexOf(2): "+a1.indexOf("java改"));
       sop("subList(1,3): "+a1.subList(1,3));
    
      }
    
      public static void main(String[] args)
      {
       ArrayList a1=new ArrayList();
           a1.add("java1");
           a1.add("java2");
           a1.add("java3");
           a1.add("java4");
        sop("原集合为: "+a1);   
        
        method_listIterator(a1);
        method_add(a1);
        method_remove(a1);
        method_set(a1);
        method_get(a1);
    
    
      }
      //ListIterator
      public static void method_listIterator(ArrayList a1)
      { 
        
         //在迭代过程中准备添加或者删除元素
         /*
        for(Iterator it=a1.iterator();it.hasNext();)
         {
          Object obj=it.next();
          if(obj.equals("java2"))
             //a1.add("java7");//集合在该位置添加,对迭代器来说还是认为有4个元素,不能确定取不取
                               //如果用集合进行删除操作,同理.
    
            //it.remove();//迭代器删除的是集合java2的引用    
          sop("obj="+obj);   
         }
         */
      /*
        会报并发修改(迭代器和集合操作)异常.
        解决:
        要么使用集合方法修改,
        要么使用迭代器方法修改.
       */
      
       ListIterator li=a1.listIterator();
       //sop("nextIndex: "+li.nextIndex());//0
      //sop("previousIndex: "+li.previousIndex());//-1
        while(li.hasNext())
        {
          Object obj=li.next();
          if(obj.equals("java2"))
             li.add("java7");
            //li.set("java8");//
        } 
        sop("li.add(\"java7\"): "+a1);
         
        //sop("nextIndex: "+li.nextIndex());//5
        //sop("previousIndex: "+li.previousIndex());//4
        //逆向遍历
        while(li.hasPrevious())
           sop(li.previous());
        
        //sop("nextIndex: "+li.nextIndex());//0
        //sop("previousIndex: "+li.previousIndex());//-1
       
      }
    
    }
    /*
    hasNext(),next()与hasPrevious(),previous
    public boolean hasNext()
    {
      return cursor!=size;
    }
    public boolean hasPrevious()
    {
      return cursor!=0;
    }
    cursor=0;
                  0 1 2
    next()        0 1 2 3 //返回当前元素(return cursor),指向下一个(cursor++)[0,1 1,2 2,3]
    
    previous()    0 1 2 //返回前一个(return cursor-1),指向前一个(cursor)[]
    
    public int nextIndex()
    {
       return cursor;
    }
    
    public int previousIndex() 
    {
       return cursor - 1;
    }
    
    */

    ListDemo

    6.ArrayList与Vector

    /*
    List<-ArrayList:底层的数据结构使用的是数组结构
                    :查询更改很快,但是增删稍慢
                    :线程不同步
                    (线性表...)
        <-LinkedList:底层的数据结构使用的是链表结构
                   (单链表,双向链表,循环链表...)
                  :增删速度很快,查询稍慢,线程不同步
        <-Vector:底层是数组数据结构.
                 :线程同步.被ArrayList替代了.
    可变长度数组:
    ArrayList:
    public ArrayList()构造一个初始容量为 10 的空列表。 
    >10 new一个新的数组长度为10*(1+0.5)=15
        把原来数组中的元素拷贝到新数组,把新元素添加到末尾元素后.
    
    Vector:则每次需要增大容量时,容量将增大一倍->比较浪费空间
    
    */

    7.Vector集合简单示例:

    import java.util.*;
    /*
     枚举就是Vector特有的取出方式.
     发现枚举(Enumeration<E>)和迭代器很像
     其实枚举和迭代器是一样的.
     
     因为枚举的名称以及方法名称都过长
     所以被迭代器取代了
     枚举郁郁而终了
    */
    class VectorDemo
    {
        public static void main(String[] args)
        {
            Vector v=new Vector();
            v.add("java1"); 
            v.add("java2"); 
            v.add("java3"); 
            v.add("java4"); 
            Enumeration en=v.elements();//返回Enumeration<E>是一个接口
            while(en.hasMoreElements())//使用的Enumeration中的方法
            {
             System.out.println(en.nextElement());
            
            }
        
        }
    }

    VectorDemo

    8.LinkedList集合:

    import java.util.*;
    /*
    LinkedList特有方法:
     addFirst();//头插法建
     addLast();//尾插法建表
    
     getFirst();
     getLast();
     获取元素,但不删除元素
    
     removeFirst();
     removeLast();
     既取还删,并且返回被删元素
     如果列表为空抛出NoSuchElementException 
    
     
     
     
     
     在JDK1.6出现替代方法
     offerFirst();
     offerLast();
     插入指定元素
     
     peekFirst();
     peekLast();
     获取但不移除此列表的元素;如果此列表为空,则返回 null。 
     
     pollFirst()
     pollLast();
    获取并移除此列表的元素;如果此列表为空,则返回 null。 
    
    */
    class LinkedListDemo
    {
     public static void sop(Object obj)
     {
      System.out.println(obj);
     
     }
     
    
     public static void main(String[] args)
     {
      LinkedList link=new LinkedList();
      link.addFirst("java1");
      link.addFirst("java2");
      link.addLast("java3");
      link.addFirst("java4");
      sop(link);
      sop(link.removeFirst());
      sop("size="+link.size());
      sop("\n");
      while(!link.isEmpty())
        sop(link.removeFirst());//取一个删一个
    
     }
    
    
    
    }

    LinkedListDemo

    9.利用LinkedList集合模拟栈和队列:

    /*
     使用LinkedList模拟一个堆栈或者队列数据结构
    
    堆栈:后进先出 如同一个杯子
    队列:先进先出 如同一个水管
    
    
    
    */
    import java.util.*;
    
    /*
    为什么要封装?
     LinkedList只有自身含义
     要做成跟项目相关的名称
     用自定义名称显得更清晰
    */
    class QueueList
    {
      private LinkedList link;
      
      QueueList()
      {
       link=new LinkedList();
      
      }
      public void queueAdd(Object obj)
      {
       link.addFirst(obj);
      
      }
      public Object queueGet()
      {
        
          return link.removeLast();
         //return link.removeFirst();栈
      }
      public boolean isNull()
      {
          return link.isEmpty();
      }
    
    }
    class LinkedListTest
    {
        public static void main(String[] args)
        {
           QueueList q=new QueueList();
           q.queueAdd("aa");
           q.queueAdd("cc");
           q.queueAdd("ee");
           q.queueAdd("ff");
          while(!q.isNull())
            System.out.println(q.queueGet());
        }
    }
    /*
    比较频繁增删操作:LinkedList
    增删同时有查询:ArrayList
    
    
    
    */

    LinkedListTest

    10.ArrayList集合:去除集合中重复元素

    1.存储字符串对象:

    /*
    去除ArrayList集合中重复的元素
    
    */
    /*
    算法思想:
    ①新建一个容器
    ②把旧容器中的元素放到新容器中,并且每次放入均进行判断
      如果出现相同元素则不再放入.
    */
    import java.util.*;
    class ArrayListTest
    {
        public static ArrayList removeRepeat(ArrayList al)
        {
          //方法一:集合操作
          /*
          ArrayList as=new ArrayList();
          for(int i=0;i<al.size();++i)
            if(!as.contains(al.get(i)))
              as.add(al.get(i));
         */
          
          //方法二:迭代器操作 
          ArrayList as=new ArrayList();
          Object obj;
          ListIterator li=al.listIterator();
          while(li.hasNext())
           if(!as.contains(obj=li.next()))
              as.add(obj);
          
          return as;
    
        }
        
        public static void main(String[] args)
        {
         ArrayList al=new ArrayList();
         al.add("aa");
         al.add("bb");
         al.add("aa");
         al.add("aa");
         al.add("cc");
         al.add("bb");
        System.out.println(al);
        System.out.println(removeRepeat(al));
        }
    }
    /*
    总结:
      ①尽量不要next和previous同时用
      这样很容易被指针的指向搞糊涂.
      它们两个都会改变cursor.
      ②尽量不要使用多个next,取一次判断一次
    */

    2.存储自定义对象:

    /*
    将自定义对象作为元素存到ArrayList集合中,并去除重复元素
    
    比如:存人对象.同姓名同年龄,视为同一个人,为同一个人.
    */
    /*
    思想:
    ①定义人 类,将数据封装人对象.并且复写掉Object中的equals方法
    ②调用contains进行比较,筛选出不同的元素
    */
    import java.util.*;
    class Person
    {
        private String name;
        private int age;
        
        Person(String name,int age)
        {
           this.name=name;
           this.age=age;
        }
        
        public boolean equals(Object obj)
        {
            if(!(obj instanceof Person))
              return false;//不同的对象
            
            Person p=(Person)obj;
            return (this.name==p.name)&&(this.age==p.age);
                   //this.name.equals(p.name) 用String类的equals方法
                   
        }
    
        public void printPerson()
        {
         System.out.println("name:"+name+",age:"+age);
        }
       //以上也可以通过定义两个方法(getName,getAge)获取姓名和年龄
    }
    class ArrayListTest2
    {
        public static ArrayList singlePerson(ArrayList ai)
        {
          ArrayList as=new ArrayList();
          ListIterator lt = ai.listIterator();
          while(lt.hasNext())
          {
            Object obj;
           if(!as.contains(obj=lt.next()))
             as.add(obj);
          }
          return as;
        }
        public static void main(String[] args)
        {
          ArrayList ai=new ArrayList();
          ai.add(new Person("zhang",12));
          ai.add(new Person("li",12));
          ai.add(new Person("wang",13));
          ai.add(new Person("zhang",12));
          ai.add(new Person("zhang",12));
          ai.add(new Person("wang",13));
          for(int i=0;i<ai.size();++i)
           ((Person)(ai.get(i))).printPerson();
         
          System.out.println();
        
          ai=singlePerson(ai);
          for(int i=0;i<ai.size();++i)
          ((Person)(ai.get(i))).printPerson();
         
        }
    
    }
    /*
    contains 方法:
    //ArrayList类
    public boolean contains(Object o) {
            return indexOf(o) >= 0;
        }
     public int indexOf(Object o) {
            if (o == null) {
                for (int i = 0; i < size; i++)
                    if (elementData[i]==null)
                        return i;
            } else {
                for (int i = 0; i < size; i++)
                    if (o.equals(elementData[i]))//在集合中,AbstractList中复写了equals
                                                 //其实比较的是集合中对象的地址
                        return i;
            }
            return -1;
        }
    
    public boolean equals(Object obj)
        {
            if(!(obj instanceof Person))
              return false;//不同的对象
            
            Person p=(Person)obj;
            System.out.println(this.name+"..."+p.age);
            return (this.name==p.name)&&(this.age==p.age);
                   //this.name.equals(p.name) 用String类的equals方法
                   //用这种方法更好点,因为字符串常量也是一个对象
                   //用==也可以,因为比较的是对象中的name
        }
    
    remove方法也是调用equals方法把要删除的对象与集合中的对象逐一比较
    */
    /*
     List集合判断元素是否相同,依据的是equals方法.
    
    */
    ArrayList中自定义对象去除重复

    11.HashSet集合:

    /*
    set:元素是无序的(存入和取出的顺序不一定一致),元素不可以重复.
       <-hashSet:底层的数据结构是哈希表   
       
    Set集合的功能和Collection是一致的
    */
    import java.util.*;
    class HashSetDemo
    {
        public static void main(String[] args)
        {
         String s1=new String("ab");
         String s2=new String("ab");
          HashSet hs=new HashSet();
          hs.add(new HashSetDemo());
          hs.add(new HashSetDemo());
          hs.add(new String("java"));
          hs.add(new String("java"));
          System.out.println(hs);
          System.out.println(s1.hashCode()+" "+s2.hashCode());// 内存地址不同
                                                              //但是其哈希地址相同,String复写了hashCode
          /*
          在hs集合中由哈希函数决定在哈希表中的存储顺序,
          造成存的顺序和取的顺序不一致
          而List集合中的元素存的顺序和取的顺序一致
          因为每一个元素都有一个索引(角标)
          */
        }
    }

    HashSetDemo

    12.HashSet集合如何保证元素唯一性?

    /*
    用HashSet存储自定义对象(以人为例)
    
    
    HashSet是如何保证元素唯一性呢?
      是通过元素的两个方法,hashCode和equals来完成
       如果元素的HashCode值相同,会再调用equals与其具有相同hashCode的元素逐一比较,如果为true则认为相同元素,反之不同元素.
      如果元素的hashCode()值不同,不在调用equals进行比较,即说明是不同元素. 
    
    */
    /*
     通常自定义对象时,要复写equals和hashCode
     因为可能存到HashSet集合中
    */
    import java.util.*;
    class Person
    {
        private String name;
        private int age;
        
        Person(String name,int age)
        {
           this.name=name;
           this.age=age;
        }
        //复写hashCode(),使其返回一个定值
        /*
        public int hashCode()
        {
         System.out.println(this.name+"....hashCode()....");
          return 60;
        }
        */
        //重新复写hashCode()
        public int hashCode()//注意返回值为int,注意复写时返回值不要超出int范围
        {
          System.out.println(this.name+"....hashCode()....");
          return name.hashCode()+age*37;//如果不让age*37,不同对象可能出现40+20和30+30,相同概率很高,为了尽量保证hashCode唯一
          //利用String类中的hashCode()方法
        }
        public boolean equals(Object obj)
        {
            if(!(obj instanceof Person))
              return false;//不同的对象
           
            Person p=(Person)obj;
             System.out.println(this.name+"...equals..."+p.name);
            return (this.name==p.name)&&(this.age==p.age);
                   //this.name.equals(p.name) 用String类的equals方法
                   
        }
      
        public void printPerson()
        {
         System.out.println("name:"+name+",age:"+age);
        }
     
    }
    class HashSetDemo2
    {
    
        
        public static void main(String[] args)
        {
          HashSet hs=new HashSet();
          hs.add(new Person("zhang",11));
          hs.add(new Person("li",11));
          hs.add(new Person("zhang",11));
          hs.add(new Person("wang",12));
          Iterator it=hs.iterator();
          while(it.hasNext())
            ((Person)it.next()).printPerson();
        
        }
    
    }
    /*
    以下为分析过程:
    1.未复写hashCode()和equals
      会打印出四个值,并且有相同的.
      这是因为在存入hs集合时,会首先调用hashCode方法,比较哈希值,即是Object的hashCode方法
      Object中的hashCode有个特点:
        实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数.
      也就是说以上为四个不同对象->hashCode值一定不同->因此都存入hs中
    
    2.只复写了equals
      在对象存入hs中的时候,并没有调用equals,这是因为hashCode已经不同,无需调用
    
    3.当再把hashCode()复写返回一个定值
      ①new Person("zhang",11)会首先调用hashCode,此时集合中没有元素,存入集合
      ②new Person("li",11)先调用hashCode->与zhang,11相同
        ->调用equals方法与zhang,11比较->return false->存入
      ③new Person("zhang",11)调用hashCode->与zhang,11和li,11都相同
        ->调用equals方法和zhang,11比较->return true->不再比较并且不存入hs
      ④new Person("wang",12)同理与以上已存入集合的两个比较->最终存入hs
     这时候,发现避免了重复的存入,但是让hashCode返回一个值,这样每个对象都必须进行
     两次比较->没有必要(内存地址不同,无需再equals)
    
    4.重新复写hashCode()
    
    */
    
    /*
    了解下String类中的hashCode()
    
     private int hash;
     
     public int hashCode() {
            int h = hash;
            if (h == 0 && value.length > 0) {
                char val[] = value;
    
                for (int i = 0; i < value.length; i++) {
                    h = 31 * h + val[i];
                }
                hash = h;
            }
            return h;
        }
    
    */

    3.

    hashCode()相同

    4.

    HashCode不同

                     一个非常好的总结关于Equals和HashCode:http://www.iteye.com/topic/257191

    13.HashSet中的remove和contains方法:

    import java.util.*;
    class Person
    {
        private String name;
        private int age;
        
        Person(String name,int age)
        {
           this.name=name;
           this.age=age;
        }
        
        public int hashCode()//注意返回值为int,注意复写时返回值不要超出int范围
        {
          System.out.println(this.name+"....hashCode()....");
          return name.hashCode()+age*37;
        }
        public boolean equals(Object obj)
        {
            if(!(obj instanceof Person))
              return false;//不同的对象
           
            Person p=(Person)obj;
             System.out.println(this.name+"...equals..."+p.name);
            return (this.name==p.name)&&(this.age==p.age);
                   //this.name.equals(p.name) 用String类的equals方法
                   
        }
      
        public void printPerson()
        {
         System.out.println("name:"+name+",age:"+age);
        }
     
    }
    class HashSetDemo2
    {
    
        
        public static void main(String[] args)
        {
          HashSet hs=new HashSet();
          hs.add(new Person("zhang",11));
          hs.add(new Person("li",11));
          hs.add(new Person("wang",12));
          Iterator it=hs.iterator();
          while(it.hasNext())
            ((Person)it.next()).printPerson();
       
          System.out.println(hs.contains(new Person("zhang",11)));//true,remove同理
          //以上,hashCode()与集合中zhang,11相同->在调用equals->依然相同->存在(true)
          
          
          /*
          注意: 对于判断元素是否存在,以及删除等操作,依据的是元素的hashCode方法和equals方法
          
          而List集合判断元素是否相同,以及删除等操作,依据的是equals方法.
    
          */
        }
    
    }

    HashSet_remove,contains

  • 相关阅读:
    平稳退化,JS和HTML标记分离,极致性能的JavaScript图片库
    简单选择排序(Simple Selection Sort)的C语言实现
    堆排序(Heap Sort)的C语言实现
    快速排序(Quick Sort)的C语言实现
    希尔排序(Shell's Sort)的C语言实现
    2-路插入排序(2-way Insertion Sort)的C语言实现
    折半插入排序(Binary Insertion Sort)的C语言实现
    直接插入排序(Straight Insertion Sort)的C语言实现
    栈的链式存储方法的C语言实现
    栈的顺序存储方式的C语言实现
  • 原文地址:https://www.cnblogs.com/yiqiu2324/p/2994085.html
Copyright © 2011-2022 走看看