Collection List Set和Map用法与区别

labels:Collection List Set和Map用法与区别 java 散列表 集合

Collection           接
口的接口      对
象的集合   
├   List                    子接口         按进入先后有序保存      可
重复   
│├   LinkedList                 接口实现类      链表      插入删除      没有同步      线程不安全   
│├   ArrayList                   接口实现类      数组      随机访问      没有同步      线程不安全   
│└   Vector                   
   接口实现类      数组                     同步             线程安全   
│       └   Stack
└   Set                    子接口          仅接收一次,并做内部排序

├   HashSet

│       └   LinkedHashSet
└   TreeSet

对 于   List   ,关心的是顺序,   它
保证维护元素特定的顺序(允许有相同元素),使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在   List 中的位置,类似于数组下标)来访问   List  中的元素。


于   Set   ,只关心某元素是否属于   Set   (不   允
许有相同元素   ),而不关心它的顺序。

Map                 接
口         键值对的集合   
├   Hashtable                   接口实现类                     同步                线程安全   
├   HashMap                    接口实现类                     没有同步       线程不安全

│├   LinkedHashMap

│└   WeakHashMap

├   TreeMap
└   IdentifyHashMap

对于   Map   ,
最大的特点是键值映射,且为一一映射,键不能重复,值可以,所以是用键来索引值。   方法   put(Object
key, Object value)   添
加一个“值”   (   想要得东西   )   和与“值”相关联的“键”  (key)
(   使用它来查找   )   。方法   get(Object
key)   返回与给定“键”相关联的“值”。

Map   同样对每个元素保存一份,但这是基于   "   键   "   的,   Map   也有内置的排序,因而不关心元素添加的顺序。如果添加元素的顺序对你
很重要,应该使用   LinkedHashSet   或
者   LinkedHashMap.


于效率,   Map   由于采用了哈希散列,查找元素时明显比   ArrayList   快。

但 我有一个自己的原则想法:复杂的问题简单化。即把很多晦涩难懂的问题用通俗直白的话,一下子就看明白了,而不是大段大段的写。不得不指出的是现在部分所谓
的“专家”往往把简单的问题复杂化,让人看了生畏,甚至望而却步,以此来显示他的高深莫测,当然也可能有别的用意,那我就不得而知了。

更为精炼的总结:

Collection   是对象集合,   Collection   有两个子接口   List   和   Set

List   可以通过下标   (1,2..)   来取得值,值可以重复

而   Set   只能通过游标来取值,并且值是不能重复的

ArrayList   ,   Vector   ,   LinkedList   是   List   的实现类

ArrayList   是线程不安全的,   Vector   是线程安全的,这两个类底层都是由数组实现的

LinkedList   是线程不安全的,底层是由链表实现的

Map   是键值对集合

HashTable   和   HashMap   是   Map   的实现类     
HashTable   是线程安全的,不能存储   null   值     
HashMap   不是线程安全的,可以存储   null   值

所 以,如果你是想在一个很短的时间来弄明白这些问题,比如   1~2   分
钟。没有也不想花大量时间于此,那么建议你现在就可以收兵走人了。

如 果你想对此做一个详细的了解,请继续看下去。


所周知,   Java   来源于   C++   ,屏蔽了其底层实现,简化了对底层实现的管理,使开发者专注于上层功
能的实现。在   C/C++   里关于数据的存储需要程序员非常清楚,而   Java   程序员可以完全不管这些,那么,   Java   是怎么管理的呢?其实   Java   还是需要面临这些问题,只不过经过封装后,变得面目全非。所以对于像
我这种从   C/C++   转向   Java   的人还需要一段时间适应,   Collection   、   List  、   Set   、   Map   等概念还需要一个接受的过程。其实到后来发现,不管是什么语言,其底
层存储不外乎数组、线性表、栈、队列、串、树和图等数据结构。想明白了这些,一切都敞亮了。

一、容器(   Collection   )   接口   
     容器(   Collection   )是最基本的集合接口,一个容器(   Collection   )保存一组对象(  Object   ),即对象是容器的元素(   Elements   )。一些   Collection   允许相同的元素而另一些不行。一些能排序而另一些不行。   Java
SDK   不提供直接继承自   Collection   的类,   Java
SDK   提供的类都是继承自   Collection   的   “   子接口   ”   如   List   和   Set   。   
  所有实现   Collection   接
口的类都必须提供两个标准的构造函数:无参数的构造函数用于创建一个空的   Collection   ,
有一个   Collection   参数的构造函数用于创建一个新的   Collection   ,这个新的  Collection   与传入的   Collection   有相同的元素。后一个构造函数允许用户复制一个   Collection   。  
  如何遍历   Collection   中
的每一个元素?不论   Collection   的
实际类型如何,它都支持一个  iterator()   的
方法,该方法返回一个迭代子,使用该迭代子即可逐一访问   Collection   中
每一个元素。典型的用法如下:   
       Iterator it =
collection.iterator();
//  获得一个迭代子   
       while(it.hasNext())
{
         Object obj = it.next();
//  得到下一个元素   
       }

由   Collection   接
口派生的两个接口是   List   和   Set   。   List   按对象进入的顺序保存对象,不做排序或编辑操作。   Set   对每个对象只接受一次,并使用自己内部的排序方法   (   通常,你只关心某个元素是否属于   Set,   而不关心它的顺序   --   否则应该使用   List)   。

1   ,   List   接口   
     List   是
有序的   Collection   ,次序是   List   最重要的特点:它保证维护元素特定的顺序。使用此接口能够精确的控制
每个元素插入的位置。用户能够使用索引(元素在   List   中
的位置,类似于数组下标)来访问   List   中
的元素,这类似于   Java   的
数组。和下面要提到的   Set   不
同,   List   允许有相同的元素。   
     除了具有   Collection   接口必备的   iterator()   方法外,   List   还提供一个   listIterator()  方法,返回一个   ListIterator   接口,和标准的   Iterator   接口相比,   ListIterator   多了一些  add()   之类的方法,允许添加,删除,设定元素,   还
能向前或向后遍历。   
  实现   List   接
口的常用类有   LinkedList   ,   ArrayList   ,   Vector   和   Stack   。其中,最常用的是   LinkedList   和   ArrayList   两个。   

LinkedList   类   
        LinkedList   实现了   List   接口,允许   null   元素。此外   LinkedList   提供额外的  addFirst(),
addLast(), getFirst(), getLast(), removeFirst(), removeLast(), insertFirst(),
insertLast()   方
法在   LinkedList   的首部或尾部,这些方法(没有在任何接口或基类中定义
过)使  LinkedList   可被用作堆栈(   stack   ),队列(   queue   )或双向队列(   deque   )。

注意   LinkedList   没
有同步方法。如果多个线程同时访问一个   List   ,
则必须自己实现访问同步。一种解决方法是在创建   List   时
构造一个同步的   List   :   
       List
list = Collections.synchronizedList(new LinkedList(...));

特点:对顺序访问进行了优化,向   List   中间插入与删除的开销并不大。随机访问则相对较慢。   (  使用   ArrayList   代
替。   )

ArrayList   类   
     ArrayList   是
由数组实现的   List   ,并且实现了可变大小的数组。它允许所有元素,包括   null  。   ArrayList   没有同步。   size   ,   isEmpty   ,   get   ,   set   方法运行时间为常数。但是   add  方法开销为分摊的常数,添加   n   个元素需要   O(n)   的
时间。其他的方法运行时间为线性。   
     每个   ArrayList   实例都有一个容量(   Capacity   ),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自
动增加,但是增长算法并没有定义。当需要插入大量元素时,在插入前可以调用   ensureCapacity   方
法来增加   ArrayList   的容量以提高插入效率。   
  和   LinkedList   一
样,   ArrayList   也是非同步的(   unsynchronized   )。

特点:允许对元素进行快速随机访问,但是向   List   中间插入与移除元素的速度很慢。   ListIterator  只应该用来由后向前遍历   ArrayList,   而不是用来插入和移除元素。因为那比   LinkedList   开销要大很多。

Vector   类   
        Vector   非常类似   ArrayList   ,但是   Vector   是同步的。由   Vector   创建的   Iterator  ,虽然和   ArrayList   创建的   Iterator   是同一接口,但是,因为   Vector   是同步的,当一个  Iterator   被创建而且正在被使用,另一个线程改变了   Vector   的状态(例如,添加或删除了一些元素),这时调用   Iterator   的方法时将抛出   ConcurrentModificationException   ,
因此必须捕获该异常。   

       Stack  类:   Stack   继
承自   Vector   ,实现一个后进先出的堆栈。   Stack   提供   5   个额外的方法使得   Vector   得
以被当作堆栈使用。基本的   push   和   pop  方法,还有   peek   方
法得到栈顶的元素,   empty   方
法测试堆栈是否为空,   search   方
法检测一个元素在堆栈中的位置。   Stack  刚
创建后是空栈。   

2   ,   Set   接口   
     Set   具
有与   Collection   完全一样的接口,因此没有任何额外的功能,不像前面有
几个不同的  List   。实际上   Set   就是   Collection   ,只是行为不同。(这是继承与多态思想的典型应用:表现不同的行
为)。其次,   Set   是一种不包含重复的元素的   Collection   ,加入   Set   的元素必须定义  equals()   方法以确保对象的唯一性   (   即
任意的两个元素   e1   和   e2   都有  e1.equals(e2)=false  ),与   List   不
同的是,   Set   接口不保证维护元素的次序。最后,   Set   最多有一个   null   元素。 
  很明显,   Set   的
构造函数有一个约束条件,传入的   Collection   参
数不能包含重复的元素。   
  请注意:必须小心操作可变对象(   Mutable
Object   )。如果一个   Set   中的可变元素改变了自身状态导致   Object.equals(Object)=true   将
导致一些问题。

HashSet   

为快速查找设计的   Set   。
存入   HashSet   的对象必须定义   hashCode()   。

LinkedHashSet   类:
具有   HashSet   的查询速度,且内部使用链表维护元素的顺序   (   插入的次序   )   。
于是在使用迭代器遍历   Set   时, 结果会按元素插入的次序显示。

TreeSet  

保存次序的   Set,  底层为树结构。使用它可以从   Set   中
提取有序的序列。

二、   Map   接口   
     请注意,   Map   没有继承   Collection   接口,   Map   提供   key   到   value   的映射,你可以通过“键”查找“值”。一个   Map   中不能包含相同的   key   ,每个   key 只能映射一个   value   。   Map  接口提供   3   种集合的视图,   Map   的
内容可以被当作一组   key   集
合,一组   value   集合,或者一组   key-value   映射。

方法   put(Object key, Object
value)   添加一个“值”   (   想要得东西   )   和
与“值”相关联的“键”   (key) (   使
用它来查找   )   。方法   get(Object
key)   返回与给定“键”相关联的“值”。可以用   containsKey()   和   containsValue()   测试   Map   中是否包含某个“键”或“值”。   标
准的  Java   类库中包含了几种不同的 Map   :   HashMap,
TreeMap, LinkedHashMap, WeakHashMap,
IdentityHashMap   。它们都有同样的基本接口   Map   ,但是行为、效率、排序策略、保存对象的生命周期和判定“键”等价的
策略等各不相同。

Map   同
样对每个元素保存一份,但这是基于   "   键   "   的,   Map   也
有内置的排序,因而不关心元素添加的顺序。如果添加元素的顺序对你很重要,应该使用   LinkedHashSet   或者  LinkedHashMap.

执行效率是   Map   的
一个大问题。看看   get()   要
做哪些事,就会明白为什么在   ArrayList   中
搜索“键”是相当慢的。而这正是   HashMap   提
高速度的地方。   HashMap   使
用了特殊的值,称为“散列码”   (hash code)   ,
来取代对键的缓慢搜索。“散列码”是“相对唯一”用以代表对象的   int  值,
它是通过将该对象的某些信息进行转换而生成的(在下面总结二:需要的注意的地方有更进一步探讨)。所有   Java   对象都能产生散列码,因为   hashCode()   是定义在基类   Object   中的方法   。  HashMap   就是使用对象的   hashCode()   进行快速查询的。此方法能够显著提高性能。

Hashtable   类   
     Hashtable   继
承   Map   接口,实现一个   key-value   映射的哈希表。任何非空(   non-null  )的对象都可作为   key   或者   value   。   
  添加数据使用   put(key,
value)   ,取出数据使用   get(key)   ,
这两个基本操作的时间开销为常数。   
     Hashtable  通过初始化容量   (initial
capacity)  和负载因子   (load
factor)   两个参数调整性能。通常缺省的   load factor
0.75   较好地实现了时间和空间的均衡。增大   load
factor   可以节省空间但相应的查找时间将增大,这会影响像   get   和   put   这样的操作。   
     使用   Hashtable   的简单示例如下,将   1   ,   2   ,   3   放到   Hashtable   中,
他们的   key  分别是   ”one”   ,   ”two”   ,   ”three”   :   
       Hashtable
numbers = new Hashtable();
       numbers.put(“one”, new
Integer(1));
       numbers.put(“two”, new
Integer(2));
       numbers.put(“three”, new
Integer(3));
  要取出一个数,比如   2   ,
用相应的   key   :   
       Integer
n =
(Integer)numbers.get(“two”);
       System.out.println(“two =
” +
n);
     由于作为   key   的对象将通过计算其散列函数来确定与之对应的   value   的位置,因此任何作为  key   的对象都必须实现   hashCode   方法和   equals   方法。   hashCode   方法和   equals   方法继承自根类   Object   ,如果你用自定义的类当作   key   的话,要相当小心,按照散列函数的定义,如果两个对象相同,即   obj1.equals(obj2)=true   ,则它们的   hashCode   必须相同,但如果两个对象不同,则它们的   hashCode   不一定不同,如果两个不同对象的   hashCode   相同,这种现象称为冲突,冲突会导致操作哈希表的时间开销增大,所以
尽量定义好的   hashCode()   方法,能加快哈希表的操作。   
  如果相同的对象有不同的   hashCode   ,
对哈希表的操作会出现意想不到的结果(期待的   get   方
法返回   null   ),要避免这种问题,只需要牢记一条:要同时复写   equals   方法和   hashCode   方法,而不要只写其中一个。   
     Hashtable   是
同步的。   

HashMap   类   
        HashMap   和   Hashtable   类似,也是基于散列表的实现。不同之处在于   HashMap   是非同步的,并且允许   null   ,即   null
value   和   null
key   。将   HashMap   视为   Collection   时(  values()   方法可返回   Collection   ),插入和查询“键值对”的开销是固定的,但其迭代子操作时间开销和   HashMap  的容量成比例。因此,如果迭代操作的性能相当重要的话,不要将   HashMap   的初始化容量   (initial
capacity)   设得过高,或者负载因子   (load
factor)   过低。

     LinkedHashMap  类:类似于   HashMap   ,
但是迭代遍历它时,取得“键值对”的顺序是其插入次序,或者是最近最少使用   (LRU)   的
次序。只比   HashMap 慢一点。而在迭代访问时发而更快,因为它使用链表维护 内部次序。

WeakHashMap   类: 弱键(   weak
key   )   Map   是一种改进的   HashMap   ,它是为解决特殊问题设计的,对   key   实行   “   弱引用   ”   ,
如果一个   key   不再被外部所引用(没有   map   之外的引用),那么该   key   可以被垃圾收集器   (GC)   回收。

TreeMap   

基于红黑树数据结构的实现。查看“键”或“键值对”时,它们会被排序   (   次序由   Comparabel   或  Comparator   决定   )   。   TreeMap   的
特点在于,你得到的结果是经过排序的。   TreeMap   是
唯一的带有   subMap()   方法的   Map   ,它可以返回一个子树。

IdentifyHashMap   

使用   ==   代
替   equals()   对“键”作比较的   hash
map   。专为解决特殊问题而设计。

总结一:比较

1   
数组   (Array)   

数组类   (Arrays)

Java   所有“存储及随机访问一连串对象”的做法,   array   是最有效率的一种。但缺点是容量固定且无法动态改变。   array   还有一个缺点是,无法判断其中实际存有多少元素,   length   只是告诉我们   array  的容量。

Java   中有一个数组类   (Arrays)   ,专门用来操作   array   。数组类   (arrays)   中拥有一组   static  函数。

equals()   :比较两个   array   是否相等。   array   拥有相同元素个数,且所有对应元素两两相等。

fill()   :将值填入   array   中。

sort()   :用来对   array   进行排序。

binarySearch()   :在排好序的   array   中寻找元素。

System.arraycopy()   :   array   的复制。


编写程序时不知道究竟需要多少对象,需要在空间不足时自动扩增容量,则需要使用容器类库,   array  不适用。

2   , 容器类与数组的区别

容 器类仅能持有对象引用(指向对象的指针),而不是将对象信息   copy   一
份至数列某位置。一旦将对象置入容器内,便损失了该对象的型别信息。

3   
容器   (Collection)   
与   Map   
联系与区别

Collection   类型,每个位置只有一个元素。

Map   类型,持有   key-value  对   (pair)   ,像个小型数据库。

Collections   是针对集合类的一个帮助类。提供了一系列静态方法实现对各种集合的搜
索、排序、线程完全化等操作。相当于对   Array   进
行类似操作的类——   Arrays   。

如,   Collections.max(Collection
coll);  取   coll   中最大的元素。

Collections.sort(List
list);  对   list   中 元素排序

List   ,   Set   ,   Map   将持有对象一律视为   Object   型别。

Collection   、   List   、   Set   、   Map   都是接口,不能实例化。继承自它们的   ArrayList,
Vector, HashTable, HashMap   是
具象   class   ,这些才可被实例化。

vector   容器确切知道它所持有的对象隶属什么型别。   vector   不进行边界检查。

总结二:需要注意的地方

1   、   Collection   只
能通过   iterator()   遍历元素,没有   get()   方法来取得某个元素。

2   、   Set   和   Collection   拥有一模一样的接口。但排除掉传入的   Collection   参数重复的元素。

3   、   List   ,
可以通过   get()   方法来一次取出一个元素。使用数字来选择一堆对象中的一个,  get(0)...   。   (add/get)

4   、   Map   用   put(k,v)
/
get(k)   ,还可以使用   containsKey()/containsValue()   来
检查其中是否含有某个   key/value   。

HashMap   会利用对象的   hashCode   来快速找到   key   。


希码   (hashing)   就是将对象的信息经过一些转变形成一个独一无二的   int   值,这个值存储在一个  array   中。我们都知道所有存储结构中,   array   查找速度是最快的。所以,可以加速查找。发生碰撞时,让   array   指向多个   values   。即,数组每个位置上又生成一个梿表。

5   、   Map   中
元素,可以将   key   序列、   value   序列单独抽取出来。

使
用   keySet()   抽取   key   序列,将   map   中的所有   keys   生成一个   Set   。

使
用   values()   抽取   value   序列,将   map   中的所有   values   生成一个   Collection   。


什么一个生成   Set   ,一个生成   Collection   ?那是因为,   key   总是独一无二的,   value   允许重复。

总结三:如何选择   
从效率角度:


各种   Lists   ,对于需要快速插入,删除元素,应该使用   LinkedList   (可用   LinkedList   构造堆栈   stack   、队列   queue   ),如果需要快速随机访问元素,应该使用   ArrayList   。最好的做法是以  ArrayList   作为缺省选择。   Vector   总是比   ArrayList   慢,所以要尽量避免使用。


各种   Sets   中,   HashSet   通常优于   HashTree   (插入、查找)。只有当需要产生一个经过排序的序列,才用   TreeSet   。   HashTree   存在的唯一理由:能够维护其内元素的排序状态。


各种   Maps   中   HashMap   用于快速查找。

最 后,当元素个数固定,用   Array   ,
因为   Array   效率是最高的。


以结论:最常用的是   ArrayList   ,   HashSet   ,   HashMap   ,   Array   。

更近一步分析:


果程序在单线程环境中,或者访问仅仅在一个线程中进行,考虑非同步的类,其效率较高,如果多个线程可能同时操作一个类,应该使用同步的类。   
要特别注意对哈希表的操作,作为   key   的
对象要同时正确复写   equals   方
法和   hashCode   方法。  
尽量返回接口而非实际的类型,如返回   List   而非   ArrayList   ,这样如果以后需要将   ArrayList   换成   LinkedList   时,客户端代码不用改变。这就是针对抽象编程。

时间: 2024-10-09 22:45:47

Collection List Set和Map用法与区别的相关文章

List,Set,Map用法以及区别

List,Set,Map是否继承自Collection接口? 答:List,Set是,Map不是. 如图: Collection ├List │├LinkedList │├ArrayList │└Vector │ └Stack └Set Map ├Hashtable ├HashMap └WeakHashMap Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素.一些Collection允许相同的元素而另一些不行.一些能排序而另一些不

map()和foreach()区别------详细用法介绍

1.foreach.用法详解 //forEach使用姿势一: var numbers = [1, 2, 3, 4, 5], sum = 0; numbers.forEach(function(item) { sum += item; }); console.log(sum); //结果:15 //forEach使用姿势二: var numbers = [1, 2, 3, 4, 5], sum = 0; function adder(item) { sum += item; } //直接调用add

Java Collection框架—List\ set \map 的异同世界

Java集合是多个对象的容方法.集合(容方法),简单点,其实就是一个对象,能将具有相同性质的多个元素汇聚成一个整体. Collections Framwork是用来表现和操纵集合的一个统一的体系结构. 常用的集合有HashMap,HashSet,ArrayList--,因为对于每一个集合的内涵都很模糊,所以才会导致自己不知道具体的使用环境.我们通过对比来区分异同: Set(集) Set可以说是一种最简单的集合,主要的两个实现类,HashSet,TreeSet,两者最大的区别就是TreeSet是有

JAVA的容器---List,Map,Set的区别

Set 数据是不区分顺序的List 是分先后顺序的ArrayList 是 List的一个实现. arrayList 读快改慢,linkedList 是读慢改快 . 这跟它们的存储有关. ArrayList 顺序开辟空间一个挨着一个.LinkedList是存储人以位置,然后用指针关联 JAVA的容器---List,Map,Set Collection ├List │├LinkedList │├ArrayList │└Vector │ └Stack └Set Map ├Hashtable ├Hash

Java中Set Map List 的区别

java中set map list的区别: 都是集合接口 简要说明 set --其中的值不允许重复,无序的数据结构 list   --其中的值允许重复,因为其为有序的数据结构 map--成对的数据结构,健值必须具有唯一性(键不能同,否则值替换) List按对象进入的顺序保存对象,不做排序或编辑操作.Set对每个对象只接受一次,并使用自己内部的排序方法(通常,你只关心某个元素是否属于Set,而不关心它的顺序--否则应该使用List).Map同样对每个元素保存一份,但这是基于"键"的,Ma

javascript中window与document对象、setInterval与setTimeout定时器的用法与区别

一.写在前面 本人前端菜鸟一枚,学习前端不久,学习过程中有很多概念.定义在使用时容易混淆,在此给向我一样刚踏入前端之门的童鞋们归纳一下.今天给大家分享一下js中window与document对象.setInterval与setTimeout定时器的用法与区别.讲得不对的地方,烦请大家指正,还望前辈.大牛多多指教! 二.window对象与document对象的用法和区别 window是全局对象,document是window对象的一个属性它也是一个对象.如图: document对象指的页面这个文档

Linux中yum和apt-get用法及区别

Linux中yum和apt-get用法及区别 一般来说著名的linux系统基本上分两大类: 1.RedHat系列:Redhat.Centos.Fedora等 2.Debian系列:Debian.Ubuntu等 RedHat 系列 1 常见的安装包格式 rpm包,安装rpm包的命令是"rpm -参数" 2 包管理工具  yum 3 支持tar包 Debian系列 1 常见的安装包格式 deb包,安装deb包的命令是"dpkg -参数" 2 包管理工具 apt-get

PHP基础温习之echo print printf sprintf print_r var_dump的用法与区别

原文:PHP基础温习之echo print printf sprintf print_r var_dump的用法与区别 一.echoecho() 实际上不是一个函数,是php语句,因此您无需对其使用括号.不过,如果您希望向 echo() 传递一个以上的参数,那么使用括号会发生解析错误.而且echo是返回void的,并不返回值,所以不能使用它来赋值.例子: 复制代码代码如下: <?php $a = echo("55nav"); // 错误!不能用来赋值 echo "55n

jquery.fn.extend与jquery.extend用法与区别

关于jquery.fn.extend与jquery.extend用法与区别. jQuery为开发插件提拱了两个方法,分别是:  代码如下 复制代码 jQuery.fn.extend(object);  和   jQuery.extend(object); jQuery.extend(object); 为扩展jQuery类本身.为类添加新的方法. jQuery.fn.extend(object);给jQuery对象添加方法. fn 是什么东西呢.查看jQuery代码,就不难发现.  代码如下 复制