`
talentkep
  • 浏览: 100500 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

Java类库中的集合类解析

    博客分类:
阅读更多

这篇我准备从源码的高度来看看集合中各个实现类的是如何组织我们存进去的数据的,主要包括Java类库中提供的几个具体的类:
LinkedList
ArrayList
HashMap
HashSet
TreeMap
TreeSet
PriorityQueue(顺序按下面的讲解顺序)


-----------------------------------------------------------------------------------------------------
1、java.util.LinkedList<E>
当我们创建一个LinkedList类的对象,并且试图增加一个新的元素的时候,到底是如何组织我们传进去的数据的呢?

Java代码 复制代码
  1. //创建一个LinkedList类型的对象   
  2. java.util.LinkedList<String> l=new java.util.LinkedList<String>();   
  3. l.add(e);//e为E类的对象  
//创建一个LinkedList类型的对象
java.util.LinkedList<String> l=new java.util.LinkedList<String>();
l.add(e);//e为E类的对象



打开add方法的源码看看:

Java代码 复制代码
  1. public boolean add(E e) {   
  2. //调用LinkedList的私有方法   
  3. //header是LinkedList中的一个属性,这样定义的private transient Entry<E> //header = new Entry<E>(null, null, null);   
  4.   
  5.     addBefore(e, header);           
  6. return true;   
  7.     }   
  8. //被调用的私有方法   
  9. private Entry<E> addBefore(E e, Entry<E> entry) {   
  10.     Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);   
  11.     newEntry.previous.next = newEntry;   
  12.     newEntry.next.previous = newEntry;   
  13.     size++;   
  14.     modCount++;   
  15.     return newEntry;   
  16.     }   
  17. //Entry<E>是LinkedList的内部类,包装每一个E类型的对象e,形成一个链表   
  18. private static class Entry<E> {   
  19.     E element;   
  20.     Entry<E> next;   
  21.     Entry<E> previous;   
  22.   
  23.     Entry(E element, Entry<E> next, Entry<E> previous) {   
  24.         this.element = element;   
  25.         this.next = next;   
  26.         this.previous = previous;   
  27.     }   
  28.     }  
public boolean add(E e) {
//调用LinkedList的私有方法
//header是LinkedList中的一个属性,这样定义的private transient Entry<E> //header = new Entry<E>(null, null, null);

	addBefore(e, header);        
return true;
	}
//被调用的私有方法
private Entry<E> addBefore(E e, Entry<E> entry) {
	Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
	newEntry.previous.next = newEntry;
	newEntry.next.previous = newEntry;
	size++;
	modCount++;
	return newEntry;
	}
//Entry<E>是LinkedList的内部类,包装每一个E类型的对象e,形成一个链表
private static class Entry<E> {
	E element;
	Entry<E> next;
	Entry<E> previous;

	Entry(E element, Entry<E> next, Entry<E> previous) {
	    this.element = element;
	    this.next = next;
	    this.previous = previous;
	}
    }



我们惊喜的发现,原来就是把我们传去的e对象包装成了Entry<E>,然后通过Entry<E>的next和previous两个属性形成了一个以包装后的e对象(即Entry<E>)为节点的双向链表。
于是我们彻底明白了LinkedList果然名副其实,就是一个链表嘛!



-----------------------------------------------------------------------------------------------------
2、java.util.ArrayList<E>


我们看看在ArrayList对象调用add();方法时,底层到底是如何执行的

Java代码 复制代码
  1. public boolean add(E e) {   
  2.     ensureCapacity(size + 1);  // size是ArrayList中元素的个数   
  3.     elementData[size++] = e;   //在调整后的elementData末尾加入新的元素   
  4.     return true;   
  5.     }   
  6.  public void ensureCapacity(int minCapacity) {   
  7.     modCount++;   
  8.     //elementData就是ArrayList中一个数组类型的属性,用来放进去的元素:    //Object[] elementData   
  9.     int oldCapacity = elementData.length;   
  10.     if (minCapacity > oldCapacity) {//原来的elementData空间不够用了!   
  11.         Object oldData[] = elementData;   
  12.         int newCapacity = (oldCapacity * 3)/2 + 1;   
  13.        //如果通过oldCapacity 计算出的新空间依然不够用   
  14.     if (newCapacity < minCapacity)          
  15.     newCapacity = minCapacity;   
  16.             // minCapacity is usually close to size, so this is a win:   
  17.     //这一步最后会调用System.arraycopy(original, 0, copy, 0,   
  18.                              Math.min(original.length, newLength));   
  19.     //来实现将所有的元素copy到长度更大的数组中,这一步将很费时间   
  20.          elementData = Arrays.copyOf(elementData, newCapacity);   
  21.     }   
  22.     }  
public boolean add(E e) {
	ensureCapacity(size + 1);  // size是ArrayList中元素的个数
	elementData[size++] = e;   //在调整后的elementData末尾加入新的元素
	return true;
    }
 public void ensureCapacity(int minCapacity) {
	modCount++;
	//elementData就是ArrayList中一个数组类型的属性,用来放进去的元素:	//Object[] elementData
	int oldCapacity = elementData.length;
	if (minCapacity > oldCapacity) {//原来的elementData空间不够用了!
	    Object oldData[] = elementData;
	    int newCapacity = (oldCapacity * 3)/2 + 1;
       //如果通过oldCapacity 计算出的新空间依然不够用
	if (newCapacity < minCapacity)		
	newCapacity = minCapacity;
            // minCapacity is usually close to size, so this is a win:
	//这一步最后会调用System.arraycopy(original, 0, copy, 0,
	                         Math.min(original.length, newLength));
	//来实现将所有的元素copy到长度更大的数组中,这一步将很费时间
         elementData = Arrays.copyOf(elementData, newCapacity);
	}
	}



于是我们发现:原来ArrayList也是如名字说的,用Array组织数据。不过它内部定义的那个调整elementData数组的方法copy太多,显然当数据量大的时候,性能不会很好。



-----------------------------------------------------------------------------------------------------
3、java.util.HashMap<K,V>

Java代码 复制代码
  1. //向HashMap中插入键值对   
  2.      public V put(K key, V value) {   
  3.              if (key == null)   //如果没有输入的key是null值   
  4.                  return putForNullKey(value);//插在Entry[0]的第一个,返回null   
  5.         //获得哈希码   
  6.         //1、首先用key类定义的hashcode()方法计算得到一个int   
  7.         //2、进行一些>>>和^的操作   
  8.              int hash = hash(key.hashCode());   
  9.         //通过&运算将hash按二进制位取反(1变为0,0变为1)   
  10.         //得到要插入的元素在table中的index   
  11.              int i = indexFor(hash, table.length);   
  12.            
  13.         //遍历table[i]数据元下拖带的一个链表的所有元素   
  14.              for (Entry<K,V> e = table[i]; e != null; e = e.next) {   
  15.                  Object k;   
  16.             //如果有一个已经存在的元素的哈希码"=="为true,   
  17.             //并且key值"=="或者"equals"为true   
  18.             //也就是所谓的key经过hashcode()的一系列运算和   
  19.            //equals()的一系列运算相同的元素,就替换原来的value   
  20.                  if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {   
  21.                      V oldValue = e.value;   
  22.                      e.value = value;   
  23.                      e.recordAccess(this);   
  24.                      return oldValue;   
  25.                  }   
  26.              }   
  27.   
  28.              modCount++;   
  29.         //把原来在table[i]位置的元素挤到Entry<K,V>的next位置   
  30.              addEntry(hash, key, value, i);   
  31.              return null;   
  32.          }   
  33.   }  
//向HashMap中插入键值对
	 public V put(K key, V value) {
	         if (key == null)   //如果没有输入的key是null值
	             return putForNullKey(value);//插在Entry[0]的第一个,返回null
	 	//获得哈希码
	 	//1、首先用key类定义的hashcode()方法计算得到一个int
	 	//2、进行一些>>>和^的操作
	         int hash = hash(key.hashCode());
	 	//通过&运算将hash按二进制位取反(1变为0,0变为1)
	 	//得到要插入的元素在table中的index
	         int i = indexFor(hash, table.length);
	 	
	 	//遍历table[i]数据元下拖带的一个链表的所有元素
	         for (Entry<K,V> e = table[i]; e != null; e = e.next) {
	             Object k;
	 	    //如果有一个已经存在的元素的哈希码"=="为true,
	 	    //并且key值"=="或者"equals"为true
	 	    //也就是所谓的key经过hashcode()的一系列运算和
	 	   //equals()的一系列运算相同的元素,就替换原来的value
	             if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
	                 V oldValue = e.value;
	                 e.value = value;
	                 e.recordAccess(this);
	                 return oldValue;
	             }
	         }

	         modCount++;
	 	//把原来在table[i]位置的元素挤到Entry<K,V>的next位置
	         addEntry(hash, key, value, i);
	         return null;
	     }
  }



想必大家看这段代码都看到晕了吧,为了让大家能够更加形象的人知道HashMap对数据的的组织形式,上了一个HaspMap数据结构图:



这里解释一下,这个图的最左边的一些就是上面源码中的table也就是HashMap的一个属性Entry[] table。将一个新的键值对插入需要经过这几步:
---给key值计算哈码(计算在这一步int hash = hash(key.hashCode());),
    ---得出在table数组的中index:int i = indexFor(hash, table.
length);
---将键值对插入index确定的上图所示的一个横向的链表中。如果在这个链表中有要插入的pair的key经过hashcode()的一系列运算和equals()的一系列运算相同的元素,就替换原来的value。(这也就是我们自己定义的类要用到HashMap存储的时候,必须重写hashcode()和equals()方法,并且要保证对同一对象两个方法计算结果要相同的原因。因为如果不相同,在一个同一对象为key插入值的时候就不会像你期望的那样后插入的value覆盖前面的value了,而是会重新开辟一个空间存储)

于是,到这里我们明白了,原来HashMap就是通过散列表这种数据结构组织数据的


-----------------------------------------------------------------------------------------------------
4、java.util.HashSet<E>

Java代码 复制代码
  1. public boolean add(E e) {   
  2.     //map是该类的一个属性,这样定义的:HashMap<E,Object> map   
  3.     //这里e作为key了   
  4.     //value用本类的属性代替private static final Object PRESENT = new Object();每个键值对都相同   
  5.     return map.put(e, PRESENT)==null;   
  6.     }  
public boolean add(E e) {
	//map是该类的一个属性,这样定义的:HashMap<E,Object> map
	//这里e作为key了
	//value用本类的属性代替private static final Object PRESENT = new Object();每个键值对都相同
	return map.put(e, PRESENT)==null;
	}



小样直接自己不解决,抛给HashMap类的put()方法,也就是用一个散列表存数据。详解见第三条对HashMap的讲解


-----------------------------------------------------------------------------------------------------
5、java.util.TreeMap<E>

Java代码 复制代码
  1. public V put(K key, V value) {   
  2.         Entry<K,V> t = root;//root是整棵树的根节点   
  3.         if (t == null) {   
  4.         //插入的第一个元素会成为根节点   
  5.             root = new Entry<K,V>(key, value, null);   
  6.             size = 1;   
  7.             modCount++;   
  8.             return null;   
  9.         }   
  10.         int cmp;   
  11.         Entry<K,V> parent;   
  12.         // 调用Comparator的compare()方法确定新加的元素出现的位置。   
  13.     //我们可以再自己定义的类中实现Comparator接口,然后传给树集的构造器。从而按照自己定义的不同的比较规则来给整个树的数据进行排序。   
  14.         Comparator<? super K> cpr = comparator;   
  15.         if (cpr != null) {   
  16.             do {   
  17.                 parent = t;   
  18.                 cmp = cpr.compare(key, t.key);   
  19.                 if (cmp < 0)   
  20.                     t = t.left;   
  21.                 else if (cmp > 0)   
  22.                     t = t.right;   
  23.                 else  
  24.                     return t.setValue(value);   
  25.             } while (t != null);   
  26.         }   
  27.         else {   
  28.             if (key == null)   
  29.                 throw new NullPointerException();   
  30.             Comparable<? super K> k = (Comparable<? super K>) key;   
  31.             do {   
  32.                 parent = t;   
  33.                 cmp = k.compareTo(t.key);   
  34.                 if (cmp < 0)   
  35.                     t = t.left;   
  36.                 else if (cmp > 0)   
  37.                     t = t.right;   
  38.                 else  
  39.                     return t.setValue(value);   
  40.             } while (t != null);   
  41.         }   
  42.     //这里我们将传进来的数据包装成Entry<K,V> ,通过Entry<K,V> 内部类的//属性 Entry<K,V> parent来组织一棵树   
  43.         Entry<K,V> e = new Entry<K,V>(key, value, parent);   
  44.         if (cmp < 0)   
  45.             parent.left = e;   
  46.         else  
  47.             parent.right = e;   
  48.         fixAfterInsertion(e);   
  49.         size++;   
  50.         modCount++;   
  51.         return null;   
  52.     }  
public V put(K key, V value) {
        Entry<K,V> t = root;//root是整棵树的根节点
        if (t == null) {
	    //插入的第一个元素会成为根节点
            root = new Entry<K,V>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        // 调用Comparator的compare()方法确定新加的元素出现的位置。
	//我们可以再自己定义的类中实现Comparator接口,然后传给树集的构造器。从而按照自己定义的不同的比较规则来给整个树的数据进行排序。
        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);
        }
	//这里我们将传进来的数据包装成Entry<K,V> ,通过Entry<K,V> 内部类的//属性 Entry<K,V> parent来组织一棵树
        Entry<K,V> e = new Entry<K,V>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
	}



我们又可以开心的大笑了,原来就是如此简单,就是按照一定的规律形成一棵二叉树来存数据。
大笑过后,我们再次静下心来观察,源码中出现了这样一句:k.compareTo(t.key);是说用key对应的类中实现的compareTo()方法来判断两个key的先后顺序。有若干标准的java平台类都实现了Compatable接口(Compatator可以自己定义不同的比较规则,不过这个接口的比较规则只有一个,是定义key的类的时候定义的,没有可变性),如String类:

Java代码 复制代码
  1. //用字典式排序。不展开分析了。   
  2.  public int compareTo(String anotherString) {   
  3.     int len1 = count;   
  4.     int len2 = anotherString.count;   
  5.     int n = Math.min(len1, len2);   
  6.     char v1[] = value;   
  7.     char v2[] = anotherString.value;   
  8.     int i = offset;   
  9.     int j = anotherString.offset;   
  10.   
  11.     if (i == j) {   
  12.         int k = i;   
  13.         int lim = n + i;   
  14.         while (k < lim) {   
  15.         char c1 = v1[k];   
  16.         char c2 = v2[k];   
  17.         if (c1 != c2) {   
  18.             return c1 - c2;   
  19.         }   
  20.         k++;   
  21.         }   
  22.     } else {   
  23.         while (n-- != 0) {   
  24.         char c1 = v1[i++];   
  25.         char c2 = v2[j++];   
  26.         if (c1 != c2) {   
  27.             return c1 - c2;   
  28.         }   
  29.         }   
  30.     }   
  31.     return len1 - len2;   
  32.         }  
//用字典式排序。不展开分析了。
 public int compareTo(String anotherString) {
	int len1 = count;
	int len2 = anotherString.count;
	int n = Math.min(len1, len2);
	char v1[] = value;
	char v2[] = anotherString.value;
	int i = offset;
	int j = anotherString.offset;

	if (i == j) {
	    int k = i;
	    int lim = n + i;
	    while (k < lim) {
		char c1 = v1[k];
		char c2 = v2[k];
		if (c1 != c2) {
		    return c1 - c2;
		}
		k++;
	    }
	} else {
	    while (n-- != 0) {
		char c1 = v1[i++];
		char c2 = v2[j++];
		if (c1 != c2) {
		    return c1 - c2;
		}
	    }
	}
	return len1 - len2;
	    }


所以,我们自己定义key的类的时候,要特别注意compareTo()方法中算法的选择,以便有一个最好的插入、查找、遍历的性能。一般而言将元素添加到树集的速度快于数组和链表,慢于散列表(素服比较:数组、链表<树集<散列表)。



-----------------------------------------------------------------------------------------------------
6、java.util.TreeSet<E>

Java代码 复制代码
  1. public boolean add(E e) {   
  2.     return m.put(e, PRESENT)==null;   
  3.     }  
public boolean add(E e) {
	return m.put(e, PRESENT)==null;
	}


相信大家看到源码立马就能明白了吧,向HashSet一样TreeSet也偷懒了(至于为什么要偷懒,感兴趣的朋友可以去研究,这里不展开了),也是用二叉树的结构存数据,不多说!



-----------------------------------------------------------------------------------------------------
7、java.util.PriorityQueue<E>
(这一条有错,详解见附)

Java代码 复制代码
  1. public boolean add(E e) {   
  2.         return offer(e);   
  3.     }   
  4. public boolean offer(E e) {   
  5.         if (e == null)   
  6.             throw new NullPointerException();   
  7.         modCount++;   
  8.         int i = size;   
  9.         if (i >= queue.length)//属性:Object[] queue   
  10.             grow(i + 1);   
  11.         size = i + 1;   
  12.         if (i == 0)   
  13.             queue[0] = e;   
  14.         else  
  15.             siftUp(i, e);   
  16.         return true;   
  17.     }  
public boolean add(E e) {
        return offer(e);
	}
public boolean offer(E e) {
        if (e == null)
            throw new NullPointerException();
        modCount++;
        int i = size;
        if (i >= queue.length)//属性:Object[] queue
            grow(i + 1);
        size = i + 1;
        if (i == 0)
            queue[0] = e;
        else
            siftUp(i, e);
        return true;
	}



一看就明白,就是通过数组组织数据。不过喜欢刨根问底的朋友又会提出一个问题了:
既然和ArrayList一样都是数组组织数据,那干嘛还要存在这个类呢?
问的好!继续看:
PriorityQueue类在数组满了的时候(代码为i >= queue.length),就调用grow(i + 1)这个方法来调整queue的长度。具体调整的算法如下

Java代码 复制代码
  1. private void grow(int minCapacity) {   
  2.         if (minCapacity < 0// overflow   
  3.             throw new OutOfMemoryError();   
  4.     int oldCapacity = queue.length;   
  5.         // Double size if small; else grow by 50%   
  6.         int newCapacity = ((oldCapacity < 64)?   
  7.                            ((oldCapacity + 1) * 2):   
  8.                            ((oldCapacity / 2) * 3));   
  9.         if (newCapacity < 0// overflow   
  10.             newCapacity = Integer.MAX_VALUE;   
  11.         if (newCapacity < minCapacity)   
  12.             newCapacity = minCapacity;   
  13.         queue = Arrays.copyOf(queue, newCapacity);   
  14.     }  
private void grow(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
	int oldCapacity = queue.length;
        // Double size if small; else grow by 50%
        int newCapacity = ((oldCapacity < 64)?
                           ((oldCapacity + 1) * 2):
                           ((oldCapacity / 2) * 3));
        if (newCapacity < 0) // overflow
            newCapacity = Integer.MAX_VALUE;
        if (newCapacity < minCapacity)
            newCapacity = minCapacity;
        queue = Arrays.copyOf(queue, newCapacity);
	}



而ArrayList一上来就调用方法调整了:ensureCapacity(size + 1);里面的具体算法这里就不列出来了。两个类调整的算法不同。这就造成了两者性能上差别。

tip:好了,今天就分析道这里了。进一步的研究,等过段时间才能出来,到时候再贴出来。时间仓促,难免有漏洞,大家多提意见。
另外抱怨一下JE的编辑器,真不好用,害得我重新录入!


纠错:感谢大家能及时反馈给我一些有用的信息,就不一一回复了。就不在原来的文章里改错了,把错误的纠正全写在这后面了,再次感谢!

-----------------------------------------------------------------------------------------------------
PriorityQueue<E>重新写了一份:


我们看看调用add()方法在底层到底发生了什么事情!

Java代码 复制代码
  1. public boolean add(E e) {   
  2.         return offer(e);   
  3.     }   
  4.   
  5. public boolean offer(E e) {   
  6. //前面这的几行无非就是判断非空,判断本类的属性queue的长度是否够用然后做相应调整   
  7.         if (e == null)   
  8.             throw new NullPointerException();   
  9.         modCount++;   
  10.         int i = size;   
  11.         if (i >= queue.length)   
  12.             grow(i + 1);   
  13.         size = i + 1;   
  14. //最后终于要将元素插进去了   
  15. //如果queue空就插在index为0的位置,很好理解   
  16. //否则调用siftUp()方法(第一个参数是the position to fill,第二个参数是the element to insert)   
  17.         if (i == 0)   
  18.             queue[0] = e;   
  19.         else  
  20.             siftUp(i, e);   
  21.         return true;   
  22.     }   
  23. //再来看看siftUp()方法是如何实现的   
  24. //api文档的注释的意思是:将x插入合适的位置保持heap的有序性不变   
  25. //排序标准有两种途径获取:   
  26. //1、在构造PriorityQueue的时候传入的Comparator ,这个优先选用   
  27. //2、 要插入的x自己实现的compareTo方法   
  28. private void siftDown(int k, E x) {   
  29.         if (comparator != null)   
  30.             siftDownUsingComparator(k, x);   
  31.         else  
  32.             siftDownComparable(k, x);   
  33.     }   
  34.   
  35. //这里我只需分析comparator的情况就可以了   
  36. private void siftUpUsingComparator(int k, E x) {   
  37. //最坏的情况是:我找了一圈发现x才是整棵树种最小的。这时k为0,也就是到达整个堆的最小的元素(或者整棵树的根节点),停止循环。           
  38. while (k > 0) {   
  39.     //第一句的意思是获得要插入的这个k位置在queue中对应的父元素的索引   
  40.     //我可以告诉大家这个式子的计算结果是:queue[n]节点的子节点是queue[2*n+1]和queue[2*(n+1)]   
  41.             int parent = (k - 1) >>> 1;   
  42.             Object e = queue[parent];   
  43.     //如果比较规则确定x"大于"父节点,就插在k位置了,跳出循环   
  44.             if (comparator.compare(x, (E) e) >= 0)   
  45.                 break;   
  46.     //如果发现x较小,就将父节点的元素移到这个k位置   
  47.             queue[k] = e;   
  48.             k = parent;//现在要插入的位置变为原来父节点的位置   
  49.         }   
  50.         queue[k] = x;//   
  51.     }  
public boolean add(E e) {
        return offer(e);
	}

public boolean offer(E e) {
//前面这的几行无非就是判断非空,判断本类的属性queue的长度是否够用然后做相应调整
        if (e == null)
            throw new NullPointerException();
        modCount++;
        int i = size;
        if (i >= queue.length)
            grow(i + 1);
        size = i + 1;
//最后终于要将元素插进去了
//如果queue空就插在index为0的位置,很好理解
//否则调用siftUp()方法(第一个参数是the position to fill,第二个参数是the element to insert)
        if (i == 0)
            queue[0] = e;
        else
            siftUp(i, e);
        return true;
	}
//再来看看siftUp()方法是如何实现的
//api文档的注释的意思是:将x插入合适的位置保持heap的有序性不变
//排序标准有两种途径获取:
//1、在构造PriorityQueue的时候传入的Comparator ,这个优先选用
//2、 要插入的x自己实现的compareTo方法
private void siftDown(int k, E x) {
        if (comparator != null)
            siftDownUsingComparator(k, x);
        else
            siftDownComparable(k, x);
	}

//这里我只需分析comparator的情况就可以了
private void siftUpUsingComparator(int k, E x) {
//最坏的情况是:我找了一圈发现x才是整棵树种最小的。这时k为0,也就是到达整个堆的最小的元素(或者整棵树的根节点),停止循环。        
while (k > 0) {
	//第一句的意思是获得要插入的这个k位置在queue中对应的父元素的索引
	//我可以告诉大家这个式子的计算结果是:queue[n]节点的子节点是queue[2*n+1]和queue[2*(n+1)]
            int parent = (k - 1) >>> 1;
            Object e = queue[parent];
	//如果比较规则确定x"大于"父节点,就插在k位置了,跳出循环
            if (comparator.compare(x, (E) e) >= 0)
                break;
	//如果发现x较小,就将父节点的元素移到这个k位置
            queue[k] = e;
            k = parent;//现在要插入的位置变为原来父节点的位置
        }
        queue[k] = x;//
	}


嗯,这个类用了一种“堆”(逻辑上是二叉树,存储上用数组,树中的元素有大小关系,越小在数组中的index也越小)的数据结构。
典型应用是存储有优先级的任务,因为每次调用remove移除最小的元素(优先级最高的元素),都会自动排序,保证每次移除的都是优先级最高的任务。
同样,TreeMap逻辑上也是通过有序二叉树来组织数据的,不过,TreeMap通过节点的链接来组织存储结构,而PriorityQueue是通过数组的一些列计算确定逻辑上的树的节点的存放位置

分享到:
评论

相关推荐

    Java类库中文手册

    Java类库由许多模块组成,包括核心类库、集合框架、I/O流、网络编程、多线程、反射、数据库连接(JDBC)、XML处理等。 1. **核心类库**:这是Java语言的基础,包含`java.lang`、`java.util`、`java.io`等包,提供...

    Java类库下载Q

    Java类库下载Q指的是一个包含多种常用Java类库的集合,提供了丰富的功能,使得Java开发者可以快速地集成到自己的项目中,提升开发速度。"Q"可能代表这个集合的特定版本或者特性。 在Java编程中,类库通常分为以下几...

    java类库详解PDF格式的

    Java 类库是 Java 编程语言的核心组成部分,它提供了丰富的接口和类,使得开发者能够高效地构建复杂的软件系统。这份“Java 类库详解”的 PDF 文档集合,显然是一份详尽的学习资料,涵盖了 Java 类库的多个方面。...

    java2类库 java类库的源文件

    2. **集合框架**:`java.util` 包包括了各种集合类,如 `ArrayList`、`LinkedList`、`HashMap` 和 `HashSet`,以及迭代器、泛型等高级概念。集合框架是 Java 中处理对象数组的重要工具。 3. **网络编程**:`java...

    经典java例子,涉及到大部分java类库的使用.rar

    在"超级经典java例子,涉及到大部分java类库的使用"这个压缩包中,你将有机会看到这些类库的实际应用,从而加深对Java编程的理解。通过阅读和运行这些示例,你可以更熟练地掌握Java类库,提升你的编程技能。记住,...

    Java类库大全

    Java类库大全是一个集合了众多Java开发中常用和实用类库的资源集合,旨在为开发者提供便利,提高开发效率。这个资源包包含了丰富的Java库,覆盖了从基础数据类型操作到复杂的网络通信、多线程处理、数据库操作、图形...

    java类库

    这个压缩包文件"egs"很可能包含了示例代码或者练习题目,用于帮助理解Java类库的使用。 Java API主要分为几个部分: 1. **基础类库**:包括`java.lang`包,这是每个Java程序的基础,包含诸如`Object`、`String`、`...

    java类库源码

    在本文中,我们将探讨几个重要的Java类库组件,并分析它们在JDK1.6中的作用。 1. **java** 包:这是Java最核心的包,包含了所有基本类型的包装类(如Integer, Boolean)以及基础的集合框架(如List, Set, Map)。...

    WEBSerVice客户端AXIS解析JAVA类库.以及客户代码生成BAT文件

    首先,你需要对标题中的"WEBSerVice客户端AXIS解析JAVA类库"有一个基本理解。Web服务是基于开放标准(如SOAP和WSDL)的接口,允许不同平台的应用程序交换数据。AXIS是Java中的一个工具集,它提供了Web服务客户端和...

    java常用类库中文速查表

    本知识点将详细介绍几个常用的Java类库,包括它们的功能、应用场景以及相应的下载地址。 首先,我们需要了解基础类库Commons Lang,它提供了对JDK中java.lang包的补充,提供了各种各样的Util工具类,简化了很多常用...

    java常用类库源码

    9. **字符串处理**:String类是Java中最常用的类之一,源码解析可以帮助我们理解字符串的不可变性、substring、replace等方法的实现。 10. **枚举和注解**:Java枚举提供了一种安全的常量表示,而注解则提供了一种...

    json类库,Java解析json必用

    本篇文章将深入探讨Java中解析和生成JSON的类库,并介绍如何使用它们进行数据转换。 ### JSON的基本结构 JSON基于JavaScript语法,但并不依赖JavaScript执行环境。其基本结构包括对象(Object)和数组(Array)。...

    java核心类库使用大全

    2. **集合框架**:涵盖了各种集合类的创建、操作和遍历,以及泛型和迭代器的概念。 3. **异常处理**:介绍了如何使用try-catch-finally语句,以及自定义异常。 4. **多线程**:讲解线程的创建、同步、通信和管理,...

    java类库详解

    这个“java类库详解”旨在深入解析这些类库,帮助你理解和掌握Java中的各类类的使用,从而进一步深化对Java编程思想的理解。 1. **基础类库** - **Object类**: 所有Java类的根类,包含equals()、hashCode()和...

    JAVA2 SDK 类库.rar_java 类库

    Java类库是Java平台的核心组成部分,它提供了大量的预先编写好的类和接口,供开发者在编写Java程序时直接使用。这些类库覆盖了网络通信、I/O操作、多线程、数据库连接、XML处理、图形用户界面(GUI)设计等多个领域...

    Java标准类库PDF

    在Java标准类库中,`java.lang`包是非常核心的部分之一,它包含了运行Java程序所必需的一些基本类,例如`Object`类、基本数据类型的包装类以及其他一些常用类如`System`、`Runtime`和`Math`等。 #### 二、基本数据...

    java开发常用的类库集合

    Java开发常用的类库集合是Java开发者在构建应用时不可或缺的部分,它们提供了丰富的功能,提高了开发效率。以下将详细解析这些类库以及它们在Java开发中的作用。 1. JDBC(Java Database Connectivity):JDBC是...

    超全的Java类库祥解

    本资源“超全的Java类库祥解”是一个全面的指南,旨在深入解析Java类库中的关键组件,帮助开发者更好地理解和利用这些工具。 在Java类库中,最基础的部分是Java Standard Edition (Java SE) 的核心类库。这些类库...

Global site tag (gtag.js) - Google Analytics