站长之家ip地址查询,介绍美食的网站模板免费下载,公司做公司网站,wordpress qq挂件#xff08;尊重劳动成果#xff0c;转载请注明出处#xff1a;https://yangwenqiang.blog.csdn.net/article/details/105418475冷血之心的博客#xff09;  
背景 
一直都有这么一个打算#xff0c;那就是将Java中常见集合的源码进行一个全面的梳理#xff08;尽管已经有…尊重劳动成果转载请注明出处https://yangwenqiang.blog.csdn.net/article/details/105418475冷血之心的博客  
背景 
一直都有这么一个打算那就是将Java中常见集合的源码进行一个全面的梳理尽管已经有很多人进行了梳理总结。重复造轮子的意义就在于笔者可以亲自去阅读与欣赏JDK集合源码学习JDK设计者们那优雅的code风格。 
那么就让我们从常用的集合ArrayList来看起吧。注. 本系列文章所使用的JDK版本为1.8  
正文 
ArrayList介绍 
相信各位小伙伴都相当熟悉ArrayList的使用不管是平时的日常工作或者是面试时候的手撕算法中我们都在频繁的使用ArrayList当然还有LinkedList。 
当然对于面试题目“ArrayList和LinkedList有什么区别” 我们也可以倒背如流分析的头头是道。 
ArrayList和LinkedList的区别总结如下 
ArrayList底层使用了动态数组实现实质上是一个动态数组LinkedList底层使用了双向链表实现可当作堆栈、队列、双端队列使用ArrayList在随机存取方面效率高于LinkedListLinkedList在节点的增删方面效率高于ArrayListArrayList必须预留一定的空间当空间不足的时候会进行扩容操作LinkedList的开销是必须存储节点的信息以及节点的指针信息 接下来我们先来依次分析ArrayList中关键性的源码吧。 
类关键信息解析 
ArrayList的实现原理是动态数组它是线程不安全的允许其中元素为null。ArrayList实现了 List, RandomAccess, Cloneable, java.io.Serializable接口如下所示  该类的继承关系图如下所示  
这里笔者突然觉得RandomAccess比较陌生顺势点开去看了下这个接口是干嘛用的 
答其中RandomAccess代表了其拥有随机快速访问的能力ArrayList可以以O(1)的时间复杂度去根据下标访问元素。 
JDK源码中给出了这样的例子  举个例子就是说我们的ArrayList实现了RandomAccess接口表示具有了随机访问的能力所以在遍历的时候使用for循环随机获取遍历速度更快 
而LinkedList没有实现该接口不具备随机访问能力所以遍历LinkedList的时候我们优先使用迭代器来进行遍历。 
比如说这段代码 
//        ListInteger list  new ArrayList();ListInteger list  new LinkedList();list.add(1);list.add(3);list.add(2);list.add(4);for (int i  0; i  list.size(); i) {System.out.println(list.get(i));}LinkedList执行起来效率会很低大家可以点开看看list.get(i)到底经历了什么鬼后续章节我们也会解析。  
关键属性 
既然ArrayList底层实现是一个动态数组那么来看下都有哪些基本属性吧。 
// 用于序列化的版本号这里可以忽略
private static final long serialVersionUID  8683452581122892189L;/集合的初始容量为10private static final int DEFAULT_CAPACITY  10;//指定大小的构造函数里可能使用到的空数组private static final Object[] EMPTY_ELEMENTDATA  {};//默认构造函数里的空数组private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA  {};//真正存放元素的数组transient Object[] elementData;// 数组中真正包含的元素个数private int size;// 最大数组容量private static final int MAX_ARRAY_SIZE  Integer.MAX_VALUE - 8;至于说JDK8中为什么会有两个默认空数组大家可以参考下这篇文章的介绍 https://blog.csdn.net/weixin_43390562/article/details/101236833 简单理解就是说为了避免创建太多的空数组减少内存的消耗所以整了一个全局的空数组出来。  
构造方法 
三种构造方法 // 默认无参构造方法初始容量是10在add元素的时候才会真正去初始化容量为10public ArrayList() {this.elementData  DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}// 指定容量创建ArrayList分为容量大于0等于0以及小于0三种情况public ArrayList(int initialCapacity) {if (initialCapacity  0) {this.elementData  new Object[initialCapacity];} else if (initialCapacity  0) {this.elementData  EMPTY_ELEMENTDATA;} else {throw new IllegalArgumentException(Illegal Capacity: initialCapacity);}}//将别的集合直接赋值进行创建的构造方法public ArrayList(Collection? extends E c) {//直接用toArray()的方法获取集合的数组对象并且直接赋值给elementDataelementData  c.toArray();size  elementData.length;// 这里是当c.toArray出错没有返回Object[]时利用Arrays.copyOf 来复制集合c中的元素到elementData数组中if (elementData.getClass() ! Object[].class)elementData  Arrays.copyOf(elementData, size, Object[].class);} 如何扩容 
既ArrayList底层是基于动态数组实现的那必不可少的会有一个扩容的过程吧讲道理应该是我们add元素的时候是可能触发扩容过程的接下里我们先来看下扩容方法grow( )的实现吧。 //扩容方法private void grow(int minCapacity) {// 记录扩容前数组的长度int oldCapacity  elementData.length;//将原数组的长度扩大0.5倍作为扩容后新数组的长度如果扩容前数组长度为10那么经过扩容后的数组长度应该为15int newCapacity  oldCapacity  (oldCapacity  1);//如果扩容后的长度小于当前数据量那么就将当前数据量的长度作为本次扩容的长度if (newCapacity - minCapacity  0)newCapacity  minCapacity;//判断新数组长度是否大于可分配数组的最大大小if (newCapacity - MAX_ARRAY_SIZE  0)//将扩容长度设置为最大可用长度newCapacity  hugeCapacity(minCapacity);// 拷贝扩容构建一个新的数组elementData  Arrays.copyOf(elementData, newCapacity);}//判断如果新数组长度超过当前数组定义的最大长度时就将扩容长度设置为Interger.MAX_VALUE,也就是int的最大长度private static int hugeCapacity(int minCapacity) {if (minCapacity  0) // overflowthrow new OutOfMemoryError();return (minCapacity  MAX_ARRAY_SIZE) ?Integer.MAX_VALUE : MAX_ARRAY_SIZE;} 常用API 
这里列出ArrayList中常用的API后边会对其源码进行一定的浅析。 
boolean add(E e)
将指定的元素添加到此列表的尾部。void add(int index, E element)
将指定的元素插入此列表中的指定位置。boolean addAll(Collection c)
按照指定 collection 的迭代器所返回的元素顺序将该 collection 中的所有元素添加到此列表的尾部。boolean addAll(int index, Collection c)
从指定的位置开始将指定 collection 中的所有元素插入到此列表中。void clear()
移除此列表中的所有元素。Object clone()
返回此 ArrayList 实例的浅表副本。boolean contains(Object o)
如果此列表中包含指定的元素则返回 true。void ensureCapacity(int minCapacity)
如有必要增加此 ArrayList 实例的容量以确保它至少能够容纳最小容量参数所指定的元素数。E get(int index)
返回此列表中指定位置上的元素。int indexOf(Object o)
返回此列表中首次出现的指定元素的索引或如果此列表不包含元素则返回 -1。boolean isEmpty()
如果此列表中没有元素则返回 trueint lastIndexOf(Object o)
返回此列表中最后一次出现的指定元素的索引或如果此列表不包含索引则返回 -1。E remove(int index)
移除此列表中指定位置上的元素。boolean remove(Object o)
移除此列表中首次出现的指定元素如果存在。protected void removeRange(int fromIndex, int toIndex)
移除列表中索引在 fromIndex包括和 toIndex不包括之间的所有元素。E set(int index, E element)
用指定的元素替代此列表中指定位置上的元素。int size()
返回此列表中的元素数。Object[] toArray()
按适当顺序从第一个到最后一个元素返回包含此列表中所有元素的数组。T[] toArray(T[] a)
按适当顺序从第一个到最后一个元素返回包含此列表中所有元素的数组返回数组的运行时类型是指定数组的运行时类型。void trimToSize()
将此 ArrayList 实例的容量调整为列表的当前大小。 add增加元素 
从上边列出的常见API中我们可以看出当前存在四种add方法我们先来看第一种 
/*** Appends the specified element to the end of this list.** param e element to be appended to this list* return tttrue/tt (as specified by {link Collection#add})*/public boolean add(E e) {//判断添加后的长度是否需要扩容ensureCapacityInternal(size  1); //在数组末尾添加上当前元素并且修改size大小elementData[size]  e;return true;}private void ensureCapacityInternal(int minCapacity) {// 判断是否是第一次初始化数组if (elementData  DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {minCapacity  Math.max(DEFAULT_CAPACITY, minCapacity);}ensureExplicitCapacity(minCapacity);}// 判断扩容的方法private void ensureExplicitCapacity(int minCapacity) {// 如果需要扩容modCount此参数是指当前列表结构被修改的次数modCount;// 判断当前数据量是否大于数组的长度if (minCapacity - elementData.length  0)// 如果大于则进行扩容操作grow(minCapacity);}这块需要注意的是DEFAULT_CAPACITY是初始容量前面说了等于10扩容grow方法我们在上边也介绍过了。 
接下来是指定index的add方法本质上没有区别只不过多了一个元素copy的过程以及index check的逻辑 public void add(int index, E element) {rangeCheckForAdd(index);ensureCapacityInternal(size  1);  // Increments modCount!!// 拷贝数组将下标后面的元素全部向后移动一位System.arraycopy(elementData, index, elementData, index  1,size - index);elementData[index]  element;size;}/*** A version of rangeCheck used by add and addAll.*/private void rangeCheckForAdd(int index) {if (index  size || index  0)throw new IndexOutOfBoundsException(outOfBoundsMsg(index));}然后就是添加一个完整集合的add方法 
public boolean addAll(Collection? extends E c) {Object[] a  c.toArray();int numNew  a.length;ensureCapacityInternal(size  numNew);  // Increments modCountSystem.arraycopy(a, 0, elementData, size, numNew);size  numNew;return numNew ! 0;}指定index的add一个集合的方法 
public boolean addAll(int index, Collection? extends E c) {rangeCheckForAdd(index);Object[] a  c.toArray();int numNew  a.length;ensureCapacityInternal(size  numNew);  // Increments modCountint numMoved  size - index;if (numMoved  0)System.arraycopy(elementData, index, elementData, index  numNew,numMoved);System.arraycopy(a, 0, elementData, index, numNew);size  numNew;return numNew ! 0;}这两个方法也是一样的判断是否需要扩容然后进行了一个数组copy的过程。 
add方法总结 
先判断是否越界是否需要扩容如果扩容 就复制数组然后设置对应下标元素值。 
扩容过程 
如果需要扩容的话默认扩容一半。如果扩容一半不够就用目标的size作为扩容后的容量。在扩容成功后会修改modCount get方法获取元素 
既然是一个数组那没啥好说的check一下index然后直接获取即可。 /*** Returns the element at the specified position in this list.** param  index index of the element to return* return the element at the specified position in this list* throws IndexOutOfBoundsException {inheritDoc}*/public E get(int index) {rangeCheck(index);return elementData(index);}private void rangeCheck(int index) {if (index  size)throw new IndexOutOfBoundsException(outOfBoundsMsg(index));}set方法设置元素 
先看源码 
/*** Replaces the element at the specified position in this list with* the specified element.** param index index of the element to replace* param element element to be stored at the specified position* return the element previously at the specified position* throws IndexOutOfBoundsException {inheritDoc}*/public E set(int index, E element) {rangeCheck(index);E oldValue  elementData(index);elementData[index]  element;return oldValue;}注意就是替换了index位置的元素值但是这里返回值是一个该位置上的oldValue remove删除元素 
public E remove(int index) {rangeCheck(index);//判断是否越界modCount;//修改modeCount 因为结构改变了E oldValue  elementData(index);//读出要删除的值int numMoved  size - index - 1;if (numMoved  0)System.arraycopy(elementData, index1, elementData, index,numMoved);//用复制 覆盖数组数据elementData[--size]  null; // clear to let GC do its work  //置空原尾部数据 不再强引用 可以GC掉return oldValue;
}这个删除指定位置index上的元素还是很好理解的还是涉及到了一个元素的copy。接下来再看一个删除指定Object的方法 
//删除该元素在数组中第一次出现的位置上的数据。 如果有该元素返回true如果false。
public boolean remove(Object o) {if (o  null) {for (int index  0; index  size; index)if (elementData[index]  null) {fastRemove(index);//根据index删除元素return true;}} else {for (int index  0; index  size; index)if (o.equals(elementData[index])) {fastRemove(index);return true;}}return false;
}
//不会越界 不用判断 也不需要取出该元素。
private void fastRemove(int index) {modCount;//修改modCountint numMoved  size - index - 1;//计算要移动的元素数量if (numMoved  0)System.arraycopy(elementData, index1, elementData, index,numMoved);//以复制覆盖元素 完成删除elementData[--size]  null; // clear to let GC do its work  //置空 不再强引用
} 
删除操作一定会修改modCount且可能涉及到数组的复制相对低效并且在批量删除中涉及高效的保存两个集合公有元素的算法  
说了增add删remove改set查get之后我们再看看下其余常见的API方法吧。  
indexOf方法 
这个我们就不贴源码了感兴趣的可以看看很简单就是简单遍历找到该元素找不到返回-1。另外lastIndexOf方法则是从后开始找该元素。  
contains方法 
借助于indexOf方法实现源码如下 
public boolean contains(Object o) {return indexOf(o)  0;}clear方法 
/*** Removes all of the elements from this list.  The list will* be empty after this call returns.*/public void clear() {modCount;// clear to let GC do its workfor (int i  0; i  size; i)elementData[i]  null;size  0;}clear方法就是将数组中的所有位置都置为null并且会修改modCount。  
size和isEmpty方法 
/*** Returns the number of elements in this list.** return the number of elements in this list*/public int size() {return size;}/*** Returns tttrue/tt if this list contains no elements.** return tttrue/tt if this list contains no elements*/public boolean isEmpty() {return size  0;}我们在前面介绍ArrayList属性的时候介绍到了size属性在add以及remove方法中会进行size和size–。size直接记录了当前实际存在于数组中的元素所以可以直接返回。  
toArray方法 
/*** Returns an array containing all of the elements in this list* in proper sequence (from first to last element).** pThe returned array will be safe in that no references to it are* maintained by this list.  (In other words, this method must allocate* a new array).  The caller is thus free to modify the returned array.** pThis method acts as bridge between array-based and collection-based* APIs.** return an array containing all of the elements in this list in*         proper sequence*/public Object[] toArray() {return Arrays.copyOf(elementData, size);}/*** Returns an array containing all of the elements in this list in proper* sequence (from first to last element); the runtime type of the returned* array is that of the specified array.  If the list fits in the* specified array, it is returned therein.  Otherwise, a new array is* allocated with the runtime type of the specified array and the size of* this list.** pIf the list fits in the specified array with room to spare* (i.e., the array has more elements than the list), the element in* the array immediately following the end of the collection is set to* ttnull/tt.  (This is useful in determining the length of the* list ionly/i if the caller knows that the list does not contain* any null elements.)** param a the array into which the elements of the list are to*          be stored, if it is big enough; otherwise, a new array of the*          same runtime type is allocated for this purpose.* return an array containing the elements of the list* throws ArrayStoreException if the runtime type of the specified array*         is not a supertype of the runtime type of every element in*         this list* throws NullPointerException if the specified array is null*/SuppressWarnings(unchecked)public T T[] toArray(T[] a) {if (a.length  size)// Make a new array of as runtime type, but my contents:return (T[]) Arrays.copyOf(elementData, size, a.getClass());System.arraycopy(elementData, 0, a, 0, size);if (a.length  size)a[size]  null;return a;}这个方法也是常用的可以将当前的list方便的转换为一个数组底层实现还是一个数组的copy  
迭代器 Iterator 
public IteratorE iterator() {return new Itr();
}
/*** An optimized version of AbstractList.Itr*/
private class Itr implements IteratorE {int cursor;       // index of next element to return //默认是0int 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)//再次判断是否越界在 我们这里的操作时有异步线程修改了Listthrow new ConcurrentModificationException();cursor  i  1;//游标1return (E) elementData[lastRet  i];//返回元素 并设置上一次返回的元素的下标}public void remove() {//remove 掉 上一次next的元素if (lastRet  0)//先判断是否next过throw new IllegalStateException();checkForComodification();//判断是否修改过try {ArrayList.this.remove(lastRet);//删除元素 remove方法内会修改 modCount 所以后面要更新Iterator里的这个标志值cursor  lastRet; //要删除的游标lastRet  -1; //不能重复删除 所以修改删除的标志位expectedModCount  modCount;//更新 判断集合是否修改的标志} catch (IndexOutOfBoundsException ex) {throw new ConcurrentModificationException();}}
//判断是否修改过了List的结构如果有修改抛出异常final void checkForComodification() {if (modCount ! expectedModCount)throw new ConcurrentModificationException();}
}迭代器其实是一个内部类包括Itr和ListItr如下所示  那么这两种迭代器有啥区别呢我们一起来看下Iterator和ListIterator的区别如下 
前者可以遍历list和set集合后者只能用来遍历list集合前者只能前向遍历集合后者可以前向和后向遍历集合后者实现了前者增加了一些新的功能。 感兴趣的同学可以看下源码也在ArrayList中哦是一个内部类  
ensureCapacity 
这是一个重要但是不常用的APIensureCapacity可以保证将当前数组扩容到足够容纳指定的个数减少程序中的自动扩容次数接下来我们举个例子 
ArrayListInteger list  new ArrayList();long begin1  System.currentTimeMillis();for (int i  0; i  10000; i) {list.add(i);}System.out.println(cost time2:(System.currentTimeMillis()-begin1));ArrayListInteger list2  new ArrayList();list2.ensureCapacity(10000);long begin2  System.currentTimeMillis();for (int i  0; i  10000; i) {list2.add(i);}System.out.println(cost time2:(System.currentTimeMillis()-begin2));上边这段代码我们对list2在使用前提前进行了扩容执行效率显著提升。 
cost time2:4
cost time2:1内部实现如下主要基于grow方法进行扩容实现的。 
/*** Increases the capacity of this ttArrayList/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) {int minExpand  (elementData ! DEFAULTCAPACITY_EMPTY_ELEMENTDATA)// any size if not default element table? 0// larger than default for default empty table. Its already// supposed to be at default size.: DEFAULT_CAPACITY;if (minCapacity  minExpand) {ensureExplicitCapacity(minCapacity);}}private void ensureCapacityInternal(int minCapacity) {if (elementData  DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {minCapacity  Math.max(DEFAULT_CAPACITY, minCapacity);}ensureExplicitCapacity(minCapacity);}private void ensureExplicitCapacity(int minCapacity) {modCount;// overflow-conscious codeif (minCapacity - elementData.length  0)grow(minCapacity);}ArrayList源码总结 
源码实在是太多我们通过对常用API的分析大概可以得出ArrayList的底层实现原理。总结如下 
ArrayList内部基于动态数组实现所以可以进行扩容操作每一次扩容增量都是50%基于数组实现的所以内部多个API都避免不了对数组的copy操作比如set和remove操作所以导致ArrayList插入和删除效率低下基于数组实现并且实现了RandomAccess所以可以随机访问根据index来找到元素的值所以ArrayList获取元素的效率很高提供了多个迭代器都是基于内部类实现的底层源码中没有做同步处理所以是线程不安全的之前的版本Vector原理基本一直但是Vector在方法的实现上都会加上synchronized关键字modeCount会在适当的时候进行操作可以实现快速失败 后续我会继续更新集合源码系列文章欢迎大家关注交流~ 如果对你有帮助记得点赞哈欢迎大家关注我的博客关注公众号文强的技术小屋学习更多技术知识一起遨游知识海洋~  
 文章转载自: http://www.morning.dsxgc.cn.gov.cn.dsxgc.cn http://www.morning.fbbmg.cn.gov.cn.fbbmg.cn http://www.morning.lqffg.cn.gov.cn.lqffg.cn http://www.morning.tzlfc.cn.gov.cn.tzlfc.cn http://www.morning.ppwdh.cn.gov.cn.ppwdh.cn http://www.morning.pqxjq.cn.gov.cn.pqxjq.cn http://www.morning.fbpdp.cn.gov.cn.fbpdp.cn http://www.morning.fnhxp.cn.gov.cn.fnhxp.cn http://www.morning.rrdch.cn.gov.cn.rrdch.cn http://www.morning.psxcr.cn.gov.cn.psxcr.cn http://www.morning.dhqzc.cn.gov.cn.dhqzc.cn http://www.morning.pcqdf.cn.gov.cn.pcqdf.cn http://www.morning.rfxyk.cn.gov.cn.rfxyk.cn http://www.morning.nqlcj.cn.gov.cn.nqlcj.cn http://www.morning.rpms.cn.gov.cn.rpms.cn http://www.morning.hqnsf.cn.gov.cn.hqnsf.cn http://www.morning.lgqdl.cn.gov.cn.lgqdl.cn http://www.morning.wjlbb.cn.gov.cn.wjlbb.cn http://www.morning.qnjcx.cn.gov.cn.qnjcx.cn http://www.morning.bpmnz.cn.gov.cn.bpmnz.cn http://www.morning.nwrzf.cn.gov.cn.nwrzf.cn http://www.morning.gmwqd.cn.gov.cn.gmwqd.cn http://www.morning.phgz.cn.gov.cn.phgz.cn http://www.morning.spwln.cn.gov.cn.spwln.cn http://www.morning.gwtbn.cn.gov.cn.gwtbn.cn http://www.morning.syxmx.cn.gov.cn.syxmx.cn http://www.morning.mhnb.cn.gov.cn.mhnb.cn http://www.morning.pdbgm.cn.gov.cn.pdbgm.cn http://www.morning.xiaobaixinyong.cn.gov.cn.xiaobaixinyong.cn http://www.morning.pqnps.cn.gov.cn.pqnps.cn http://www.morning.lzdbb.cn.gov.cn.lzdbb.cn http://www.morning.hrpmt.cn.gov.cn.hrpmt.cn http://www.morning.hyjpl.cn.gov.cn.hyjpl.cn http://www.morning.nmrtb.cn.gov.cn.nmrtb.cn http://www.morning.gcthj.cn.gov.cn.gcthj.cn http://www.morning.rlhjg.cn.gov.cn.rlhjg.cn http://www.morning.wkwds.cn.gov.cn.wkwds.cn http://www.morning.mrxgm.cn.gov.cn.mrxgm.cn http://www.morning.scjtr.cn.gov.cn.scjtr.cn http://www.morning.mynbc.cn.gov.cn.mynbc.cn http://www.morning.pswqx.cn.gov.cn.pswqx.cn http://www.morning.jfnbh.cn.gov.cn.jfnbh.cn http://www.morning.bflwj.cn.gov.cn.bflwj.cn http://www.morning.hpprx.cn.gov.cn.hpprx.cn http://www.morning.lxngn.cn.gov.cn.lxngn.cn http://www.morning.qpmmg.cn.gov.cn.qpmmg.cn http://www.morning.zcckq.cn.gov.cn.zcckq.cn http://www.morning.muzishu.com.gov.cn.muzishu.com http://www.morning.lhrxq.cn.gov.cn.lhrxq.cn http://www.morning.zstbc.cn.gov.cn.zstbc.cn http://www.morning.mmtbn.cn.gov.cn.mmtbn.cn http://www.morning.rwqk.cn.gov.cn.rwqk.cn http://www.morning.ndxrm.cn.gov.cn.ndxrm.cn http://www.morning.pcxgj.cn.gov.cn.pcxgj.cn http://www.morning.pbtrx.cn.gov.cn.pbtrx.cn http://www.morning.lkcqz.cn.gov.cn.lkcqz.cn http://www.morning.rsqpc.cn.gov.cn.rsqpc.cn http://www.morning.pnmnl.cn.gov.cn.pnmnl.cn http://www.morning.wrdlf.cn.gov.cn.wrdlf.cn http://www.morning.jynzb.cn.gov.cn.jynzb.cn http://www.morning.ydyjf.cn.gov.cn.ydyjf.cn http://www.morning.ttdbr.cn.gov.cn.ttdbr.cn http://www.morning.gwsdt.cn.gov.cn.gwsdt.cn http://www.morning.lzqxb.cn.gov.cn.lzqxb.cn http://www.morning.jxzfg.cn.gov.cn.jxzfg.cn http://www.morning.rknhd.cn.gov.cn.rknhd.cn http://www.morning.syznh.cn.gov.cn.syznh.cn http://www.morning.pmbcr.cn.gov.cn.pmbcr.cn http://www.morning.hblkq.cn.gov.cn.hblkq.cn http://www.morning.yfwygl.cn.gov.cn.yfwygl.cn http://www.morning.hjlwt.cn.gov.cn.hjlwt.cn http://www.morning.qyfrd.cn.gov.cn.qyfrd.cn http://www.morning.pxwzk.cn.gov.cn.pxwzk.cn http://www.morning.wmlby.cn.gov.cn.wmlby.cn http://www.morning.pszw.cn.gov.cn.pszw.cn http://www.morning.qbzfp.cn.gov.cn.qbzfp.cn http://www.morning.gfrjs.cn.gov.cn.gfrjs.cn http://www.morning.sxfmg.cn.gov.cn.sxfmg.cn http://www.morning.gtnyq.cn.gov.cn.gtnyq.cn http://www.morning.wcjgg.cn.gov.cn.wcjgg.cn