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

posted @ 2013-04-01 18:39  伊秋  阅读(585)  评论(0编辑  收藏  举报