??xml version="1.0" encoding="utf-8" standalone="yes"?>555夜色666亚洲国产免,欧美风情在线视频,色综合中文字幕http://www.aygfsteel.com/dongbule/category/47488.html建造高性能门户|?/description>zh-cnWed, 15 Jun 2011 12:18:22 GMTWed, 15 Jun 2011 12:18:22 GMT60java数据l构-Comparable&Comparatorhttp://www.aygfsteel.com/dongbule/archive/2011/06/15/352361.html陈于?/dc:creator>陈于?/author>Wed, 15 Jun 2011 07:07:00 GMThttp://www.aygfsteel.com/dongbule/archive/2011/06/15/352361.htmlhttp://www.aygfsteel.com/dongbule/comments/352361.htmlhttp://www.aygfsteel.com/dongbule/archive/2011/06/15/352361.html#Feedback0http://www.aygfsteel.com/dongbule/comments/commentRss/352361.htmlhttp://www.aygfsteel.com/dongbule/services/trackbacks/352361.html在深入了解TreeMap之前Q有两个接口要先弄清楚,分别是Comparable和Comparator

Comparable & Comparator

Comparable
Comparable 此接口强行对实现它的每个cȝ对象q行整体排序。这U排序被UCؓcȝ自然排序Q类?compareTo Ҏ被称为它的自然比较方法。对象本w就已经支持自比较所需要实现的接口Q如 String、Integer 自己可以完成比较大操作)?br />
它只有一个方?int compareTo(T o),用来比较当前对象和传入的参数Q查看jdk apiQ共有这一批类实现了此接口Q见下图


打开实现了该接口的Integer包装cL看源?/div>
public final class Integer extends Number implements Comparable<Integer> {

    
public int compareTo(Integer anotherInteger) {
    
int thisVal = this.value;
    
int anotherVal = anotherInteger.value;
    
return (thisVal<anotherVal ? -1 : (thisVal==anotherVal ? 0 : 1));
    }

可以看到该方法通过当前的Integer对象的值是否大于传入的参数来得到返回|Integer的compareTo实现是按数字大小的自然排序,再找另一个例子String

public final class String
    
implements java.io.Serializable, Comparable<String>, CharSequence
{
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;
    }

可以看出String的compareTo实现是按字符串中字符的UnicodeD行排序的Q也真是因ؓ各种cd现的不同Q所以只有在怺可比的类才能q行排序Q否则会抛出java.lang.ClassCastException的异常。应该说Comparable是集合内部实现的排序Q想要进行集合外的排序就需要Comparator接口?br />
Comparator
如果一个类不能实现Comparable接口Q那么我们自己可以提供Comparator的排序,如果你不喜欢~省的Comparator行ؓQ照样可以编写自qComparator?br />
Comparator只有2个方法,分别为int compare(T o1, T o2) 和boolean equals(Object obj)

Comparator的compareҎ的返回值和Comparable的compareToq回值基本相|如果排序W一个元素出现在W二个元素之前,则返回一个负|如果W一个元素出现在W二个元素后面,q回一个正|再否则,q回零。与Comparable不同的是Comparator是由用户自行军_处理?br /> 通过一个小例子可以比较明确的看?/div>
      class ComparatorTest implements Comparator{
          
public int compare(Object u1, Object u2) {
            String uname1 
= ((User)u1).getName();
            String uname2 
= ((User)u2).getName();
            
return uname1.compareTo(uname2);
        }
      }
      
class User {
          
private String name;
          
public String getName(){
              
return this.name;
          }
      }

Comparator比较函数Ҏ些对?collection q行整体排序。可以将 Comparator 传递给 sort ҎQ如 Collections.sortQ,从而允许在排序序上实现精控制。还可以使用 Comparator 来控制某些数据结构(?TreeSet ?TreeMapQ的序?/div>

]]>java数据l构-HashMaphttp://www.aygfsteel.com/dongbule/archive/2011/02/15/344387.html陈于?/dc:creator>陈于?/author>Tue, 15 Feb 2011 11:18:00 GMThttp://www.aygfsteel.com/dongbule/archive/2011/02/15/344387.htmlhttp://www.aygfsteel.com/dongbule/comments/344387.htmlhttp://www.aygfsteel.com/dongbule/archive/2011/02/15/344387.html#Feedback6http://www.aygfsteel.com/dongbule/comments/commentRss/344387.htmlhttp://www.aygfsteel.com/dongbule/services/trackbacks/344387.html
下面以HashMapZ子,深入对Map的实现机制进行了解,在这个过E中Q请打开jdk源码?br />
Hash法

HashMap使用Hash法Q所以在解剖HashMap之间Q需要先单的了解Hash法QHash法一般也成ؓ散列法Q通过散列法Q意的D{化成固定的长度输出,该输出就是散列|q是一U压~映,也就是,散列值的I间q远于输入的值空间?br /> 单的_hash法的意义在于提供了一U快速存取数据的Ҏ,它用一U算法徏立键g真实g间的对应关系,(每一个真实值只能有一个键?但是一个键值可以对应多个真实?,q样可以快速在数组{里面存取数据?br />
下面我们建立一个HashMap,然后往里面攑օ12对key-valueQ这个HashMap的默认数l长度ؓ16Q我们的key分别存放在该数组的格子中Q每个格子下面存攄元素又是以链表的方式存放元素?br />
    public static void main(String[] args) {
        Map map 
= new HashMap();
        map.put(
"What""chenyz");
        map.put(
"You""chenyz");
        map.put(
"Don't""chenyz");
        map.put(
"Know""chenyz");
        map.put(
"About""chenyz");
        map.put(
"Geo""chenyz");
        map.put(
"APIs""chenyz");
        map.put(
"Can't""chenyz");
        map.put(
"Hurt""chenyz");
        map.put(
"you""chenyz");
        map.put(
"google""chenyz");
        map.put(
"map""chenyz");
        map.put(
"hello""chenyz");
    }

当我们新d一个元素时Q首先我们通过Hash法计算个元素的Hash值的hashcodeQ通过q个hashcode的|我们可以计出q个新元素应该存攑֜q个hash表的哪个格子里面Q如果这个格子中已经存在元素Q那么就把新的元素加入到已经存在格子元素的链表中?br />
q行上面的程序,我们对HashMap源码q行一点修改,打印出每个key对象的hash?br />
What-->hash|8
You-->hash|3
Don't-->hash|7
Know-->hash|13
About-->hash|11
Geo-->hash|12
APIs-->hash|1
Can't-->hash|7
Hurt-->hash|1
you-->hash|10
google-->hash|3
map-->hash|8
hello-->hash|0

计算出来的Hash值分别代表该key应该存放在Hash表中对应数字的格子中Q如果该格子已经有元素存在,那么该key׃链表的方式依ơ放入格子中



从上表可以看出,Hash表是U性表和链表的l合所得,Ҏ数据l构的定义,可以得出_劣的结论,Hash法的存取速度要比数组差一些,但是比v单纯的链表,在查扑֒存取斚w却要好多?br />
如果要查找一个元素时Q同L方式Q通过Hash函数计算个元素的Hash值hashcodeQ然后通过q个hashcode|直接扑ֈ跟这个hash值相对应的线性格子,q如该格子后Q对q个格子存放的链表元素逐个q行比较Q直到找到对应的hash倹{?br />
在简单了解完Hash法后,我们打开HashMap源码

初始化HashMap

下面我们看看Map map = new HashMap();q段代码I竟做了什么,发生了什么数据结构的变化?br />
HashMap中几个重要的属?br />
transient Entry[] table;
用来保存key-value的对象Entry数组Q也是Hash?br />
transient int size;
q回HashMap的键值对个数

final float loadFactor;
负蝲因子Q用来决定Entry数组是否扩容的因子,HashMap默认?.75f

int threshold;
重构因子Q?capacity * load factor)负蝲因子与Entry[]数组容积的乘?br />
public class HashMap<K,V>
    
extends AbstractMap<K,V>
    
implements Map<K,V>, Cloneable, Serializable
{
    
int threshold;    

    
final float loadFactor;

    
transient Entry[] table;

    
static final float DEFAULT_LOAD_FACTOR = 0.75f;

    
static final int DEFAULT_INITIAL_CAPACITY = 16;

    
public HashMap(int initialCapacity, float loadFactor) {
        
if (initialCapacity < 0)
            
throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        
if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity 
= MAXIMUM_CAPACITY;
        
if (loadFactor <= 0 || Float.isNaN(loadFactor))
            
throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);

        
// Find a power of 2 >= initialCapacity
        int capacity = 1;
        
while (capacity < initialCapacity)
            capacity 
<<= 1;

        
this.loadFactor = loadFactor;
        threshold 
= (int)(capacity * loadFactor);
        table 
= new Entry[capacity];
        init();
    }
以public HashMap(int initialCapacity, float loadFactor)构造函Cؓ例,另外两个构造函数实际上也是以同U方式来构徏HashMap.

首先是要定hashMap的初始化的长度,q里使用的策略是循环查出一个大于initialCapacity?的次方的敎ͼ例如initialCapacity的值是10Q那么大?0的数??ơ方Q也是16Qcapacity的D赋予?6Q那么实际上table数组的长度是16Q之所以采用这L{略来构建Hash表的长度Q是因ؓ2的次方运对于计机来说是有相当的效率?br />
loadFactorQ被UCؓ负蝲因子QHashMap的默认负载因子是0.75f

thresholdQ接下来是重构因子,p载因子和定w的乘机组成,它表C当HashMap元素被存放了多少个之后,需要对HashMapq行重构?br />
通过q一pd的计和定义后,初始化Entry[] table;

put(key,value)

接下来看一对key-value是如何被存放到HashMap中:put(key,value)

    public V put(K key, V value) {
        
if (key == null)
            
return putForNullKey(value);
        
int hash = hash(key.hashCode());
        
        
int i = indexFor(hash, table.length);
        System.out.println(key
+"-->hash|"+i);//q就是刚才程序打印出来的key对应hash?/span>
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue 
= e.value;
                e.value 
= value;
                e.recordAccess(
this);
                
return oldValue;
            }
        }

        modCount
++;
        addEntry(hash, key, value, i);
        
return null;
    }

    
static int hash(int h) {
        h 
^= (h >>> 20^ (h >>> 12);
        
return h ^ (h >>> 7^ (h >>> 4);
    }

    
static int indexFor(int h, int length) {
        
return h & (length-1);
    }

q里是整个hash的关键,h开源码查看一步一步查看?br />
hash(key.hashCode()) 计算出key的hash?//对于hash()的算法,q里有一分析很透彻的文?lt;HashMap hashҎ分析>
indexFor(hash, table.length) 通过一个与法计算出来Q该key应在存放在Hash表的哪个格子中?br /> for (Entry<K,V> e = table[i]; e != null; e = e.next) 然后再遍历table[i]g的链表,判断是否已经存在一LkeyQ如果存在一Lkey|那么q新的value覆盖旧的valueQƈ把旧的valueD回?br /> addEntry(hash, key, value, i) 如果l过遍历链表没有发现同样的keyQ那么进行addEntry函数的操作,增加当前key到hash表中的第i个格子中的链表中

    void addEntry(int hash, K key, V value, int bucketIndex) {
        Entry
<K,V> e = table[bucketIndex];
        table[bucketIndex] 
= new Entry<K,V>(hash, key, value, e);
        
if (size++ >= threshold)
            resize(
2 * table.length);
    }

Entry<K,V> e = table[bucketIndex];
  创徏一个Entry对象来存N|ps:Entry对象是一个链表对象)
table[bucketIndex] = new Entry<K,V>(hash, key, value, e); Entry对象d到链表中
if (size++ >= threshold) resize(2 * table.length); 最后将sizeq行自增Q判断size值是否大于重构因子,如果大于那么是用resizeq行扩容重构?br />
    void resize(int newCapacity) {
        Entry[] oldTable 
= table;
        
int oldCapacity = oldTable.length;
        
if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold 
= Integer.MAX_VALUE;
            
return;
        }

        Entry[] newTable 
= new Entry[newCapacity];
        transfer(newTable);
        table 
= newTable;
        threshold 
= (int)(newCapacity * loadFactor);
    }

q里Z么是否需要扩定w构,其实是涉及到负蝲因子的性能问题

loadFactor负蝲因子
上面说过loadFactor是一个hashMap的决定性属性,HashSet和HashMap的默认负载因子都?.75Q它表示Q如果哈希表的容量超q?/4Ӟ自动成倍的增加哈希表的定wQ这个值是权衡了时间和I间的成本,如果负蝲因子较高Q虽然会减少对内存空间的需求,但也会增加查找数据的旉开销Q无论是put()和get()都涉及到Ҏ据进行查扄动作Q所以负载因子是不适宜讄q高



get(key)

接下来看看get(key)做了什?br />
    public V get(Object key) {
        
if (key == null)
            
return getForNullKey();
        
int hash = hash(key.hashCode());
        
for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e 
!= null;
             e 
= e.next) {
            Object k;
            
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                
return e.value;
        }
        
return null;
    }

q些动作g是跟put(keyQvalue)相识Q通过hash法获取key的hash码,再通过indexFor定位key存在于table的哪一个下表,获取该下标然后对下标中的链表q行遍历比对Q如果有W合q接返回该key的value倹{?br />
keySet()

q里q涉及另一个问题,上面说了HashMap是跟set没有M亲属关系Q但map也一样实CkeySet接口Q下面谱析一下keySet在hashMap中是如何实现的,q里l出部分代码Q请l合源码查看

public K next() {
            
return nextEntry().getKey();
        }

    
final Entry<K,V> nextEntry() {
            
if (modCount != expectedModCount)
                
throw new ConcurrentModificationException();
            Entry
<K,V> e = next;
            
if (e == null)
                
throw new NoSuchElementException();

            
if ((next = e.next) == null) {
                Entry[] t 
= table;
                
while (index < t.length && (next = t[index++]) == null)
                    ;
            }
        current 
= e;
            
return e;
        }

代码很简单,是Ҏ个格子里面的链表q行遍历Q也正是q个原因Q当我们依次key值putqhashMap中,但在使用map.entrySet().iterator()q行遍历时候却不是put时候的序?br />
扩容
在前面说到put函数的时候,已经提过了扩容的问题

if (size++ >= threshold)
resize(
2 * table.length);


q里一个是否扩容的判断Q当数据辑ֈ了threshold所谓的重构因子Q而不是HashMap的最大容量,p行扩宏V?br />

    
void resize(int newCapacity) {
        Entry[] oldTable 
= table;
        
int oldCapacity = oldTable.length;
        
if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold 
= Integer.MAX_VALUE;
            
return;
        }

        Entry[] newTable 
= new Entry[newCapacity];
        transfer(newTable);
        table 
= newTable;
        threshold 
= (int)(newCapacity * loadFactor);
    }

    
void transfer(Entry[] newTable) {
        Entry[] src 
= table;
        
int newCapacity = newTable.length;
        
for (int j = 0; j < src.length; j++) {
            Entry
<K,V> e = src[j];
            
if (e != null) {
                src[j] 
= null;
                
do {
                    Entry
<K,V> next = e.next;
                    
int i = indexFor(e.hash, newCapacity);
                    e.next 
= newTable[i];
                    newTable[i] 
= e;
                    e 
= next;
                } 
while (e != null);
            }
        }
    }


transferҎ实际上是所有的元素重新q行一些hashQ这是因为容量变化了Q每个元素相对应的hashg会不一栗?br />
使用HashMap

1.不要再高q发中用HashMapQHashMap是线E不安全Q如果被多个U程׃n之后Q将可能发生不可预知的问题?br /> 2.如果数据大小事固定的Q最好在初始化的时候就lHashMap一个合理的定w|如果使用new HashMap()默认构造函敎ͼ重构因子的值是16*0.75=12Q当HashMap的容量超q了12后,׃q行一pd的扩容运,重徏一个原来成倍的数组Qƈ且对原来存在的元素进行重新的hashq算Q如果你的数据是有成千上万的Q那么你的成千上万的数据也要跟这你的扩容不断的hashQ这生高额的内存和cpu的大量开销?br />
当然啦,HashMap的函数还有很多,不过都是Ztable的链表进行操作,当然也就是hash法QMap & hashMap在^时我们的应用非常多,最重要的是我们要对每句代码中每块数据结构变化心中有数?br />


上面主要是参考了jdk源码Q数据结构和一些相兌料本着好记性不如烂博客的精记录下来,希望朋友们如果发觉哪里不对请指出来,虚心h

----------------------------------------

by 陈于?
QQ:34174409
Mail: dongbule@163.com


]]>
java数据l构-Listhttp://www.aygfsteel.com/dongbule/archive/2011/01/16/343063.html陈于?/dc:creator>陈于?/author>Sun, 16 Jan 2011 09:36:00 GMThttp://www.aygfsteel.com/dongbule/archive/2011/01/16/343063.htmlhttp://www.aygfsteel.com/dongbule/comments/343063.htmlhttp://www.aygfsteel.com/dongbule/archive/2011/01/16/343063.html#Feedback0http://www.aygfsteel.com/dongbule/comments/commentRss/343063.htmlhttp://www.aygfsteel.com/dongbule/services/trackbacks/343063.html ArrayList
ArrayList其实是一l长度可变的数组Q当实例化了一个ArrayListQ该数据也被实例化了Q当向集合中d对象Ӟ数组的大也随着改变Q这样它所带来的有优点是快速的随机讉KQ即使访问每个元素所带来的性能问题也是很小的,但缺点就是想其中d或删除对象速度慢,当你创徏的数l是不确定其定wQ所以当我们改变q个数组时就必须在内存中做很多的处理Q如你想要数l中L两个元素中间d对象Q那么在内存中数l要Ud所有后面的对象?br />
LinkedList
LinkedList是通过节点的连接实现链表的数据l构Q向linkedList中插入或删除元素的速度是特别快Q而随问的速度相对较慢Q这个是׃链表本n的性质造成的,在链表中Q每个节炚w包含了前一个节点的引用Q后一个节点的引用和节点存储|当一个新节点插入式,只需要修改其中相关的前后关系节点引用卛_Q删除节点也是一栗操作对象只需要改变节点的链接Q新节点可以存放在内存的M位置Q但也就是因为如此LinkedList虽然存在get()ҎQ但是这个方法通过遍历节点来定位所以速度很慢。LinkedListq单独具addFrist(),addLast(),getFrist(),getLast(),removeFirst(),removeLast()ҎQ这些方法得LinkedList可以作ؓ堆栈Q队列,和双队列来用?br />
说白了,ArrayList和LinkedList是数据l构中的序存储表和铑ּ存储表?br />
ArrayList构造原?/strong>
上面已经清楚ArrayList和LinkedList是数据l构的顺序表和链?不清楚的ȝ数据l构的书)Q下面简单分析一下它们的实现方式?br /> 下表是摘自sum提供的ArrayList的api文档

ArrayList()
          构造一个初始容量ؓ 10 的空列表?br /> ArrayList(Collection<? extends E> c)
          构造一个包含指?collection 的元素的列表Q这些元素是按照?collection 的P代器q回它们的顺序排列的?br /> ArrayList(int initialCapacity)
          构造一个具有指定初始容量的I列表?br />
W一个构造函数是没有默认构徏了一个初始容?0的空列表Q第二个构造函数是制定collection元素的列表,W三个构造函数是qh定构造的列表初始化容量多,如果使用W一个构造函数则表示默认调用该参CؓinitialCapacity=10来构造一个列表对象?br />
ArrayList源码E微q行分析

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

    
private transient Object[] elementData;

    
private int size;

    
public ArrayList(int initialCapacity) {
    
super();
        
if (initialCapacity < 0)
            
throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
    
this.elementData = new Object[initialCapacity];
    }

    
public ArrayList() {
    
this(10);
    }

    
public ArrayList(Collection<? extends E> c) {
    elementData 
= c.toArray();
    size 
= elementData.length;
    
// c.toArray might (incorrectly) not return Object[] (see 6260652)
    if (elementData.getClass() != Object[].class)
        elementData 
= Arrays.copyOf(elementData, size, Object[].class);
    }

    
public int size() {
    
return size;
    }

}


可以发现ArrayList中包含两个主要的属?br />
    private transient Object[] elementData;

    private int size;

其中elementData[]是列表的实现的核心数l,我们使用该数l来存放集合中的数据Q而我们的构造函数所传递的initialCapacity参数是构数组的长度?br /> 在看看size的实现Ş式,它的作用是返回size的属性值的大小Q我们再看看另外一个构造函数public ArrayList(Collection<? extends E> c)Q该构造函数的作用是把另外一个容器对象中的元素放入当点的List对象中。首先是通过调用另外一个容器对象c的size()来设|当前List对象的size属性的长度大小。接下来׃乎对elementData[]数组q行初始化,最后通过Arrays.copyOf(U[] original, int newLength, Class<? extends T[]> newType)Ҏ把当前容器中的对象都存放q新的数lelementDataQ主要就完成了一个列表的创徏?br />
ArrayList定w扩充
q有一个问题就是我们所建立的ArrayList是用数l来实现的,但数l的长度一旦被初始化就不能改变Q而我们在l此列表对象d元素时却没有受到长度的限Ӟ所以,ArrayList的elementData属性一定是存在一个动态扩充容量的机制Q下面把相关的部分源码脓出来再做研究

public boolean add(E e) {
    ensureCapacity(size 
+ 1);  // Increments modCount!!
    elementData[size++= e;
    
return true;
    }

    
protected transient int modCount = 0;    

    
/**
     * Increases the capacity of this <tt>ArrayList</tt> instance, if
     * necessary, to ensure that it can hold at least the number of elements
     * specified by the minimum capacity argument.
     *
     * 
@param   minCapacity   the desired minimum capacity
     
*/
    
public void ensureCapacity(int minCapacity) {
    modCount
++;
    
int oldCapacity = elementData.length;
    
if (minCapacity > oldCapacity) {
        Object oldData[] 
= elementData;
        
int newCapacity = (oldCapacity * 3)/2 + 1;
            
if (newCapacity < minCapacity)
        newCapacity 
= minCapacity;
            
// minCapacity is usually close to size, so this is a win:
            elementData = Arrays.copyOf(elementData, newCapacity);
    }
    }

看看public boolean add(E e)ҎQ可以发现在d一个元素到容器中的时候,我们会先通过ensureCapacity(size + 1)判断该数l是否需要扩充?br /> public void ensureCapacity(int minCapacity)q个Ҏ是用来判断当前的数组是否需要扩充,q且该扩充多。modCount++; 表示当前的对象对elementData数组q行了多次扩充Q清I和U除{操作,相当于是一个对当前List对象的一个操作记录数?br /> int oldCapacity = elementData.length; 初始化oldCapacityQ表CZؓ当前elementData数组的长度?br /> if (minCapacity > oldCapacity) 判断minCapacity和oldCapacity谁大Q来军_是否需要扩充?br /> int newCapacity = (oldCapacity * 3)/2 + 1; 扩充的策列是判断(oldCapacity * 3)/2 + 1和minCapacity两者之间谁更大Q取更大的数作ؓ扩充后数l的initialCapacity|然后使用数组拯的方式,把以前的数据转移到新的数l对象中
如果minCapacity 于 oldCapacity ׃需要再扩充?br />
ArrayList删除元素

public E remove(int index) {
    RangeCheck(index);

    modCount
++;
    E oldValue 
= (E) elementData[index];

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

    
return oldValue;
    }

    
private void RangeCheck(int index) {
    
if (index >= size)
        
throw new IndexOutOfBoundsException(
        
"Index: "+index+", Size: "+size);
    }

在看看ArrayListU除元素是怎么实现的,首先判断需要删除的index是否在elementData数组的下标内Q如不存在则抛出IndexOutOfBoundsException?br /> modCount++; 与扩充元素一个,删除元素也记下来操作数?br /> E oldValue = (E) elementData[index]; 获取需要删除元素的对象?br /> int numMoved = size - index - 1; 获取需要被删除元素的下标,删除该元素后Q数l需要在此元素下标后的所有对象进行内存的Ud?br /> System.arraycopy(elementData, index+1, elementData, index,numMoved);对numMoved后面的所有对象通过copy的方式进行内存的Ud重新构徏数组?br />
说完ArrayList的实玎ͼ再说说linkedList

构徏双链?LinkedList)
LinkedList是类gC语言的双链表Q双链表比单链表多了一个域Q这个双链表有了三个域Q一个域来用存储数据元素Q一个用来指向后l节点,另一个是指向l点的直接前p炏V?br />
public class LinkedList<E>
    
extends AbstractSequentialList<E>
    
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
    
private transient Entry<E> header = new Entry<E>(nullnullnull);
    
private transient int size = 0;

    
public LinkedList() {
        header.next 
= header.previous = header;
    }

    
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;
    }
    }

}

在EntrycMQ定义了三个属性,分别为E element 表示数据与,Entry<E> next为后l指针域QEntry<E> previous为前驱指针域?br /> 在LinkedList中定义了一个重要的属性headerQ头l点Q不会纳入链表的d?该节点的previous是指向最后节点,next是指向第一节点?br /> 构造函数LinkedList() 构造一个空列表。将header的前驱指针域和后l指针域都指向了自己,看到q里可以发现Qnext和previous是一个引用,其实也相{于C里面的指针,不过C不会处理I指针,直接放操作系l处理了Qjavaq接抛出NullPointerExceptionQ根本不让它破坏pȝ的机会?br />
LinkedList元素变动
上面说到了LinkedList的新增和删除的效率比ArrayList的高Q实际上?链表操作q些ҎӞ只需要改?个节点各自的前驱指针和后l指针域Q而ArrayList是需要移动很多的元素?br />
public boolean add(E e) {
    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;
    }

    
private E remove(Entry<E> e) {
    
if (e == header)
        
throw new NoSuchElementException();

        E result 
= e.element;
    e.previous.next 
= e.next;
    e.next.previous 
= e.previous;
        e.next 
= e.previous = null;
        e.element 
= null;
    size
--;
    modCount
++;
        
return result;
    }

相比ArrayList的add()ҎQLinkedList实现h非常单,主要是两行代码:
newEntry.previous.next = newEntry;上一节点的后l节Ҏ向新增的节点
newEntry.next.previous = newEntry;头节点的前驱节点指向新增节点Qsize和modCount自增记录?br />
同样remove的实C非常?br /> e.previous.next = e.next;该节点的后一节点的后去节Ҏ向该节点的后p点,
e.next.previous = e.previous;该节点的后一节点的前pҎ向该节点的前p炏V?br /> e.next = e.previous = null;把该节点的前p点和后驱节点全部指向null?br /> e.element = null;把该节点的数据域讄为null?br />
随机讉K
相比序表,链表的随问效率要低得?理论说法Q不是绝?QArrayList可以Ҏ索引可行随问,而LinkedList则不要遍历访问?br />
public E get(int index) {
        
return entry(index).element;
    }

    
private Entry<E> entry(int index) {
        
if (index < 0 || index >= size)
            
throw new IndexOutOfBoundsException("Index: "+index+
                                                
", Size: "+size);
        Entry
<E> e = header;
        
if (index < (size >> 1)) {
            
for (int i = 0; i <= index; i++)
                e 
= e.next;
        } 
else {
            
for (int i = size; i > index; i--)
                e 
= e.previous;
        }
        
return e;
    }

上列的代码是对一个链表的遍历Q其中包含了一个算法,如果l的索引号小于总节Ҏ的一半,则在链表的前半部分第一个节点完q行遍历Q如果给的烦引号大于总节Ҏ的一半,则从最后一个节点往前进行遍历直到烦引号?br />
最?strong>ȝ
一下ArrayList和LinkedList的各自特?br /> 1.ArrayList是基于线性表的顺序存储表QLinkedList是基本线性表的链表存储表?br /> 2.对于新增和删除元素,LinkedList比较占有优势Q只需要变前后2个节点,而ArrayList要移动数据?br /> 3.对于随机讉K来说QArrayList比较占有优势Q可以根据烦引号快速访问,而LinkedList则需要遍历集合的元素来定位?br /> 4.而对于P代操?iterate)和查找操?indexOf)Q两者是差不多?br /> 不过上面都是Z理论Q具体问题还是要Ҏ事实q行分析Q如ArrayList删除的元素刚好是队列的最后一个元素,那么是无需要移动数据的Q大体我们可以认为需要随问较多的那么比较适合用ArrayListQ如果是插入和删?如消息队?较多的那么就需要考虑LinkedList?br />
上面主要是参考了jdk源码Q数据结构和一些相兌料本着好记性不如烂博客的精记录下来,希望朋友们如果发觉哪里不对请指出来,虚心h

----------------------------------------

by 陈于?
QQ:34174409
Mail: dongbule@163.com


]]>
java数据l构-sethttp://www.aygfsteel.com/dongbule/archive/2011/01/06/342446.html陈于?/dc:creator>陈于?/author>Thu, 06 Jan 2011 10:07:00 GMThttp://www.aygfsteel.com/dongbule/archive/2011/01/06/342446.htmlhttp://www.aygfsteel.com/dongbule/comments/342446.htmlhttp://www.aygfsteel.com/dongbule/archive/2011/01/06/342446.html#Feedback0http://www.aygfsteel.com/dongbule/comments/commentRss/342446.htmlhttp://www.aygfsteel.com/dongbule/services/trackbacks/342446.html
        Set<String> set = new HashSet<String>();
        String a 
= "1",b = "2",c = "1",d = "3",e = "2";
        set.add(a);
        set.add(b);
        set.add(c);
        set.add(d);
        set.add(e);
        
        Iterator it 
= set.iterator();
        
while(it.hasNext()){
            String s 
= (String)it.next();
            System.out.print(s
+",");
        }
        System.out.println(
"一共有对象Q?/span>"+set.size()+"?/span>"); //打印l果是:3,2,1,一共有对象Q??/div>
我们都知道Set是一U最单的集合Q对象的排序无特定的规则Q集合里面存攄是对象的引用Q所以没有重复的对象Q在上面的代码中Q程序创Za、b、c、d、e五个变量Q其中a和cQb和e所引用的字W串是一致的Q然后向setd了这5个变量。打印出来的size()只有3个,实际上向集合加入的只?个对象,在执行Set的add()Ҏ时已l进行了判断q个对象是否存在于集合,如果对象已经存在则不l箋执行?br />

Set的接口有两个实现c,HashSet和TreeSetQHashSet是按照哈希算法来q行存取集合中的对象Q存取速度比较快,TreeSetcLCZSortedSet接口Q具有排序功?br />
HashSet
HashSet是按照哈希算法来存取集合中的对象Q具有很好的存取和查找性能Q当向集合中加入一个对象时QHashSet会调用对象的hashCode()Ҏ来获取哈希码Q然后根据这个哈希吗来计对象在集合中的存放位置?br /> 在ObjectcM定义了hashCode()和equal()Qequal()是按照内存地址比较对象是否相同Q如果object1.equal(object1)w为trueӞ则表明了object1和object2变量实际上引用了同一个对象,那么object1和object2的哈希码也是肯定相同?br /> E微的看看HashSet的源?br />
public class HashSet<E>
    
extends AbstractSet<E>
    
implements Set<E>, Cloneable, java.io.Serializable
{

    
private transient HashMap<E,Object> map;
    
private static final Object PRESENT = new Object();
        
public HashSet() {
        map 
= new HashMap<E,Object>();
    }

   
public boolean add(E e) {
        
return map.put(e, PRESENT)==null;
    }
}

public class HashMap<K,V>
    
extends AbstractMap<K,V>
    
implements Map<K,V>, Cloneable, Serializable
{

    
public V put(K key, V value) {
        
if (key == null)
            
return putForNullKey(value);
        
int hash = hash(key.hashCode());
        
int i = indexFor(hash, table.length);
        
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue 
= e.value;
                e.value 
= value;
                e.recordAccess(
this);
                
return oldValue;
            }
        }

        modCount
++;
        addEntry(hash, key, value, i);
        
return null;
    }

}

我们会发现我们调用Set的add()Ҏ其实是返回一个transient HashMap.put(e, PRESENT)Q再跟进HashMap的put(K key, V value)Ҏq行查看,可以看到int hash = hash(key.hashCode());使用了set.add(key)的key做了一个哈希处理得C个哈希码Q然后再Entry做了一个遍历,使用equal()Ҏ对讲原有的对象和新添加的对象q行了一个比较,如果出现了true的情况,q接返回一个oldValueQ如果在遍历Ҏ的过E中没有出现ture的情况,则l一下的步骤modCount++Q将对象L自加Qƈ且l执行addEntry()的操作,下面涉及HashMap的操作就不l?br />
实际上HashSet的底层实现是ZHashMapQ所以在此处涉及到Hash法不展开Q详l可以见另一?a id="viewpost1_TitleUrl" href="../archive/2011/02/15/344387.html">java数据l构-HashMap?br />
TreeSet
TreeSetcL实现了SortedSet接口Q所以能够对集合中的对象q行排序

        Set iset = new TreeSet();
        iset.add(
new Integer(1));
        iset.add(
new Integer(10));
        iset.add(
new Integer(5));
        iset.add(
new Integer(8));
        iset.add(
"2");
        Iterator it2 
= iset.iterator();
        
while(it2.hasNext()){
            Integer s 
= (Integer)it2.next();
            System.out.print(s
+",");
        }
        System.out.println(
"一共有对象Q?/span>"+iset.size()+"?/span>");//打印出来的结果:1,2,5,8,10,一共有对象Q??/span>

当TreeSet向集合加入一个对象时Q会把它插入到有序的对象序列中,TreeSet包括了两U排序方式,自然排序和客户化排序Q在默认的情况下使用自然排序?br />
自然排序
在jdkcd中,有部分类实现了Comparable接口Q如IntegerQDouble{等QComparable接口有一个compareTo()Ҏ可以q行比较QTreeSet调用对象的compareTo()Ҏ比较集合中对象的大小Q然后进行升序排序。如IntegerQ?br />
public final class Integer extends Number implements Comparable<Integer> {

    
public int compareTo(Integer anotherInteger) {
    
int thisVal = this.value;
    
int anotherVal = anotherInteger.value;
    
return (thisVal<anotherVal ? -1 : (thisVal==anotherVal ? 0 : 1));
    }

}

ZComparable的属性,使用自然排序Ӟ只能向TreeSet集合中加入同cd的对象,q且q些对象的类必须实现了Comparable的接口,以下我们试向TreeSet集合加入两个不同cd的对象,会发现抛出ClassCastException

Set iset = new TreeSet();
iset.add(
new Integer(1));
iset.add(
new Integer(8));
iset.add(
"2");
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer

我们再打开TreeSet的源码进行查?br />
public class TreeSet<E> extends AbstractSet<E>
    
implements NavigableSet<E>, Cloneable, java.io.Serializable
{
    
private transient NavigableMap<E,Object> m;

    
private static final Object PRESENT = new Object();

    
public boolean add(E e) {
    
return m.put(e, PRESENT)==null;
    }

}

NavigableMap为接口,实现cMؓTreeMap

public class TreeMap<K,V>
    
extends AbstractMap<K,V>
    
implements NavigableMap<K,V>, Cloneable, java.io.Serializable
{
    
private final Comparator<? super K> comparator;

    
private transient Entry<K,V> root = null;

    
public V put(K key, V value) {
        Entry
<K,V> t = root;
        
if (t == null) {
        
// TBD:
        
// 5045147: (coll) Adding null to an empty TreeSet should
        
// throw NullPointerException
        
//
        
// compare(key, key); // type check
            root = new Entry<K,V>(key, value, null);
            size 
= 1;
            modCount
++;
            
return null;
        }
        
int cmp;
        Entry
<K,V> parent;
        
// split comparator and comparable paths
        Comparator<? super K> cpr = comparator;
        
if (cpr != null) {
            
do {
                parent 
= t;
                cmp 
= cpr.compare(key, t.key); //此处如果cd不相同可能抛出ClassCastException
                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> e = new Entry<K,V>(key, value, parent);
        
if (cmp < 0)
            parent.left 
= e;
        
else
            parent.right 
= e;
        fixAfterInsertion(e);
        size
++;
        modCount
++;
        
return null;
    }

}

首先判断原始集合是否存在Q如果不存在则直接创建,无需比较?br /> 如果原始集合存在的话Q先d出Comparator对象Q然后遍历原始集合tQ用parentZ时比较|逐个使用compare(key, t.key)Ҏ与添加对象keyq行比较Q决定元素排在集合的left或right?br />
客户端排?/strong>
客户端排序时因ؓjava.util.Comparator<Type>接口提供了具体的排序方式Q?lt;Type>指定了被比较对象的类型,Comparator有个compare(Type x,Type y)的方法,用于比较两个对象的大?br />
public class NameComparator implements Comparator<Name>{
    
    
public int compare(Name n1,Name n2){
        
if(n1.getName().compareTo(n2.getName())>0return -1;
        
if(n1.getName().compareTo(n2.getName())<0return 1;
        
        
return 0;
    }
    
    
public static void main(String[] args) {
        Set
<Name> set = new TreeSet<Name>(new NameComparator());
        
        Name n1 
= new Name("ray");
        Name n2 
= new Name("tom");
        Name n3 
= new Name("jame");
        set.add(n1);
        set.add(n2);
        set.add(n3);
        
        Iterator it 
= set.iterator();
        
while(it.hasNext()){
            Name s 
= (Name)it.next();
            System.out.print(s.getName()
+",");
        }
        System.out.println(
"一共有对象Q?/span>"+set.size()+"?/span>");
    }
}
//打印l果是:tom,ray,jame,一共有对象Q??/span>

道理与自然排序其实相同,都是通过实现Comparator接口Q就不细说了,可能有些说得不对的地方,Ƣ迎指正

----------------------------------------

by 陈于?
QQ:34174409
Mail: dongbule@163.com





]]> վ֩ģ壺 Զ| | ƽ| | | | | | | | | | ͭ| ĩ| | | | | ӽ| | | Զ| | | ˳ƽ| ʡ| ƽ| ֹ| | | | Dz| ̨| | | | | | | | |