深入理解java集合的底层操作

集合:层次结构:

(1)Collection (接口)其常用子接口 Set    List  Queue 

Set 的子接口和其实现类如下

(一)EnumSet (抽象类)  implements  Set   

(二)SortedSet(接口)     exntends  Set   

(三)HashSet                       implements  Set  

 

(一.1)EnumSet的元素加入存储的机制是:当创建EnumSet对象 EnumSet e=new EnumSet(Season.class)时就把Season类型的枚举值赋给EnumSet属性final Enum[] universe了;进行add(Object o)时,先调用typeCheck(e)进行类型检查,如类型不是Season或者其父类时则抛出异常,然后 return elements != oldElements;实际上没有加入任何元素,所以EnumSet中只可以存放相同枚举类型的对象,否则会抛出类型转换异常,

add的源代码如下:

public boolean add(E e) {
        typeCheck(e);

        long oldElements = elements;
        elements |= (1L << ((Enum)e).ordinal());
        return elements != oldElements;
    }

删除时实际也没有删除任何元素:代码如下

public boolean remove(Object e) {
        if (e == null)
            return false;
        Class eClass = e.getClass();
        if (eClass != elementType && eClass.getSuperclass() != elementType)
            return false;

        long oldElements = elements;
        elements &= ~(1L << ((Enum)e).ordinal());
        return elements != oldElements;
    }

 

(二.1)TreeSet                     implements    SortedSet

(三.1)LinkedHashSet       exntends  HashSet 

HashSet类中加入元素(可以加入不同类型的元素)的机制是根据其元素的equals(Object o)方法和hashCode()方法来判断是否能够加入新的元素,HashSet中的元素是无序的

LinkedHashSet的原理和HashSet的相同,只是LinkedHashSet是有序的,先加入的排在前面,底层通过链表来维护这种顺序。

由于HashSet和LinkedHashSet类加入和删除元素的机制是根据add(Object obj)加入对象obj的hashCode()的hash值和equals(Object o)进行比较是否可加入新元素或者存在该元素,由于所有的类的hash值都是整数,是可比较的,并且所有类默认的equals(Object o)方法都可以与任何类型的对象进行比较,如果是与不同类型的对象进行比较时,返回的值是false,所以HashSet和LinkedHashSet类中可加入不同类型的元素,如:

package set;

import java.util.HashSet;
import java.util.Iterator;

public class hashset
{
public static void main(String[] args)
{
 HashSet h=new HashSet();
 h.add("hello");
 h.add("world");
 h.add("good");
 h.add("ddd");
 h.add("aaa");
 h.add("hello");
 h.add(new hashset());
 h.add(new Integer(4));
 System.out.println(new hashset().hashCode());
 Iterator it=h.iterator();
 while(it.hasNext())
 {
  System.out.println(it.next());
 }
}
}

即可加入String类型也可加入hashsett和Integer类型

TreeSet类中加入元素(元素必须是同种类型)的机制是根据其元素对应的类中实现Comparable接口的compareto(Object o)方法来判断是否能够加入新的元素,如果该元素对应的类中没有实现该方法,则TreeSet中能加入一个元素,TreeSet默认是根据compareto(Object  o)来对元素进行升序排序,也可以实现定制排序

如 TreeSet t=new TreeSet(new Comparator()
 {
     @Override
     public int compare(R o1, R o2)
     {
              return o1.count>o2.count?-1:o1.count      }
 });

TreeSet的存储机制是用红黑树进行存储的,如下代码的存储:

package test;

import java.math.BigDecimal;
import java.util.TreeSet;
class R implements Comparable
{
 int count;
 public R(int count)
 {
  this.count=count;
  
 }
 public String toString()
 {
  return "R[count:"+count+"]";
 }
 public boolean equals(Object o)
 {
  if(this==o)
   return true;
  if(o!=null&&o.getClass()==R.class)
  {
   R r=(R)o;
   if(this.count==r.count)
    return true;
  }
   return false;
 }
 @Override
 public int compareTo(Object o)
 {
            R r=(R)o;
           return count>r.count?1:count  }
}

public class Test
{

public static void main(String[] args)
{
    TreeSet t=new TreeSet();
    t.add(new R(5));
    t.add(new R(-3));
    t.add(new R(9));
    t.add(new R(-2));
    System.out.println(t);  (1)
    R first=t.first();
    first.count=20;
    R last=t.last();
    last.count=-2;           
   
    System.out.println(t);   (2)
    System.out.println(t.remove(new R(-2)));
    System.out.println(t);
    System.out.println(t.remove(new R(5)));
    System.out.println(t);
    System.out.println(t.remove(new R(-2)));
    System.out.println(t);
}
}
(1)之前的红黑树是这样的:

            5

-3                   9

        -2

(1)-->(2)树添加修改稳定之后,形成的红黑树是这样的:

          5

20              -2
      -2

第二层-2是20的右子节点,因为删除时循根搜索,-2一直往左路搜,最后搜到20左子节点为空,所以删除失败,待5删除之后,重新平衡了树结构:

     -2

20      -2

因此-2就能被删除

TreeSet中删除元素的原理t.remove(Object obj) :

通过调用obj对象对应类中的obj.compareTo(Object obj)方法,按照红黑树搜索的方法来查找,如果存在obj.compareTo(Object obj1)返回值等于0,则删除掉该obj1元素,否则删除不成功

TreeSet中加入不同类型元素的异常问题

package set;

import java.util.Iterator;
import java.util.TreeSet;

public class treeset implements Comparable
{
 @Override
  public int compareTo(Object o)
  {
  return 1;
  }
 public static void main(String[] args)
{
 TreeSet t=new TreeSet();
 t.add(new Integer(2));//由于还没有元素,所有可以加入
 t.add(new Integer(3));//由于t中只存在Integer类型元素,所以可以继续加入
 t.add(new treeset());//虽然集合中存在Integer类型元素,但由于treeset中实现了comparaTo(Object o)方法该方法的返回值总是1,所有总是可比较的
 //t.add(new String());//出现异常,因为String 中实现的comparaTo(Object o)方法,只可以和String类型比较,由于集合中存在其他类型(Integer和treeset类型),所有抛出类型转换异常
}

List 的子接口和其实现类如下   List有序,可重复,可以获得ListIterator it=al.listIterator();ListIterator迭代器,可进行向前迭代

(一)LinkedList  implements List

(二)ArrayList     implements List

(三)Vector         implements List                      Stack extends Vector

 ArrayList 中加入元素的存储机制是根据索引来存储的,每个元素都有一个索引,所以ArrayList中可以加入相同的元素,她们的索引是不同的,并且是有序的,ArrayList删除的原理是根据remove(Object obj)中obj所对应的类的equals(Object o)方法来判断的,如果返回值是true,则删除对应的元素o,由于所有类默认的equals(Object o)方法都可以与任何类型的对象进行比较,如果是与不同类型的对象进行比较时,返回的值是false,所以ArrayList中可加入不同类型的元素,如:

package list;

import java.util.ArrayList;
import java.util.List;

public class arraylist
{
public static void main(String[] args)
{
 List l=new ArrayList();
 ArrayList al=new ArrayList();
 al.add("hello");
 al.add("www");
 al.add(new arraylist());
 al.add(new Integer(4));
 al.add("www");
 al.add(0, "good");
 System.out.println(al.get(3));
 
}
}
即可加入String类型也可加入arraylist和Integer类型

 由于arraylist重写了equals(Object o)方法,并且返回值总是true,所以当remove(Object obj)时总是删除集合中的第一个元素,不管obj是否存在

package list;

import java.util.ArrayList;
import java.util.List;

public class arraylist
{
 public boolean equals(Object o)
 {
  return true;
 }
public static void main(String[] args)
{
 List l=new ArrayList();
 ArrayList al=new ArrayList();
 al.add("hello");
 al.add("www");
 al.add("www");
 al.add(0, "good");
    al.remove(new arraylist());
    al.remove(new arraylist());
    System.out.println(al);
 
}
}

 

Vector的用法和ArrayList几乎完全相同,Vector是古老的方法,Vector中有比较多的重复的方法,Vector是线程安全的,ArrayList是线程不安全的,尽量少用Vector,如果为了线程安全,则可以通过

 ArrayList  ar=(ArrayList)Collections.synchronizedList(new ArrayList());来确保ArrayList的线程安全

Stack实现了Vector类,比Vector类多了三个方法push(Object o),入栈,pop()出栈,peek()查看栈顶元素,由于Stack也是古老的方法,所以尽量少用,如果要用“栈”这种数据结构,则可以用LinkedList类

固定长度的List

package list;

import java.util.Arrays;
import java.util.List;

//Arrays的内部类ArrayList的测试,该List是不可变,只能遍历,不能增加和删除
public class Arrays_ArrayList
{
public static void main(String[] args)
{
 //=Arrays.asList(T... a)返回的是Arrays的内部类ArrayList,而不是由实现了List接口的ArrayList、LinkedList或者Vector
 List l=Arrays.asList("heelo","good");//该集合中的元素是用/Arrays的内部类ArrayList的 private final E[] a属性来存放的;由于a是final修饰的,所以不能删除a中的元素;
 //l.remove(0);抛出异常
 System.out.println(l);
 l.set(0, "wolrd");//可以改里面的元素内容
 
}
}

 

Queue 的子接口和实现类如下:

(一)Deque  extends  Queue   LinkedList implements Deque

 (二)   PriorityQueue  implements Queue

LinkedList 加入是根据索引加入,所以能加入重复元素,删除remove(Object o)是根据元素o所对应的类的equals方法来查找删除的,所以可以加入不同类型的元素

 

PriorityQueue 加入元素的策略是通过队列中元素对应类的compareTo(Object o)方法进行比较,升序排序,当加入的元素比对尾的元素小时则和队尾的元素交换位置,直到找到该元素的存放位置为止,如果加入的元素大于或者等于队尾元素时,则直接把该元素加入到队尾,所以可以加入值相等的元素,由于不同类实现的compareTo(Object o)方法只能进行同种类型对象的比较,所以PriorityQueue 中只能加入同种类型的数据

加入的底层代码实现如下:

PriorityQueue q=new PriorityQueue();
 q.add(8);
 q.add(8);

 public boolean add(E e) {
        return offer(e);
    }

add调用了offer(e)方法

offer(e)的方法如下
 public boolean offer(E e) {
        if (e == null)
            throw new NullPointerException();
        modCount++;
        int i = size;
        if (i >= queue.length)
            grow(i + 1);
        size = i + 1;
        if (i == 0)
            queue[0] = e;
        else
            siftUp(i, e);
       
return true;
    }

  siftUp(i, e)方法如下

private void siftUp(int k, E x) {
        if (comparator != null)
            siftUpUsingComparator(k, x);
        else
            siftUpComparable(k, x);
    }

siftUpComparable(k, x)方法如下

  private void siftUpComparable(int k, E x) {
        Comparable key = (Comparable) x;
        while (k > 0) {
            int parent = (k - 1) >>> 1;
            Object e = queue[parent];
            if (key.compareTo((E) e) >= 0)
                break;
            queue[k] = e;
            k = parent;
        }
        queue[k] = key;
    }

由(key.compareTo((E) e) >= 0)break;
可知。当该元素大于或等于队尾元素时,直接加入该元素,所以可以加入重复的元素

删除则是根据equals方法来查找删除的

你可能感兴趣的:(Java)