java容器

来源:互联网 发布:智能手环功能原理 知乎 编辑:程序博客网 时间:2024/04/28 23:49
1.迭代器 


  迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。 


  Java中的Iterator功能比较简单,并且只能单向移动: 


  (1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。 


  (2) 使用next()获得序列中的下一个元素。 


  (3) 使用hasNext()检查序列中是否还有元素。 


  (4) 使用remove()将迭代器新返回的元素删除。 


  Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。 


2、Collection接口


  Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。一些Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的“子接口”如List和Set。


  所有实现Collection接口的类都必须提供两个标准的构造函数:无参数的构造函数用于创建一个空的Collection,有一个Collection参数的构造函数用于创建一个新的Collection,这个新的Collection与传入的Collection有相同的元素。后一个构造函数允许用户复制一个Collection。


 由Collection接口派生的两个接口是List和Set。


Collection 和 Collections的区别
 
Collections是个java.util下的类,它包含有各种有关集合操作的静态方法,实现对各种集合的搜索、排序、线程安全化等操作。


Collection是个java.util下的接口,它是各种集合结构的父接口。继承自它的接口主要有Set 和List.


3.List的功能方法 


  List(interface): 次序是List最重要的特点;它确保维护元素特定的顺序。List为Collection添加了许多方法,使得能够向List中间插入与移除元素(只推荐 LinkedList使用)。一个List可以生成ListIterator,使用它可以从两个方向遍历List,也可以从List中间插入和删除元素。 


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


  LinkedList: 对顺序访问进行了优化,向List中间插入与删除得开销不大,随机访问则相对较慢(可用ArrayList代替)。它具有方法addFirst()、 addLast()、getFirst()、getLast()、removeFirst()、removeLast(),这些方法(没有在任何接口或基 类中定义过)使得LinkedList可以当作堆栈、队列和双向队列使用。 注意LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:


    List list = Collections.synchronizedList(new LinkedList(...));




4.Set的功能方法 


  Set(interface): 存入Set的每个元素必须是唯一的,因为Set不保存重复元素。加入Set的Object必须定义equals()方法以确保对象的唯一性。Set与Collection有完全一样的接口。Set接口不保证维护元素的次序。 Set最常被使用的是测试归属性,可以很容易的测试某个对象是否在这个集合中。通常选择HashSet


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


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


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


  HashSet采用散列函数对元素进行排序,这是专门为快速查询而设计的;TreeSet采用红黑树的数据结构进行排序元 素;LinkedHashSet内部使用散列以加快查询速度,同时使用链表维护元素的次序,使得看起来元素是以插入的顺序保存的。需要注意的是,生成自己 的类时,Set需要维护元素的存储顺序,因此要实现Comparable接口并定义compareTo()方法


无论使用哪种Set,都需要定义equals(),但是只有在“要把对象放进HashSet”的情况下,才需要定义hashCode().因为HashSet是我们通常用的Set,所以通常也需要定义hashCode()。作为一种编程风格,你应该在覆写equals()的同时把hashCode()也覆写了。


equals和hashcode方法: 


    hashCode()是Object根类的方法(缺省情况下返回对象的内存地址),因此所有java对象都能生成hash code。HashMap利用对象的hashCode()来进行快速的查找。


    equals()是Object根类的方法,只是简单地比较两个对象的地址。
 
    无论使用哪种Set,都需要定义equals(),但是只有在“要把对象放进HashSet”的情况下,才需要定义hashCode().因为HashSet是我们通常用的Set,所以通常也需要定义hashCode()。


    如果你不覆写键的hashCode()和equals()的话,散列数据结构(HashSet,HashMap,LinkedHashSet,LinkedHashMap)就没法正确的处理键。


 
   Collection类对象是否相等对象在调用remove、contains 等方法时需要比较,这会涉及到对象类型的 equals 方法和hashCode方法;对于自定义的类型,需要重写equals 和 hashCode 方法以实现自定义的对象相等规则。Java中规定,两个内容相同的对象应该具有相等的 hashcode 。
   什么时候需要我们重写equal,hashcode方法? 目的是什么?
   这样作的目的就是为了你的类就能够很好的与java的集合框架协同工作。如果我们能够确认我们定义的类不会和java集合类产生关系,那么我们完全没有必要在覆写equals()方法的时候覆写hashCode。  
   如下情况,可能需要我们重写equal/hashcode方法: 
   要将我们自定义的对象放入HashSet中处理; 要将我们自定义的对象作为HashMap的key处理; 放入Collection容器中的自定义对象后,可能会调用remove,contains等方法时。  
   Equal和hashcode的关系和原理: 1. Hashcode并不是内存地址,是内存地址转换而来的。系统通过它也可以确定内存地址。 2. hashcode方法主要用在集合框架中,目的是为了快速比较两个对象是否相等,因为集合框架中的对象很多,每个都使用equals比较效率很差。  
   每个对象都有一个hashcode,规定:  1、内容相同的对象hashcode肯定相等  2、内容不相同的对象hashcode可能相等也可能不相等   
   所以如果两个对象的hashcode不相等则两个对象的内容肯定不相等,这样就不必一个一个去比较属性的值了,从而提高对象比较的速度。
   
   
5.Map
    在Map对象中,每一个关键字最多有一个关联的值。 不能包括两个相同的键,一个键最多能绑定一个值。null可以作为键,这样的键只有一个;可以有一个或多个键所对应的 
    值为null。当get()方法返回null值时,即可以表示Map中没有该键,也可以表示该键所对应的值为null。因此,在Map中不能由get()方法来判断Map中是否存在某个键,而应该用containsKey()方法来判断。
   Map可以成为映射表或关联数组。它的基本思想是维护键-值关联。可以使用键来查找值。关于Map的基本实现,主要有如下几种:HashMap、TreeMap、LinkedHashMap、WeakHashMap、ConcurrentHashMap和IndentityHashMap。下面主要介绍一下前三种:
   
   HashMap:基于散列表的实现。插入和查询“键值对”的开销是固定的。可以通过构造器来指定初始容量和负载因子以调整容器的性能。(容量:散列表的桶数;尺寸:当前存储的项数;负载因子:尺寸/容量。负载轻的表产生冲突的可能性小(空表负载0,半满表负载0.5),因此对插入和查找都是最理想的(但会减慢迭代速度)。当达到负载因子的水平时,容器将自动增加容量(桶数),并重新将现有对象分布到新的桶位集中)。
   
   LinkedHashMap:类似于HashMap,但迭代时保持插入顺序。查找比HashMap慢一点,但有更快的迭代速度,因为使用链表维持内部顺序。
   TreeMap:基于红黑树的实现,查看键或键值对的时候会被排序(次序由Comparable或Comparator决定)
   
   选择Map:第一选择是HashMap,只有需要顺序的时,才使用TreeMap。LinkedHashMap在插入时要比HashMap慢一些,因为它要维持链表以保证插入顺序。正因为LinkedHashMap有保持顺序的链表,所以它的迭代速度更快。


6、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是同步的。


 


7、HashMap类


  HashMap和Hashtable类似,不同之处在于HashMap是非同步的,并且允许null,即null value和null key。,但是将HashMap视为Collection时(values()方法可返回Collection),其迭代子操作时间开销和HashMap的容量成比例。因此,如果迭代操作的性能相当重要的话,不要将HashMap的初始化容量设得过高,或者load factor过低。


 
HashMap和Hashtable的区别
 
HashTable的应用非常广泛,HashMap是Hashtable的轻量级实现(非线程安全的实现),HashMap是新框架中用来代替HashTable的类,也就是说建议使用HashMap,不要使用HashTable。
1.HashTable的方法是同步的,HashMap未经同步,所以在多线程场合要手动同步HashMap这个区别就像Vector和ArrayList一样。
2.HashTable不允许null值(key和value都不可以),HashMap允许null值(key和value都可以)。
3.HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因为contains方法容易让人引起误解。
4.HashTable使用Enumeration,HashMap使用Iterator。
 
 


8、WeakHashMap类


  WeakHashMap是一种改进的HashMap,它对key实行“弱引用”,如果一个key不再被外部所引用,那么该key可以被GC回收。


 
 9、在多线程操作
 Vector和HashTable是线程安全的


LinkedList、ArrayList和HashMap是线程不安全的


由于同步需要花费时间,因此线程安全的执行效率要低于线程不安全的






案例:多线程操作导致List报NoSuchElementException


java.util.NoSuchElementException
    at java.util.LinkedList.remove(LinkedList.java:788)
    at java.util.LinkedList.removeFirst(LinkedList.java:134)
    at freemarker.core.RegexBuiltins.getPattern(RegexBuiltins.java:138)


解决方法:


调用Collections的同步List
    List<String> items = Collections.synchronizedList(new LinkedList<String>()); 


    public void remove() {  
            if (!items.isEmpty()) {  
                return items.remove(0);  
              }  


    }


设置标志,同步
    LinkedList<String> items = new LinkedList<String>(); 
    String flag="abcdef"; 
    public void remove() {  
        synchronized(flag){ 
            if (!items.isEmpty()) {  
                return items.removeFirst();  
              }  


        }
    } 
0 0
原创粉丝点击