Java基礎——集合框架(一)

——Java培訓、Android培訓、iOS培訓、.Net培訓、期待與您交流! ——-

集合類

  • 爲什麼出現集合類?
    • 面嚮對象語言對事物的體現都是以對象形式存在的,所以爲了方便對多個對象的操作,就對對象進行存儲,集合就是存儲對象最常用的一種方式
  • 數組和集合類同是容器,有何不同?
    • 數組雖然也可以存儲對象,但是長度是固定的。集合長度是可變的,數組中可以存儲基本數據類型,集合只能存儲對象。
  • 集合類的特點
    • 集合只用於存儲對象,集合長度是可變的,集合可以存儲不同類型的對象。

集合框架
集合有自己的體系,根據每個集合類的共性方法,向上抽取獲得抽象類(或者接口):Collection就是一層一層抽出來的接口

  • Collection
    • List
      • ArrayList
      • LinkedList
      • Vector
    • Set
      • TreeSet
      • HashSet

爲什麼會出現這麼多的容器呢?
因爲每一個容器對數據的存儲方式都不同,這個存儲方式稱爲數據結構。

import java.util.*;
//簡單的示例一下集合中的基本操作
public class CollectionDemo {
    public static void sop(Object obj)
    {
        System.out.println(obj);
    }
    public static void main(String[] args) 
    {
        //創建一個集合Collection的對象,因爲Collection是接口,所以用ArrayList
        ArrayList al =  new ArrayList();

        //在集合中添加元素,用add方法
        al.add("123");//字符串也是對象
        al.add("456");
        al.add("789");
        al.add("101");

        //打印原集合
        sop("原集合爲:"+al);

        //獲取集合中對象的個數,用size方法
        sop("集合中元素的個數爲 "+al.size());

        //刪除集合中的元素
        al.remove("789");
        sop("刪除789後的集合爲 "+al);

        //清空集合
        al.clear();
        sop("集合中元素的個數爲 "+al.size());
    }
}

運行結果爲

簡單方法演示

這裏需要注意的是:
add方法的參數類型是Object,以便於接收任意類型實例對象
集合中存儲的都是對象的引用(地址)

集合中的其他共性方法:取交集

import java.util.*;
//演示集合中的共性方法二:取交集,
public class CollectionDemo2{
    public static void sop(Object obj)
    {
        System.out.println(obj);
    }
    public static void main(String[] args) 
    {
        ArrayList al1 =  new ArrayList();
        al1.add("java01");
        al1.add("java02");
        al1.add("java03");
        al1.add("java04");


        ArrayList al2 =  new ArrayList();
        al2.add("java03");
        al2.add("java04");
        al2.add("java05");
        al2.add("java06");

        sop(al1);
        sop(al2);

        //al1.retainAll(al2);//al1和al2中的共有的元素會保留下來。
        //運行後的結果爲:al1 = [java03,java04]


        //al1.removeAll(al2);//al1和al2中的不同的元素會保留下來。
        //運行後的結果爲:al1 = [java01,java02]

        sop(al1);
        sop(al2);
    }
}

用迭代器操作集合中的元素

什麼是迭代器呢?其實就是集合中元素的取出方式

示例:

import java.util.*;
//演示迭代器的使用方法
public class CollectionDemo3{
    public static void sop(Object obj)
    {
        System.out.println(obj);
    }
    public static void main(String[] args) 
    {
        ArrayList al =  new ArrayList();
        al.add("java01");
        al.add("java02");
        al.add("java03");
        al.add("java04");

        Iterator it = al.iterator();//獲取迭代器,
        //iterator方法返回的是一個接口,所以這裏的it是一個實現了Iterator接口的子類對象

        while(it.hasNext())//hasNext方法:查看集合裏還有沒有元素(可迭代),如果有元素,則返回真
        {
            sop(it.next());//next方法:返回迭代的下一個元素
        }
    }
}

運行結果:

迭代器的使用方法

關於迭代器

在Collection中有很多的集合類,因爲存儲的數據結構不同,所以取出數據的方式也肯定不同,

於是取出方式就定義在了集合的內部,形成了一個內部類,這樣可以直接訪問集合中的元素。

這個內部類實現了Iterator接口,重寫了裏邊的hasNext(判斷)和next(取出)方法,對應的每一種數據類型,在內部類中都有對應的判斷和取出方法。

然後內部類對外提供了一個獲取迭代器的方法,iterator();返回一個內部類的對象。

一般的,爲了節省內存空間,也把獲取元素的方式寫成for循環的形式

for(Iterator it = al.iterator();it.hasnext(); )
{
    sop(it.next());
}

如果用while判斷形式的迭代方式的話,會建立一個it對象,並且對象會一直被引用着,不會被垃圾回收機制所回收,

但是用for循環的話,建立下對象,迭代完成之後,隨着for循環的結束,建立下的對象也就沒有了。這樣子節省空間的。

List集合中的共性方法

  • Collection
    • List:元素是有序的,元素可以重複,因爲該集合體繫有索引
    • Set:元素是無序的,元素不可以重複。因爲裏邊沒有索引

List:
特有方法,凡是可以操作角標的方法都是該體系中的特有方法

增:
add(index,element); 在指定位置添加元素
addAll(index,Collection);在指定位置添加另一個集合中的元素

刪:
remove(index);刪除指定位置的元素

改:
set(index,element);將指定位置的元素改爲參數中的元素

查:
get(index);獲取角標處的元素
subList(from,to);獲取角標區域之間的元素(包含頭不包含尾)
ListIterator();

示例代碼:

//List中的基本方法演示

import java.util.*;

public class ListDemo {
    public static void sop(Object obj)
    {
        System.out.println(obj);
    }
    public static void main(String[] args) 
    {

        ArrayList al = new ArrayList();
        al.add("java01");
        al.add("java02");
        al.add("java03");

        sop("原集合爲:"+al);


        //添加元素
        al.add(3,"java04");//在三角標位添加“java04”
        sop("修改後的集合爲"+al);


        //刪除元素
        al.remove(3);//刪除角標爲3的元素
        sop("修改後的集合爲"+al);

        //修改元素

        al.set(0,"java00");//將角標位0的元素改爲"java00"
        sop("修改後的集合爲"+al);

        //查:
        //獲取單個元素
        sop(al.get(1));//獲取角標位爲0的元素

        //獲取所有的元素  兩種方法
        for (int x = 0;x<al.size() ;x++ )
        {
            sop(al.get(x));
        }
        /*效果和上邊的for循環一樣的
        for (Iterator it = al.iterator();it.hasNext() ; )
        {
            sop(it.next());
        }
        */

        //獲取指定對象的角標:
        sop("獲取的對象的角標是"+al.indexOf("java02"));//獲取“java02在集合中的角標”

        //根據指定角標範圍獲取集合的子集合,
        List sub = al.subList(0,2);//將0,到2角標的對象取出存進一個新的List集合中。
        sop("原集合爲:"+al);
        sop("獲取的子集合爲"+sub);
    }
}

運行結果:

List集合的基本方法演示

ListIterator

集合Collection不是有自己的迭代器麼?爲什麼他的子接口List還要有自己的迭代器呢?

我們看下邊的代碼:
需求是,遍歷集合中的對象,當遍歷到某一個對象的時候,在集合中增加一個對象。

import java.util.*;
public class ListIteratorDemo {
    public static void sop(Object obj)
    {
        System.out.println(obj);
    }
    public static void main(String[] args) 
    {
        ArrayList al = new ArrayList();//創建一個List集合對象並在裏邊添加對象
        al.add("java00");
        al.add("java01");
        al.add("java02");
        al.add("java03");

        Iterator it = al.iterator();
        while(it.hasNext())
        {
            Object obj = it.next();//Object對象指向了取出來的對象的地址

            sop(obj);//打印取出來的對象

            if (obj.equals("java03"))//當取出來的對象的內容和“java03”一樣時,在集合中添加一個對象元素
            {
                al.add("java04");
            }
        }
    }
}

運行結果爲:

Iterator運行結果

從運行結果可以看到,前邊執行打印對象的時候程序都正常執行了,

到後來判斷符合條件將集合中添加元素的時候,出現了異常ConcurrentModificationException

查找API找到這個異常:

ConcurrentModificationException

檢測到併發修改,這是什麼意思呢?

在程序中,當獲取迭代器對象時(al.iterator()),迭代器得到了集合中的元素,並且在判斷(hasNext)和取出(next)的過程中,迭代器一直在操作集合中的元素,這個時候,突然調用集合的方法add對集合中的元素進行操作,一個數據同時被兩個方法操作,這就是對集合的併發修改。

簡單說就是,在迭代時,不可以通過集合的方法操作集合中的元素

在查找API中發現,Iterator中年還有一個方法可以使用,就是remove刪除。

import java.util.*;
public class ListIteratorDemo {
    public static void sop(Object obj)
    {
        System.out.println(obj);
    }
    public static void main(String[] args) 
    {
        ArrayList al = new ArrayList();//創建一個List集合對象並在裏邊添加對象
        al.add("java00");
        al.add("java01");
        al.add("java02");
        al.add("java03");
        sop("原集合爲:"+al);
        Iterator it = al.iterator();
        while(it.hasNext())
        {
            Object obj = it.next();//Object對象指向了取出來的對象的地址
            //sop(obj);//打印取出來的對象
            if (obj.equals("java03"))//當取出來的對象的內容和“java03”一樣時,在集合中刪除一個對象元素
            {
                //al.add("java04");

                it.remove();//這裏用迭代器對象調用,而且不用傳參數,他會把next()返回來的對象直接刪除掉
            }
        }
        sop("迭代器修改後的集合爲"+al);
    }
}

運行結果爲:

Iterator中的刪除方法

發現Iterator中除了判斷和取出的方法,就剩下一個刪除的方法了,

那我們要對迭代器取出來的數據進行更多的操作應該怎麼做呢?

這時候就用到了ListIterator

ListIterator是Iterator的子接口,如果想要在迭代中對其中的數據進行操作,就需要用到其子接口ListIterator,

該接口只能通過List集合的ListIterator方法獲取

ListIterator出現之後,,可以實現對集合中的元素在遍歷過程中的增刪改查操作。

使用方法如下:

import java.util.*;
public class ListIteratorDemo {
    public static void sop(Object obj)
    {
        System.out.println(obj);
    }
    public static void main(String[] args) 
    {
        ArrayList al = new ArrayList();//創建一個List集合對象並在裏邊添加對象
        al.add("java00");
        al.add("java01");
        al.add("java02");
        al.add("java03");

        sop("原集合爲:"+al);//[java00, java01, java02, java03]

        ListIterator li = al.listIterator();//獲取listIterator對象
        sop(li.hasPrevious());//false
        while(li.hasNext())
        {
            Object obj = li.next();
            if (obj.equals("java03"))
            {
                //li.add("java04");//添加元素
                //打印集合的結果爲:[java00, java01, java02, java03,java04]

                //li.remove();//刪除元素
                //打印集合的結果爲:[java00, java01, java02]

                //li.set("java04");//修改元素
                //打印集合的結果爲:[java00, java01, java02, java04]
            }
        }
        sop(li.hasNext());//false
        sop(li.hasPrevious());//true
    }
}

List體系中的成員

實現了List接口的類:

  • ArrayList:底層的數據結構是數組結構,特點:查詢速度很快,但是增刪稍慢,線程不同步.jdk1.2開始使用
  • LinkedList:底層數據結構式鏈表結構,特點:增刪速度很快,查詢速度稍慢,
  • Vector:底層是數組數據結構,線程同步,被ArrayList替代了。jdk1.0開始使用。

ArrayList和Vector在構造函數的區別:

兩個都是基本數據結構爲數組的集合,而且數組都是可變長度的,兩者都是怎麼實現可變長度的呢?

ArrayList在構造的時候會創建一個長度爲10 的數組,如果添加元素的時候,空間不夠用了,他會50%延長,就是說他會再建立一個長度爲15的數組集合,將之前集合中的元素轉移過來,然後再添加元素,

Vector在構造的時候會創建一個長度爲10的數組,如果添加元素的時候,空間不夠用了,他會100%延長,就是說會再建立一個長度爲20的數組集合,

Vector比較浪費資源,所以要使用的話還是建議使用ArrayList。

Vector中的特有方法

Vector中有自己特有的取出數據的方法:枚舉

import java.util.*;
//演示Vector的特有方法:
public class VectorDemo {
    public static void main(String[] args) 
    {
        Vector v = new Vector();
        v.add("java00");
        v.add("java01");
        v.add("java02");
        v.add("java03");

        Enumeration e = v.elements();//獲取枚舉對象
        while(e.hasMoreElements())
        {
            System.out.println(e.nextElement());
        }
    }
}

運行結果:

Vector的特有方法

從以上代碼中可以發現,枚舉和迭代器很像,其實呢,枚舉和迭代器是一樣的,

因爲枚舉的名稱以及方法的名稱都過長,所以被迭代器取代了。枚舉就不用了。

要說ArrayLIst和Vector的區別就是:Vector支持枚舉,ArrayList沒有。

LinkedList

LinkedList中的特有方法:

addFirst();在集合的首位添加元素
addLast();在集合的末尾添加元素

getFirst();獲取最首位元素
getLast();獲取最末尾元素
如果集合中沒有元素,會出現NoSuchElementException

removeFirst();刪除並且返回首位元素
removeLast();刪除並且返回末位元素
如果集合中沒有元素,會出現NoSuchElementException

演示特有方法的使用:


//演示LinkedList中的特有方法
import java.util.*;

public class LinkedListDemo {
    public static void sop(Object obj)
    {
        System.out.println(obj);
    }
    public static void main(String[] args) 
    {
        LinkedList link = new LinkedList();//建立集合對象

        link.addFirst("java01");//在首位添加元素
        link.addFirst("java02");
        link.addFirst("java03");
        link.addFirst("java04");

        sop(link);//打印集合中的元素

    }
}

運行結果:

首位添加元素

爲什麼是倒着的呢?

因爲每次存儲的時候都是在首位添加的,java01放在第一位之後,java02進來放在了首位,也就是java01的前邊,以此類推,存進去的方式就是倒着嘍。

接下來演示其他方法:


//演示LinkedList中的特有方法
import java.util.*;

public class LinkedListDemo {
    public static void sop(Object obj)
    {
        System.out.println(obj);
    }
    public static void main(String[] args) 
    {
        LinkedList link = new LinkedList();//建立集合對象

        link.addFirst("java01");//在首位添加元素
        link.addFirst("java02");
        link.addFirst("java03");
        link.addFirst("java04");

        sop("原集合爲"+link);//[java04, java03, java02, java01]

        //獲取首位元素,獲取末位元素
        sop("獲取的第一位元素是:"+link.getFirst());//java04
        sop("獲取的最後一位元素是:"+link.getLast());//java01

        //刪除並返回首位元素,打印集合長度
        sop("刪除的第一位元素是:"+link.removeFirst());//java04
        sop("刪除第一位後集合的長度是:"+link.size());//3

        sop("再一次刪除的第一位元素是:"+link.removeFirst());//java03
        sop("最後集合的長度是:"+link.size());//2

        //因爲removeFirst方法是刪除並返回某一個元素,所以第一個元素java04被返回來,並且打印出來
        //而打印size的時候,集合的長度已經少了一個。

    }
}

運行結果:

LInkedList中的特有方法

因爲集合中沒有元素的時候會蹦出來異常,所以在jdk1.6版本開始,出現了替代方法:

添加元素:
offerFirst();
offerLast();

獲取元素但不刪除元素:
peekFirst();
peekLast();

獲取元素同時刪除元素:
poolFirst();
poolLast();

LinkedList練習:
用LinkedList模擬一個堆棧或者隊列的數據結構

堆棧:數據存儲和取出方式:先進後出,如同一個杯子
隊列:數據存儲和取出方式:先進先出,如同一個水管

堆棧示例:

//用LInkedList模擬一個堆棧和隊列結構:
/*
思路:根據堆棧先進先出的特點,想到LinkedList中有特有的添加頭,添加尾,取出頭,取出尾的操作,根據這樣的方法,可以實現堆棧的操作
*/
import java.util.*;


class DuiZhan//將堆棧存取方式封裝成一個對象,
{
    private LinkedList link;//因爲堆棧類的底層使用LinkedList的方法
    DuiZhan()//所以類一初始化的時候就要創建一個LinkedList集合對象
    {
        link = new LinkedList();
    }
    public void myAdd(Object obj)
    {
        link.addFirst(obj);//添加元素的時候一直在最前邊添加(添加完以後的順序就是倒着的)
    }
    public Object myGet()
    {
        return link.removeFirst();//取出元素的時候就要從前邊取,以實現堆棧中先進後出
    }
    public boolean isNull()
    {
        return link.isEmpty();//底層的判斷爲空的方法
    }
}
public class LinkedListTest {
    public static void sop(Object obj)
    {
        System.out.println(obj);
    }
    public static void main(String[] args) 
    {
        DuiZhan dz = new DuiZhan();
        dz.myAdd("java01");
        dz.myAdd("java02");
        dz.myAdd("java03");
        dz.myAdd("java04");

        //sop(dz.myGet());
        while (!dz.isNull())
        {
            sop(dz.myGet());
        }
    }
}

運行結果爲:

堆棧描述

隊列示例;這就簡單了,存儲的方式不變,取出的方式從後面開始取就好了,代碼如下:

//用LInkedList模擬一個堆棧和隊列結構:
/*
    思路和堆棧一樣的,都是利用LinkedList中的特有方法,實現先進先出的效果。
*/
import java.util.*;


class DuiLie//將隊列存取方式封裝成一個對象,
{
    private LinkedList link;//因爲堆棧類的底層使用LinkedList的方法
    DuiLie()//所以類一初始化的時候就要創建一個LinkedList集合對象
    {
        link = new LinkedList();
    }
    public void myAdd(Object obj)
    {
        link.addFirst(obj);//添加元素的時候一直在最前邊添加(添加完以後的順序就是倒着的)
    }
    public Object myGet()
    {
        return link.removeLast();//取出元素的時候就要從後邊取,以實現堆棧中先進先出
    }
    public boolean isNull()
    {
        return link.isEmpty();//底層的判斷爲空的方法
    }
}
public class LinkedListTest2 {
    public static void sop(Object obj)
    {
        System.out.println(obj);
    }
    public static void main(String[] args) 
    {
        DuiLie dl = new DuiLie();
        dl.myAdd("java01");
        dl.myAdd("java02");
        dl.myAdd("java03");
        dl.myAdd("java04");

        //sop(dl.myGet());
        while (!dl.isNull())
        {
            sop(dl.myGet());
        }
    }
}

運行結果爲:

隊列示例

下面看一個ArrayList的練習,

需求是:刪除掉ArrayList中的重複的元素:

//需求:刪除ArrayList中的重複元素
/*
    思路:在ArrayList中是沒有辦法判斷的,
    新建一個ArrayList集合newAl,在al中取一個元素,判斷在newAl中有沒有相同的元素,
    沒有,則將該元素,存進newAl中,如果有,則不做操作
    最後將newAl返回一個ArrayList,賦值給al就行了
*/
import java.util.*;
public class ArrayListTest {
    public static void sop(Object obj)
    {
        System.out.println(obj);
    }
    public static ArrayList singleElement(ArrayList al)//刪除重複元素的方法
    {
        ArrayList newAl = new ArrayList();//先建立一個新的集合
        for (Iterator it = al.iterator();it.hasNext() ; )//遍歷老集合
        {
            Object obj = it.next();//將遍歷出來的元素用obj引用了
            if (!newAl.contains(obj))//判斷該obj對象在新集合中不存在
            {
                newAl.add(obj);//則將該元素添加進心集合中,
            }
        }
        return newAl;//最後返回新集合
    }
    public static void main(String[] args) 
    {
        ArrayList al = new ArrayList();
        al.add("java01");
        al.add("java02");
        al.add("java02");
        al.add("java03");
        al.add("java03");
        al.add("java04");

        sop(al);
        al = singleElement(al);
        sop(al);
    }
}

運行結果:

取出重複元素

運行完以後,我們的需求就提升了,要將自定義的對象,存進集合當中,然後刪除相同的元素。

有了上邊的例子,我們信手拈來:

還沒有寫刪除重複元素的代碼,先看一下程序能不能運行起來

//需求:將自定義對象存入集合中,並刪除重複的元素
/*
定義一個Person類,類中有姓名和年齡,姓名和年齡相同視爲同一個人
*/

import java.util.*;

class Person//定義Person類
{
    private String name;
    private int age;
    public void set(String name,int age)
    {
        this.name = name;
        this.age = age;
    }
    public String getName()
    {
        return name;
    }
    public int getAge()
    {
        return age;
    }
}
public class ArrayListTest2 {
    public static void sop(Object obj)
    {
        System.out.println(obj);
    }
    public static void main(String[] args) 
    {
        ArrayList al = new ArrayList();//創建集合
        Person p1 = new Person();//創建對象
        Person p2 = new Person();
        Person p3 = new Person();
        Person p4 = new Person();

        p1.set("zhangsan01",21);//給對象設置參數
        p2.set("zhangsan02",22);
        p3.set("zhangsan03",23);
        p4.set("zhangsan04",24);

        al.add(p1);//在集合中天劍參數
        al.add(p2);
        al.add(p3);
        al.add(p4);


        for (Iterator it = al.iterator();it.hasNext() ; )//通過迭代器列出集合中的元素,
        {
            Object obj = it.next();
            sop(obj.getName()+"......"+obj.getAge());//爲了顯示的清楚一寫,直接顯示的是對象中的姓名和年齡屬性。
        }

    }
}

編譯的時候出現瞭如下錯誤:

編譯失敗結果

圖中顯示,編譯失敗,爲什麼失敗呢?找不到getName和 getAge方法。

這是因爲使用add方法添加的時候,add中的參數是(Object obj),
所以將Person對象存入集合的時候,對Person對象進行了提升操作,提升成他的父類Object,
Object p = new Person();
所以在迭代器中使用obj.getName()方法的時候,這裏的obj是Object類型的,
Object中沒有getName()方法,所以會提示出這樣的錯誤
看不懂的話返回去重新學習繼承那一篇

解決方法就是在打印前對obj進行強制轉換。

//需求:將自定義對象存入集合中,並刪除重複的元素
/*
定義一個Person類,類中有姓名和年齡,姓名和年齡相同視爲同一個人
*/

import java.util.*;

class Person//定義Person類
{
    private String name;
    private int age;
    public void set(String name,int age)
    {
        this.name = name;
        this.age = age;
    }
    public String getName()
    {
        return name;
    }
    public int getAge()
    {
        return age;
    }
}
public class ArrayListTest2 {
    public static void sop(Object obj)
    {
        System.out.println(obj);
    }
    public static ArrayList singleElement(ArrayList al)//刪除重複元素的方法
    {
        ArrayList newAl = new ArrayList();//先建立一個新的集合
        for (Iterator it = al.iterator();it.hasNext() ; )//遍歷老集合
        {
            Object obj = it.next();//將遍歷出來的元素用obj引用了
            if (!newAl.contains(obj))//判斷該obj對象在新集合中不存在
            {
                newAl.add(obj);//則將該元素添加進心集合中,
            }
        }
        return newAl;//最後返回新集合
    }
    public static void main(String[] args) 
    {
        ArrayList al = new ArrayList();//創建集合
        Person p1 = new Person();//創建對象
        Person p2 = new Person();
        Person p3 = new Person();
        Person p4 = new Person();
        Person p5 = new Person();
        Person p6 = new Person();

        p1.set("zhangsan01",21);
        p2.set("zhangsan02",22);
        p3.set("zhangsan03",23);
        p4.set("zhangsan04",24);
        p5.set("zhangsan02",22);//重複元素
        p6.set("zhangsan03",23);//重複元素

        al.add(p1);
        al.add(p2);
        al.add(p3);
        al.add(p4);
        al.add(p5);//重複元素
        al.add(p6);//重複元素

        al = singleElement(al);//去除重複元素
        for (Iterator it = al.iterator();it.hasNext() ; )//再迭代打印出來
        {
            Object obj = it.next();
            Person p = (Person)obj;
            sop(p.getName()+"......"+p.getAge());
        }

    }
}

運行結果爲:

重複元素沒有被刪除

爲什麼爲出現這樣的情況呢?

這是因爲,在singleElement方法中迭代判斷的時候,用到了contains方法,
contains方法:集合中是否包含參數中的對象,
該方法的底層原理就是將自己集合中的每一個元素和參數中的元素進行比較,
怎麼比較呢?當然是用equals方法,
而在ArrayList中的equals方法比較的是地址值,
集合中添加進來的東西都是新創建的,地址值肯定不一樣的,所以他會認爲添加的每一個元素都不同,所以就不會清楚元素。

而我們的實際需求是,姓名年齡相同就視爲同一個人,
所以在這裏我們要重寫Person類的equals方法:目的在於當使用Person類對象進行比較的時候,用我自己定義的規則來比較,不再比較哈希值

修改代碼:值需要將Person類中多寫一個equals方法就行了。

//需求:將自定義對象存入集合中,並刪除重複的元素
/*
定義一個Person類,類中有姓名和年齡,姓名和年齡相同視爲同一個人
*/

import java.util.*;

class Person//定義Person類
{
    private String name;
    private int age;
    public void set(String name,int age)
    {
        this.name = name;
        this.age = age;
    }
    public String getName()
    {
        return name;
    }
    public int getAge()
    {
        return age;
    }
    //重寫了equals方法
    public boolean equals(Object obj)
    {
        if (!(obj instanceof Person))//如果傳進來的對象不是Person類的
            return false;//直接返回假
        Person p = (Person)obj;//否則將obj強制轉換成person類型的
        return this.name.equals(p.name) && this.age==p.age;//然後比較他們的name屬性是否相同,和age屬性是否相同
    }
}
public class ArrayListTest2 {
    public static void sop(Object obj)
    {
        System.out.println(obj);
    }
    public static ArrayList singleElement(ArrayList al)//刪除重複元素的方法
    {
        ArrayList newAl = new ArrayList();//先建立一個新的集合
        for (Iterator it = al.iterator();it.hasNext() ; )//遍歷老集合
        {
            Object obj = it.next();//將遍歷出來的元素用obj引用了
            if (!newAl.contains(obj))//判斷該obj對象在新集合中不存在
            {
                newAl.add(obj);//則將該元素添加進心集合中,
            }
        }
        return newAl;//最後返回新集合
    }
    public static void main(String[] args) 
    {
        ArrayList al = new ArrayList();//創建集合
        Person p1 = new Person();//創建對象
        Person p2 = new Person();
        Person p3 = new Person();
        Person p4 = new Person();
        Person p5 = new Person();
        Person p6 = new Person();

        p1.set("zhangsan01",21);
        p2.set("zhangsan02",22);
        p3.set("zhangsan03",23);
        p4.set("zhangsan04",24);
        p5.set("zhangsan02",22);
        p6.set("zhangsan03",23);

        al.add(p1);
        al.add(p2);
        al.add(p3);//重複元素
        al.add(p4);
        al.add(p5);
        al.add(p6);//重複元素

        al = singleElement(al);//去除重複元素
        for (Iterator it = al.iterator();it.hasNext() ; )//再迭代打印出來
        {
            Object obj = it.next();
            Person p = (Person)obj;
            sop(p.getName()+"......"+p.getAge());
        }

    }
}

運行結果爲:

運行結果正確

這個小程序就結束了,,然後需要我們注意的是,在contains方法中,底層調用的Person類的equals方法,因爲他要判斷自己裏邊的元素和傳進來的元素是否一樣,調用equals方法,

刪除元素:底層調用的也是equals方法,比如在以上的代碼中,

al.remove(new Person("zhangsan01",21));
//他會拿括號中的這個參數的對象去找自己集合裏邊有沒有這樣一個元素,拿自己的元素調用equals方法和參數中的對象進行比較,如果如果存在返回真,如果不存在,返回假。

那麼在開發的時候用哪個集合呢?

看需求:涉及到比較頻繁的增刪操作,用LinkedList
涉及到查詢操作,但不是很頻繁的時候,用ArrayList
只要沒有太頻繁的操作,一般都是用ArrayList

Set

元素是無序的(就是說存入的順序和取出的順序不一樣),而且元素不可以重複。

查看API發現Set集合的方法和Collection的方法是一樣的。

實現了Set接口的類有:

  • HashSet:底層數據結構是哈希表;
  • TreeSet:

HashSet集合

什麼是哈希值呢?

當我們創建好一個對象,並且直接打印對象的時候,打印出來的東西就是哈希值。

對象在HashSet中是怎麼存儲的呢?

當創建好一個對象d1,d1就有了自己的哈希值(hashCode方法可以算出來),當將d1添加進集合中的時候,會將d1的哈希值給了集合,
當創建了又一個對象d2的時候,將d2天劍進來的時候,再將d2的哈希值給了集合,
兩個對象的哈希值會進行比較,哈希值比較大的就會排在後邊,哈希值比價小的就會排在前邊。
這也是爲什麼HashSet中對象爲什麼是無序的了。

HashSet集合中的存儲方式

對象被添加進HashSet中的時候,會那自己的哈希值和集合中的哈希值進行比較,
如果哈希值不一樣,就會存儲進來,
如果哈希值一樣,會再次進行比較對象是否一樣,
如果對象一樣,則不存儲,add方法返回假,
如果對象不一樣,則會在原有的哈希值上邊順延,將對象存儲進來,add方法返回真。

//需求:簡單演示一下HashSet集合的存儲方法和存儲特點
import java.util.*;
public class HashSetDemo {
    public static void sop(Object obj)
    {
        System.out.println(obj);
    }

    public static void main(String[] args) 
    {
        HashSet hs = new HashSet();

        hs.add("java01");
        sop(hs.add("java02"));//打印結果爲真
        sop(hs.add("java02"));//重複元素,打印結果爲假
        hs.add("java03");
        hs.add("java04");
        hs.add("java04");//重複元素

        //取出Set中的元素的方法,只有一個就是迭代器:
        for (Iterator it = hs.iterator(); it.hasNext(); )
        {
            sop(it.next());
        }
    }
}

運行結果爲:

HashSet簡單演示

由上圖可以看出,HashSet中存儲的元素是無序的,而且可以保證對象的唯一性。

在HashSet中存儲自定義對象

還是上邊的需求,對象人,有屬性name和age,相同name和age的元素視爲同一個人。

//往HashSet方法中添加自定義對象,


import java.util.*;
class Person
{
    private String name;
    private int age;
    Person(String name,int age)
    {
        this.name = name;
        this.age = age;
    }
    public String getName()
    {
        return name;
    }
    public int getAge()
    {
        return age;
    }
    //在Person類中重寫了equals方法。

    public boolean equals(Object obj)//這裏必須寫的是Object類型的參數,如果不是說明覆寫的不是Object類中的額方法
    {
        //因爲傳進來的參數是Object類型的,所以在這裏要判斷一下
        if (!(obj instanceof Person))
            return false;

        //如果obj屬於Person對象的話,強轉。
        Person p = (Person)obj;

        //這裏多假一條打印語句,看看equals方法有沒有被執行到。
        System.out.println("name= "+p.name+"...age= "+p.age);


        return this.name.equals(p.name) && this.age == p.age;
    }
}
public class HashSetDemo2 {
    public static void sop(Object obj)
    {
        System.out.println(obj);
    }
    public static void main(String[] args) 
    {
        HashSet hs = new HashSet();
        hs.add(new Person("zhangsan01",21));
        hs.add(new Person("zhangsan02",22));
        hs.add(new Person("zhangsan03",23));
        hs.add(new Person("zhangsan04",24));
        hs.add(new Person("zhangsan04",24));//重複元素


        for (Iterator it = hs.iterator();it.hasNext();)
        {
            Object obj = it.next();
            Person p = (Person)obj;
            sop("name= "+p.getName()+"...age= "+p.getAge());
        }

    }
}

運行結果爲:

哈希Set中傳入自定義對象

一看結果,鬱悶的要死。equals方法根本就沒有執行,而且重複元素也存進來了,
然後一想,HashSet集合中的底層數據是哈希表,按照HashSet中的存儲規則,
我們每一次存進去的對象都是新new出來的,哈希值(地址值)肯定不一樣了,
要按照我們判斷元素唯一的方法去存儲數據的話,需要重寫hashCode方法。

根據這個思路,我們修改了代碼:

//往HashSet方法中添加自定義對象,


import java.util.*;
class Person
{
    private String name;
    private int age;
    Person(String name,int age)
    {
        this.name = name;
        this.age = age;
    }
    public String getName()
    {
        return name;
    }
    public int getAge()
    {
        return age;
    }
    //在Person類中重寫了equals方法。

    public boolean equals(Object obj)//這裏必須寫的是Object類型的參數,如果不是說明覆寫的不是Object類中的額方法
    {
        //因爲傳進來的參數是Object類型的,所以在這裏要判斷一下
        if (!(obj instanceof Person))
            return false;

        //如果obj屬於Person對象的話,強轉。
        Person p = (Person)obj;

        //這裏多假一條打印語句,看看equals方法有沒有被執行到。
        System.out.println(this.name+"...equals..."+p.name);


        return this.name.equals(p.name) && this.age == p.age;
    }

    //重寫hashCode方法。
    public int hashCode()
    {
        System.out.println(this.name+".......hashCode");
        return this.name.hashCode() + this.age*7;
    }
}
public class HashSetDemo2 {
    public static void sop(Object obj)
    {
        System.out.println(obj);
    }
    public static void main(String[] args) 
    {
        HashSet hs = new HashSet();
        hs.add(new Person("zhangsan01",21));
        hs.add(new Person("zhangsan02",22));
        hs.add(new Person("zhangsan03",23));
        hs.add(new Person("zhangsan04",24));
        hs.add(new Person("zhangsan04",24));//重複元素



        for (Iterator it = hs.iterator();it.hasNext();)
        {
            Object obj = it.next();
            Person p = (Person)obj;
            sop("name= "+p.getName()+"...age= "+p.getAge());
        }

    }
}

運行結果:

將自定義對象存進HashSet集合中實驗成功

從以上程序中我們可以看出來:HashSet是如何保證元素的唯一性的呢?

是通過元素的兩個方法:hashCode和equals來完成的。
如果元素的HashCode值相同,纔會判斷equals是否爲true;
如果元素的HashCode值不同,不會調用equals方法。

不僅是在存儲元素上,在判斷存在(contains)和刪除(remove)元素上,依賴的也是hashCode和equals方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章