Java中怎么利用Collection實現(xiàn)集合

這篇文章將為大家詳細(xì)講解有關(guān)Java中怎么利用Collection實現(xiàn)集合,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關(guān)知識有一定的了解。

創(chuàng)新互聯(lián)作為成都網(wǎng)站建設(shè)公司,專注成都網(wǎng)站建設(shè)、網(wǎng)站設(shè)計,有關(guān)成都企業(yè)網(wǎng)站建設(shè)方案、改版、費用等問題,行業(yè)涉及建筑動畫等多個領(lǐng)域,已為上千家企業(yè)服務(wù),得到了客戶的尊重與認(rèn)可。

前言

集合就是一組數(shù)的集合,就像是一個容器,但是我們應(yīng)該清楚的是集合中存放的都是對象的引用,而不是真正的實體。而我們常說的集合中的對象其實指的就是對象的引用。

我們可以把集合理解為一個小型數(shù)據(jù)庫,用于存放數(shù)據(jù),我們對集合的操作也就是數(shù)據(jù)的增刪改查,在 Java 中有兩個頂層接口 Collection 和 Map 用于定義和規(guī)范集合的相關(guān)操作。這篇文章主要說一下集合框架中的 Collection 部分。

Collection 表示一組對象,這些對象可以是有序也可以是無序的,它提供了不同的子接口滿足我們的需求。我們主要看看 List 和 Set 。

Java中怎么利用Collection實現(xiàn)集合 

List 整體的特征就是有序可重復(fù)。我們需要研究的是上圖中具體的實現(xiàn)類都有什么特性。底層的實現(xiàn)原理是什么,首先來看一看 List 的古老的實現(xiàn)類 Vector ,說是古老是因為在 JDK 1.0 的時候就出現(xiàn)了,都走開,我要開始看源碼了!這些源碼來自于 JDK 1.7。

public class Vector<E>
 extends AbstractList<E>
 implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
 /**
  * The array buffer into which the components of the vector are
  * stored.
  */
 protected Object[] elementData;

 /**
  * The number of valid components in this {@code Vector} object.
  */
 protected int elementCount;

 /**
  * The amount by which the capacity of the vector is automatically
  * incremented when its size becomes greater than its capacity. If
  * the capacity increment is less than or equal to zero, the capacity
  * of the vector is doubled each time it needs to grow.
  */
 protected int capacityIncrement;

 public Vector() {
  this(10);
 }

 // 添加元素
 public synchronized boolean add(E e) {
  modCount++;
  ensureCapacityHelper(elementCount + 1);
  elementData[elementCount++] = e;
  return true;
 }

 // 刪除元素
 public synchronized E remove(int index) {
  modCount++;
  if (index >= elementCount)
   throw new ArrayIndexOutOfBoundsException(index);
  E oldValue = elementData(index);

  int numMoved = elementCount - index - 1;
  if (numMoved > 0)
   System.arraycopy(elementData, index+1, elementData, index,
        numMoved);
  elementData[--elementCount] = null; // Let gc do its work

  return oldValue;
 }

 // 修改元素
 public synchronized E set(int index, E element) {
  if (index >= elementCount)
   throw new ArrayIndexOutOfBoundsException(index);

  E oldValue = elementData(index);
  elementData[index] = element;
  return oldValue;
 }
 // 查找元素
 public synchronized E get(int index) {
  if (index >= elementCount)
   throw new ArrayIndexOutOfBoundsException(index);

  return elementData(index);
 }
...

就以上源碼分析就可以知道關(guān)于 Vector 的特征。1 底層實現(xiàn)是使用數(shù)組來存儲數(shù)據(jù),所以相應(yīng)的查找元素和添加元素速度較快,刪除和插入元素較慢。2 數(shù)組的初始長度為 10 ,當(dāng)長度不夠時,增長量也為 10 使用變量 capacityIncrement 來表示。3 方法的聲明中都加入了 synchronized 關(guān)鍵字,線程安全的,所以效率相應(yīng)降低了。 4 沒分析出來的再看一遍。

下面開始看 ArrayList 的源碼。

public class ArrayList<E> extends AbstractList<E>
  implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
 private static final long serialVersionUID = 8683452581122892189L;

 /**
  * Default initial capacity.
  */
 private static final int DEFAULT_CAPACITY = 10;

 /**
  * Shared empty array instance used for empty instances.
  */
 private static final Object[] EMPTY_ELEMENTDATA = {};

 /**
  * The array buffer into which the elements of the ArrayList are stored.
  * DEFAULT_CAPACITY when the first element is added.
  */
 private transient Object[] elementData;

 /**
  * Constructs an empty list with an initial capacity of ten.
  */
 public ArrayList() {
  super();
  this.elementData = EMPTY_ELEMENTDATA;
 }

 // 添加元素
 public boolean add(E e) {
  ensureCapacityInternal(size + 1); // Increments modCount!!
  elementData[size++] = e;
  return true;
 }

 // 增加數(shù)組的長度
 private void grow(int minCapacity) {
  // overflow-conscious code
  int oldCapacity = elementData.length;
  int newCapacity = oldCapacity + (oldCapacity >> 1);
  if (newCapacity - minCapacity < 0)
   newCapacity = minCapacity;
  if (newCapacity - MAX_ARRAY_SIZE > 0)
   newCapacity = hugeCapacity(minCapacity);
  // minCapacity is usually close to size, so this is a win:
  elementData = Arrays.copyOf(elementData, newCapacity);
 }
...

因為源碼和 Vector 類似,所以有些就不貼了,但是不耽誤我們繼續(xù)分析 ArrayList 。1 底層存儲數(shù)據(jù)使用的還是數(shù)組,長度依舊為 10 ,但是進(jìn)步了,沒有在剛開始創(chuàng)建的時候就初始化,而是在添加第一個元素的時候才初始化的。2 方法的聲明少了 synchronized 關(guān)鍵字,線程不安全,但性能提高了。3 數(shù)組長度不夠時,會自動增加為原長度的 1.5 倍。

以上分析也能體現(xiàn)出 Vector 和 ArrayList 的差別。主要就是想說 Vector 已經(jīng)不用了。使用 ArrayList 即可,關(guān)于線程安全問題,后面再說。

接著看 LinkedList 的實現(xiàn),上源碼 ~

public class LinkedList<E>
 extends AbstractSequentialList<E>
 implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
 transient int size = 0;

 /**
  * Pointer to first node.
  */
 transient Node<E> first;

 /**
  * Pointer to last node.
  */
 transient Node<E> last;

 /**
  * Constructs an empty list.
  */
 public LinkedList() {
 }

 // 每一個元素即為一個節(jié)點,節(jié)點的結(jié)構(gòu)如下(這是一個內(nèi)部類?。?
 private static class Node<E> {
  E item;
  Node<E> next;
  Node<E> prev;

  Node(Node<E> prev, E element, Node<E> next) {
   this.item = element;
   this.next = next;
   this.prev = prev;
  }
 }

 // 添加元素
 public boolean add(E e) {
  linkLast(e);
  return true;
 }

 void linkLast(E e) {
  final Node<E> l = last;
  final Node<E> newNode = new Node<>(l, e, null);
  last = newNode;
  if (l == null)
   first = newNode;
  else
   l.next = newNode;
  size++;
  modCount++;
 }

 // 刪除某個節(jié)點的邏輯
 E unlink(Node<E> x) {
  // assert x != null;
  final E element = x.item;
  final Node<E> next = x.next;
  final Node<E> prev = x.prev;

  if (prev == null) {
   first = next;
  } else {
   prev.next = next;
   x.prev = null;
  }

  if (next == null) {
   last = prev;
  } else {
   next.prev = prev;
   x.next = null;
  }

  x.item = null;
  size--;
  modCount++;
  return element;
 }
...

重點就是 LinkedList 的底層實現(xiàn)是雙鏈表。這樣就會有以下特性,1 查找元素較慢,但是添加和刪除較快。2 占內(nèi)存,因為每一個節(jié)點都要維護(hù)兩個索引。3 線程不安全 。4 對集合長度沒有限制。

以上,List 的幾個實現(xiàn)已經(jīng)分析完成,以后再談到 Vector ,ArrayList ,LinkedList 之間的區(qū)別應(yīng)該不會不知所云了吧!還要接著看 Collection 的另一個子接口 Set 。首先有個大前提,Set 中存儲的元素是無序不可重復(fù)的。然后我們再來看實現(xiàn)類是如何實現(xiàn)的。下面開始 HashSet 的表演。

public class HashSet<E>
 extends AbstractSet<E>
 implements Set<E>, Cloneable, java.io.Serializable
{
 private transient HashMap<E,Object> map;

 // Dummy value to associate with an Object in the backing Map
 private static final Object PRESENT = new Object();

 /**
  * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
  * default initial capacity (16) and load factor (0.75).
  */
 public HashSet() {
  map = new HashMap<>();
 }

 // 添加元素,其實就是像 Map 中添加主鍵,所以添加的元素不能重復(fù)
 public boolean add(E e) {
  return map.put(e, PRESENT)==null;
 }

 // 實現(xiàn) Iterable 接口中的 Iterator 方法。
 public Iterator<E> iterator() {
  return map.keySet().iterator();
 }
...

看了源碼才發(fā)現(xiàn),1 原來 HashSet 就是對 HashMap 的封裝啊,底層實現(xiàn)是基于 hash 表的,回頭有必要再好好的介紹一下 hash 相關(guān)的知識。2 Set 集合中的值,都會以 key 的形式存放在 Map 中,所以說 Set 中的元素不能重復(fù)。3 線程不安全。4 允許存放空值,因為 Map 的鍵允許為空。

今天要說的最后一個實現(xiàn)類終于出現(xiàn)啦,他就是 TreeSet ,這個實現(xiàn)類中的元素是有序的!注意這里說的有序是指按照一定的規(guī)則排序,而我們說 Set 集合中的元素?zé)o序是因為添加進(jìn)集合的順序和輸出的順序不保證一致。TreeSet 是怎么保證有序我們待會再說,還是一樣的套路,是對 TreeMap 的封裝,線程依舊不安全。

public class TreeSet<E> extends AbstractSet<E>
 implements NavigableSet<E>, Cloneable, java.io.Serializable
{
 /**
  * The backing map.
  */
 private transient NavigableMap<E,Object> m;

 // Dummy value to associate with an Object in the backing Map
 private static final Object PRESENT = new Object();

 /**
  * Constructs a set backed by the specified navigable map.
  */
 TreeSet(NavigableMap<E,Object> m) {
  this.m = m;
 }

 /**
  * Constructs a new, empty tree set, sorted according to the
  * natural ordering of its elements. All elements inserted into
  * the set must implement the Comparable interface.
  */
 public TreeSet() {
  this(new TreeMap<E,Object>());
 }

 /**
  * Constructs a new, empty tree set, sorted according to the specified
  * comparator. All elements inserted into the set must be mutually
  * comparable by the specified comparator
  * If the Comparable natural ordering of the elements will be used.
  */
 public TreeSet(Comparator<? super E> comparator) {
  this(new TreeMap<>(comparator));
 }
 
 // 添加元素方法
 public boolean add(E e) {
  return m.put(e, PRESENT)==null;
 } 
...

我們可以看到 TreeSet 中構(gòu)造函數(shù)上方的注釋,TreeSet 要保證元素有序,保證有序的思路是在添加元素的時候進(jìn)行比較。

... // 這是 TreeMap 的 put 方法的節(jié)選,為了看到比較的過程。
 public V put(K key, V value) {
 ...
  // split comparator and comparable paths
  Comparator<? super K> cpr = comparator;
  if (cpr != null) {
   do {
    parent = t;
    cmp = cpr.compare(key, t.key);
    if (cmp < 0)
     t = t.left;
    else if (cmp > 0)
     t = t.right;
    else
     return t.setValue(value);
   } while (t != null);
  }
  else {
   if (key == null)
    throw new NullPointerException();
   Comparable<? super K> k = (Comparable<? super K>) key;
   do {
    parent = t;
    cmp = k.compareTo(t.key);
    if (cmp < 0)
     t = t.left;
    else if (cmp > 0)
     t = t.right;
    else
     return t.setValue(value);
   } while (t != null);
  }
 ...
 }

Java 中提供了兩種方式,第一種方法,需要我們所添加對象的類實現(xiàn) Comparable 接口,進(jìn)而實現(xiàn) compareTo 方法,這種方式也叫自然排序,我們并沒有傳入什么排序規(guī)則。這種方式對應(yīng) TreeSet 的空參構(gòu)造器。而另一種方式就是定制排序,即我們自己定義兩個元素的排序規(guī)則,在實例化 TreeSet 的時候傳入對應(yīng)的排序規(guī)則即可,對應(yīng)于 TreeSet 中帶有 Comparator 接口的構(gòu)造器,這里面需要實現(xiàn) compare 方法 。有點迷糊了是吧,舉個例子看看 ~

public class Person implements Comparable<Person>{

 public String name;
 public Integer age;

 public Person(String name,Integer age) {
  this.name = name;
  this.age = age;
 }
 /* 自定義的比較的邏輯:
  *  首先按照對象的 name 屬性排序
  *  其次按照 age 屬性排序
  * 方法的返回值為 0 ,大于 0,小于 0 ,分別對應(yīng)于 相等,大于,和小于
  */
 @Override
 public int compareTo(Person o) {
  int i = this.name.compareTo(o.name);
  if(i == 0){
   return this.age.compareTo(o.age);
  }else {
   return i;
  }
 }
 @Override
 public String toString() {
  return "[Person] name:"+this.name+" age:"+this.age;
 }
}

 // 以下是測試代碼
 public static void main(String[] args) {
  TreeSet<Person> set = new TreeSet<>();
  set.add(new Person("AJK923",20));
  set.add(new Person("BJK923",20));
  set.add(new Person("AJK923",21));
  set.add(new Person("BJK923",21));

  for (Person person : set) {
   System.out.println(person.toString());
  }
  /*
  [Person] name:AJK923 age:20
  [Person] name:AJK923 age:21
  [Person] name:BJK923 age:20
  [Person] name:BJK923 age:21
  */
    
  ----以下為定制排序的部分----匿名內(nèi)部類實現(xiàn) Comparator 接口----  
  TreeSet<Person> set2 = new TreeSet<>(new Comparator<Person>() {
   @Override
   public int compare(Person o1, Person o2) {
    int i = o1.age.compareTo(o2.age);
    if(i == 0){
     return o1.name.compareTo(o2.name);
    }else {
     return i;
    }
   }
  });

  set2.add(new Person("AJK923",20));
  set2.add(new Person("BJK923",20));
  set2.add(new Person("AJK923",21));
  set2.add(new Person("BJK923",21));

  for (Person person : set2) {
   System.out.println(person.toString());
  }
  /*
   [Person] name:AJK923 age:20
   [Person] name:BJK923 age:20
   [Person] name:AJK923 age:21
   [Person] name:BJK923 age:21
   */
 }

上面就是自然排序和定制排序的應(yīng)用。要說明幾點:

1 定制排序和自然排序同時存在的話,優(yōu)先執(zhí)行定制排序??梢钥纯瓷厦娴?put 方法的實現(xiàn) 。

2 自然排序?qū)?yīng) Comparable 接口,定制排序?qū)?yīng) Comparator 接口。

3 String ,包裝類,日期類都已經(jīng)實現(xiàn)了 Comparable 接口的 comparaTo 方法,所以我上面的例子中都偷懶了,沒有自己實現(xiàn)具體比較,而是直接調(diào)用現(xiàn)成的。

看到這里,也算是對 Collection 有了整體的認(rèn)識,但是這里我并沒有說到具體的 API ,我現(xiàn)在日常也用不到幾個方法,就放一張 Collection 接口的結(jié)構(gòu)圖吧,方法也比較簡單,看個名字就大概知道什么意思了。

Java中怎么利用Collection實現(xiàn)集合

還是要重點說一下 iterator 方法。這個方法定義在 Iterable 接口中(Collection 繼承了 Iterable 接口),方法返回的是 iterator 接口對象。iterator 中定義了迭代器的操作規(guī)則,而 Collection 的實現(xiàn)類中是具體的實現(xiàn)。Iterator 接口中定義了 next ,hasNext 和 remove 方法??匆幌略贏rrayList 中是如何實現(xiàn)的吧。

private class Itr implements Iterator<E> {
  int cursor;  // index of next element to return
  int lastRet = -1; // index of last element returned; -1 if no such
  int expectedModCount = modCount;

  public boolean hasNext() {
   return cursor != size;
  }

  @SuppressWarnings("unchecked")
  public E next() {
   checkForComodification();
   int i = cursor;
   if (i >= size)
    throw new NoSuchElementException();
   Object[] elementData = ArrayList.this.elementData;
   if (i >= elementData.length)
    throw new ConcurrentModificationException();
   cursor = i + 1;
   return (E) elementData[lastRet = i];
  }

  public void remove() {
   if (lastRet < 0)
    throw new IllegalStateException();
   checkForComodification();

   try {
    ArrayList.this.remove(lastRet);
    cursor = lastRet;
    lastRet = -1;
    expectedModCount = modCount;
   } catch (IndexOutOfBoundsException ex) {
    throw new ConcurrentModificationException();
   }
  }

  final void checkForComodification() {
   if (modCount != expectedModCount)
    throw new ConcurrentModificationException();
  }
 }

應(yīng)用起來還是比較簡單的,使用一個 while 循環(huán)即可。看下面這個例子。

  public static void main(String[] args) {

  List<Integer> list = new ArrayList<>(); 
  list.add(1);
  list.add(2);
  list.add(3);

  Iterator<Integer> it = list.iterator();
  while (it.hasNext()) {
   Integer i = it.next();
   System.out.println(i);
  }
 }

不知道你有沒有發(fā)現(xiàn),除了 Vector 以外,保存集合元素的那個變量都定義為 transient 不管你是數(shù)組,Node 或是 Map ,不由得我又要想一想為什么這樣設(shè)計?

先看一下 transient 的作用吧!Java 的 serialization 提供了一種持久化對象實例的機(jī)制。當(dāng)持久化對象時,可能有一個特殊的對象數(shù)據(jù)成員,我們不想用 serialization 機(jī)制來保存它。為了在一個特定對象的一個域上關(guān)閉 serialization,可以在這個域前加上關(guān)鍵字 transient 。當(dāng)一個對象被序列化的時候,transient 型變量的值不包括在序列化的表示中,非 transient 型的變量是被包括進(jìn)去的。

那么為什么要這么做呢,好好的標(biāo)準(zhǔn)序列化不用,原因如下:

對于 ArrayList 來說,底層實現(xiàn)是數(shù)組,而數(shù)組的長度和容量不一定相等,可能容量為 10 ,而我們的元素只有 5 。此時序列化的時候就有點浪費資源,序列化和反序列化還是要的,故 ArrayList 自己實現(xiàn)了兩個方法,分別是 writeObject 和 readObject ,用于序列化和反序列化。

對于 HashSet 和  HashMap 來說,底層實現(xiàn)都是依賴于 hash 表,而不同的 JVM 可能算出的 hashCode 值不一致,這樣在跨平臺的時候就會導(dǎo)致序列化紊亂。故也重寫了那兩個方法。借用一句似懂非懂的話:

當(dāng)一個對象的物理表示方法與它的邏輯數(shù)據(jù)內(nèi)容有實質(zhì)性差別時,使用默認(rèn)序列化形式有 N 種缺陷,所以應(yīng)該盡可能的根據(jù)實際情況重寫序列化方法。

對應(yīng)于 Collection ,有一個 Collections 工具類,其中提供很多方法,比如說集合的排序,求子集合,最大值,最小值,交換,填充,打亂集合等等,還記得上面說到的實現(xiàn)類中存在線程不安全的情況吧,這個工具類中提供很多對應(yīng)的 synchronized 的方法。

關(guān)于Java中怎么利用Collection實現(xiàn)集合就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

分享名稱:Java中怎么利用Collection實現(xiàn)集合
標(biāo)題來源:http://muchs.cn/article36/pgogpg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制網(wǎng)站、移動網(wǎng)站建設(shè)、外貿(mào)建站ChatGPT、商城網(wǎng)站網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

微信小程序開發(fā)