Java容器List、Set、Map详解

1.List、Set、Map概述

       Collection List Set Map 这些都代表了Java中的集合,这里主要从其元素是否有序,是否可重复,是否线程安全来进行区别记忆,以便恰当地使用。List接口对Collection进行了简单的扩充,它的具体实现类常用的有ArrayList、LinkedList和Vector。ArrayList是一种类似数组的形式进行存储,因此它的随机访问速度极快;LinkedList的内部实现是链表,它适合于在链表中间需要频繁进行插入和删除操作;Vector是线程安全的ArrayList;一般的Iterator只能对容器进行向前遍历,而listIterator()则继承了Iterator的思想,并提供了对List进行双向遍历的方法(hasPrevious()和previous())。 
      Set接口也是Collection的一种扩展,而与List不同的时,在Set中的对象元素不能重复,也就是说你不能把同样的东西两次放入同一个Set容器中。它的常用具体实现有HashSet、TreeSet、LinkedHashSet类。HashSet能快速定位一个元素,但是你放到HashSet中的对象需要实现hashCode()方法,它使用了前面说过的哈希码的算法。而TreeSet则将放入其中的元素按序存放,这就要求你放入其中的对象是可排序的,这就用到了集合框架提供的另外两个实用类Comparable和Comparator。一个类是可排序的,它就应该实现Comparable接口。有时多个类具有相同的排序算法,那就不需要在每分别重复定义相同的排序算法,只要实现Comparator接口即可。集合框架中还有两个很实用的公用类:Collections和Arrays。Collections提供了对一个Collection容器进行诸如排序、复制、查找和填充等一些非常有用的方法,Arrays则是对一个数组进行类似的操作。 
      Map是一种把键对象和值对象进行关联的容器,而一个值对象又可以是一个Map,依次类推,这样就可形成一个多级映射。对于键对象来说,像Set一样,一个Map容器中的键对象不允许重复,这是为了保持查找结果的一致性;如果有两个键对象一样,那你想得到那个键对象所对应的值对象时就有问题了,可能你得到的并不是你想的那个值对象,结果会造成混乱,所以键的唯一性很重要,也是符合集合的性质的。当然在使用过程中,某个键所对应的值对象可能会发生变化,这时会按照最后一次修改的值对象与键对应。对于值对象则没有唯一性的要求。你可以将任意多个键都映射到一个值对象上,这不会发生任何问题(不过对你的使用却可能会造成不便,你不知道你得到的到底是那一个键所对应的值对象)。Map有比较常用的实现有:HashMap、HashTable、TreeMap。HashMap也用到了哈希码的算法,以便快速查找一个键;HashTable则是线程安全的HashMap;TreeMap则是对键按序存放,因此它便有一些扩展的方法,比如firstKey(),lastKey()等,你还可以从TreeMap中指定一个范围以取得其子Map。键和值的关联很简单,用pub(Object key,Object value)方法即可将一个键与一个值对象相关联。用get(Object key)可得到与此key对象所对应的值对象。

2.List、set、map的区别与联系

       1.在使用Java的时候,我们都会遇到使用集合(Collection、Map)的时候,但是Java API提供了多种集合的实现,总的说来,Java API中所用的集合类,都是实现了Collection、Map接口,其类结构如下:
     Collection<--List<--Vector                                      有序(顺序存储)                                线程安全
    Collection<--List<--ArrayList                                   有序(顺序存储)                                线程不安全
    Collection<--List<--LinkedList                                 有序(顺序存储)                                线程不安全
    Collection<--Set<--HashSet                                    无序排异                                        线程不安全
    Collection<--Set<--HashSet<--LinkedHashSet          有序排异                                         线程不安全
    Collection<--Set<--AbstractSet<--TreeSet               有序(二叉树排序)                              线程不安全
    Map<--HashMap                                                     无序                                               线程不安全
    Map<--HashMap<--LinkedHashMap                        有序(链表)                                       线程不安全
    Map<--HashTable                                                  无序                                               线程安全
    Map<--SortedMap<--TreeMap                                有序(二叉树排序)                              线程不安全

      2.基于Array的List,其实就是封装了Array所不具备的一些功能方便我们使用,它不可能走入Array的限制。性能也就不可能超越Array。所以,在可能的情况下,我们要多运用Array。另外很重要的一点就ArrayList同Vector一样是一个基于Array上的链表,但是不同的是ArrayList不是同步的。所以在性能上要比Vector优越一些,但是当运行到多线程环境中时,可需要自己管理线程的同步问题。LinkedList不同于前面两种List,它不是基于Array的,所以不受Array性能的限制。它每一个节点(Node)都包含两方面的内容:1.节点本身的数据(data);2.下一个节点的信息(nextNode)。所以当对LinkedList做添加,删除动作的时候就不用像基于Array的List一样,必须进行大量的数据移动。只要更改nextNode的相关信息就可以实现了。这就是LinkedList的优势,故LinkedList可以用来当做栈、队列和双向队列来使用。所有的List中只能容纳单个不同类型的对象组成的表,而不是Key-Value键值对,例如:[ tom,1,c ];所有的List中可以有相同的元素,例如Vector中可以有 [ tom,koo,too,koo ];所有的List中可以有null元素,例如[ tom,null,1 ];基于Array的List(Vector,ArrayList)适合查询,而LinkedList(链表)适合添加,删除操作。

      3.虽然Set同List都实现了Collection接口,但是他们的实现方式却大不一样。List基本上都是以Array为基础。但是Set则是在HashMap的基础上来实现的,这个就是Set和List的根本区别。HashSet的存储方式是把HashMap中的Key作为Set的对应存储项。看看HashSet的add(Object obj)方法的实现就可以一目了然了。这个也是为什么在Set中不能像在List中一样有重复的项的根本原因,因为HashMap的key是不能有重复的;

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

注意:LinkedHashSet:HashSet的一个子类,一个双向链表。TreeSet:SortedSet的子类,它不同于HashSet的根本就是TreeSet是有序的。它是通过SortedMap来实现的。Set实现的基础是Map(HashMap);Set中的元素是不能重复的,如果使用add(Object obj)方法添加已经存在的对象,则会覆盖前面的对象。
      4.Iterator和ListIterator主要区别在以下方面:ListIterator有add()方法,可以向List中添加对象,而Iterator不能;ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历,但是ListIterator有hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历。Iterator就不可以;ListIterator可以定位当前的索引位置,nextIndex()和previousIndex()可以实现。Iterator没有此功能;都可实现删除对象,但是ListIterator可以实现对象的修改,set()方法可以实现。Iierator仅能遍历,不能修改。 因为ListIterator的这些功能,可以实现对LinkedList等List数据结构的操作。

3.总结

      1.Java所有“存储及随机访问一连串对象”的做法,array是最有效率的一种。但容量固定且无法动态改变且无法判断其中实际存有多少元素,length只是告诉我们array的容量;若撰写程序时不知道究竟需要多少对象,需要在空间不足时自动扩增容量,此时array不适用则需要使用容器类库。
      2.在各种Lists中,最好的做法是以ArrayList作为缺省选择。当插入删除频繁时,使用LinkedList();Vector总是比ArrayList慢,所以要尽量避免使用。在各种Sets中,HashSet通常优于HashTree(插入、查找)。只有当需要产生一个经过排序的序列,才用TreeSet。HashTree存在的唯一理由:能够维护其内元素的排序状态。 在各种Maps中HashMap用于快速查找。当元素个数固定,用Array,因为Array效率是最高的。即最常用的是ArrayList,HashSet,HashMap,Array。
      3、Collection没有get()方法来取得某个元素只能通过iterator()遍历元素;Set和Collection拥有一模一样的接口;一般的Iterator只能对容器进行向前遍历,而listIterator()则继承了Iterator的思想,并提供了对List进行双向遍历的方法;一般使用ArrayList。用LinkedList构造堆栈stack、队列queue。

      4、Map用 put(k,v) / get(k),还可以使用containsKey()/containsValue()来检查其中是否含有某个key/value;HashMap会利用对象的hashCode来快速找到key;Map中元素,可以将key序列、value序列单独抽取出来。使用keySet()抽取key序列,将map中的所有keys生成一个Set。使用values()抽取value序列,将map中的所有values生成一个Collection。为什么一个生成Set,一个生成Collection?那是因为,key总是独一无二的,value允许重复。
       5、Map常用的四种遍历方式。如下代码:

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class Test {
public static void main(String[] args) {
		Map<String, Object> map = new HashMap<>();
		map.put("name", "luna");
		map.put("age", 20);
		/**
		 * 1.根据map的entrySet()遍历map容器
		 */
		Set<Entry<String, Object>> set = map.entrySet();
		for (Entry<String, Object> entry : set) {
			System.out.println("map容器的key为:" + entry.getKey() + "对应的value值为—" + entry.getValue());
		}
		// 2.迭代器遍历Map容器
		Iterator<Entry<String, Object>> iterator = map.entrySet().iterator();
		while (iterator.hasNext()) {
			Entry<String, Object> entry = iterator.next();
			System.out.println("map容器的key为:" + entry.getKey() + "对应的value值为—" + entry.getValue());
		}
		/**
		 * 3.根据map的keySet()遍历map容器
		 */
		Set<String> ketSet = map.keySet();
		for (String string : ketSet) {
			System.out.println("map容器的key为:" + string + "对应的value值为—" + map.get(string));
		}
		// 4.迭代器遍历Map容器
		Iterator<String> iterators = map.keySet().iterator();
		while (iterators.hasNext()) {
			String key = iterators.next();
			System.out.println("map容器的key为:" + key + "对应的value值为—" + map.get(key));
		}
		// 只能获map容器中的所有value的集合
		Collection<Object> collection = map.values();
		for (Object object : collection) {
			System.out.println(object);
		}
	}
}

猜你喜欢

转载自blog.csdn.net/u011635492/article/details/80343908