Java集合详细讲解

一、集合概述

Java集合为了保存数量不确定的数据,以及保存具有映射关系的数据。集合类主要负责保存、盛装其他数据,因此集合类也被称为容器类,集合类位于java.util包下。下面是集合类的两大框架:
Collection集合框架
Map集合框架
Collection接口里定义了如下操作集合元素的方法:

  • boolean add(Object o):用于向集合里添加一个元素
  • boolean addAll(Collection c):该方法把集合c里面的所有的元素添加到指定集合里
  • void clear():清除集合里的所有元素,将集合长度变为0
  • boolean contains(Object o):返回集合里是否包含指定元素
  • boolean containsAll(Collection c):返回集合是否包含集合c里的所有元素
  • boolean isEmpty():返回集合是否为空
  • Interator iterator():返回一个Interator对象,用于遍历集合里的元素
  • boolean remove(Object o):删除集合中指定元素
  • boolean removeAll(Collection c):从集合中删除集合c里的包含的所有元素
  • boolean retainAll(Collections c):从集合中删除集合c里不包含的元素(取并集)
  • int size():该方法返回集合里元素的个数
  • Object[] toArray():该方法把集合转换成一个数组,所有的集合元素变成对应的数组元素
  • Collection接口已重写toString()方法

二、Set 集合

2.1 HashSet

当向HashSet集合中存入一个元素时,HashSet会调用该对象的HashCode()方法来得到该对象的hashCode值,然后根据该hashCode值决定该对象在HashSet中的存储位置。如果有两个元素通过equals()返回比较返回true,但它们的hashCode()方法返回值不相等,HashSet将会把它们存储在不同的位置,依然可以添加成功。

hash算法的功能:它能快速查找被检查的对象,hash算法的价值在于速度。当需要查询集合中某个元素时,hash算法可以直接根据该元素的hashCode值计算出该元素的存储位置。

HashSet中每个能存储元素的“槽位”通常称为"桶"(bucket),如果有多个元素的hashCode值相同,但它们通过equals()方法比较返回false,就需要在一个“桶”里放多个元素,这样会导致性能下降。

HashSet的存储过程

当我们向HashSet中添加一个数据时。会先调用该数据的hashCode方法来决定该数据在数组中所存储的位置。

如果该位置上没有其他的元素,则将该数据直接存放即可。如果该位置已经有了其他的元素,调用该元素所在类的equals方法进行比较。如果返回值是个true则认为两个数据相同则不能存放。如果返回值是个false则以链表的形式将该数存在该位置上。

2.2 LinkHashSet

LinkHashSet是HashSet的子类,LinkHashSet集合也是根据元素的hashCode值来决定元素的存储位置,但它同时使用链表维护元素的次序。LinkHashSet按元素的添加顺序来访问集合里的元素。

LinkHashSet需要维护元素的插入顺序,因此性能略低于HashSet性能,但在迭代访问Set里的全部元素时将有很好的性能,因为它以链表来维护内部顺序。

2.3 TreeSet

TreeSet还提供了额外的方法Comparator compatator:如果TreeSet采用了定制排序,则该方法返回定制排序所使用的Comparator;如果TreeSet采用了自然排序,则返回null。

HashSet集合采用hash算法来决定元素的存储位置不同,TreeSet采用红黑树的数据结构来存储集合元素。

TreeSet用到如下两种排序算法:

① 自然排序

实现Comparable接口,重写compareTo(Object obj)

	@Override
	//当该类有多个属性需要比较时,多层嵌套if-else比较属性
	public int compareTo(Object o) {
		if(o instanceof Person){
			Person p = (Person) o;
			int tem  = this.age.compareTo(p.age);
			if(tem ==0){
				return this.name.compareTo(p.name);
			}else {
				return tem;
			}
		}
		return 0;
	}

② 定制排序

实现Comparator接口匿名实现类

	Comparator com = new Comparator() {
		@Override
		//当该类有多个属性需要比较时,多层嵌套if-else比较属性
		public int compare(Object o1, Object o2) {
			Person p1 =(Person) o1;
			Person p2 =(Person) o2;
			int i =- (p1.getAge().compareTo(p2.getAge()));
			if(i==0){
				return - (p1.getName().compareTo(p2.getName()));
			}else{
				return i;
			}	
		}
	};
	Map map = new TreeSet(com);

2.4 EnumSet

EnumSet类是一个专门为枚举类设计的集合类,EnumSet中的所有元素都必须是指定枚举类型的枚举值,该枚举类型在创建EnumSet时,显示或隐式的指定。EnumSet的集合元素也是有序,EnumSet以枚举值在Enum类内的定义顺序来决定集合元素的顺序。EnumSet集合中不允许插入null

Enum在内部以位向量的形式进行存储,这种存储形式非常紧凑、高效,因此EnumSet对象占用内存很小,而且运行效率很好。尤其是进行批量操作(如调用containsAll()retainAll())时,如果其其参数也是EnumSet集合,则该批量操作的执行效率也非常快。

EnumSet类没有暴露任何构造器来创建该类的实例,程序应该通过它提供的类方法来创建EnumSet对象。

enum Season{
	SPRING,SUMMER,FALL,WINTER;
}

public class EnumSetTest {
	@Test	
	public void test(){
		//创建EnumSet集合的方法,集合元素就是枚举类的所有元素
		EnumSet es1 = EnumSet.allOf(Season.class);
		System.out.println(es1);//[SPRING, SUMMER, FALL, WINTER]
		//创建一个EnumSet空集合,指定其集合元素是该类的枚举值
		EnumSet es2 = EnumSet.noneOf(Season.class);
		System.out.println(es2);//[]
		//手动添加两个元素(只能添加上枚举类的值)
		es2.add(Season.SPRING);
		es2.add(Season.SUMMER);
		System.out.println(es2);//[SPRING, SUMMER]
		//已指定枚举创建EnumSet集合
		EnumSet es3 = EnumSet.of(Season.SPRING,Season.FALL);
		System.out.println(es3);//[SPRING, FALL]
		//创建一个包含从from到to枚举值范围内所有枚举值的EnumSet集合。
		EnumSet es4 = EnumSet.range(Season.SPRING,Season.FALL);
		System.out.println(es4);//[SPRING, SUMMER, FALL]
		//es5中的枚举值=全部枚举值-es4
		EnumSet es5 = EnumSet.complementOf(es4);
		System.out.println(es5);//[WINTER]
		//copy
		EnumSet es6 = EnumSet.copyOf(es5);
		System.out.println(es6);//[WINTER]
	}
}

2.5 Set实现类性能分析

HashSetTreeSetSet的两个典型。HashSet的性能总是比TreeSet好(特别是常用的添加、查询元素等操作),因为TreeSet需要额外的红黑树算法来维护集合元素的次数。只有需要一个保持次序的Set时,才应该使用TreeSet,否则都应该使用HashSet

HashSet还有一个子类:LinkHashSet,对于普通的插入、删除操作,LinkHashSetHashSet要略慢一点,这是由维护链表所带来的额外开销造成的,但由于有了链表,遍历LinkHashSet会更块。

EnumSet是所有Set实现类中性能最好的,但它只能保存同一个枚举类的枚举值作为集合元素。

必须指出的是,Set的三个实现类HashSetTreeSetEnumSet都是线程不安全的。如果有多个线程同时访问一个Set集合,并且有一个线程修改了该Set集合,则必须手动该Set集合的同步性。通常可以通过Collections工具类的synchronizedSortedSet方法来“包装”该集合。此操作最好再创建时进行,以防止对Set集合的意外非同步访问

三、List 集合

3.1 List 解析

List作为Collection接口的子接口,当然可以使用Collection接口里的全部方法。而且由于List是有序集合,因此List集合里增加了一些根据索引来操作集合元素的方法:

  • void add(int index,Object element):将元素element插入到List集合的index
  • boolean addAll(int index,Collection c):将集合c所包含的所有的元素都插入到List集合的Index
  • Object get(int index):返回集合index索引出的元素
  • int indexOf(Object o):返回对象oList集合中第一次出现的位置索引
  • int lastIndexOf(Object o):返回对象oList集合中最后一次出现的位置索引
  • Object remove(int index):删除并返回index索引处的元素
  • Object set(int index,Object element):将index索引处的元素替换成element对象,返回被替代的旧元素
  • List subList(int fromIndex,int toIndex):返回从索引fromIndex(包含)到索引toIndex(不包含)处所有集合元素组成的子集合
  • void replaceAll(UnaryOperator operatao):根据operator指定的计算规则重新设置List集合的所有元素
  • void sort(Comparator c):根据Compatator参数对List集合的元素排序

Set只提供了一个interator()方法不同,List还额外提供了一个listIterator()方法,该方法返回一个ListIterator对象,ListIterator接口继承了Iterator接口,提供了专门操作List的方法。ListIterator接口在Iterator接口的基础上增加了如下方法:

  • boolean hasPrevious():返回迭代器关联集合是否还有上一个元素
  • Object previous():返回该迭代器的上一个元素
  • void add(Object o):在指定位置插入一个元素

ListIterator增加了向前迭代的功能

	while(listIterator.hasPrevious()){
		System.out.println(listIterator.previous());
	}

3.2 ArrayList 和 Vector 实现类

ArrayListVector类都是基于数组实现的List类,所以ArrayListVector类封装了一个动态的、允许在分配的Object[]数组。ArrayListVector对象使用initialCapacity参数来设置该数组的长度。当向ArrayListVector中添加元素超过了该数组的长度时,他们的initialCapacity会自动增加。

若已知该数组的大小可以手动设置initialCapacity的值。ArrayListVector提供了如下两个方法啦重新分配Object[]数组。

  • void ensureCapacity(int minCapacity):将ArrayListVector集合中的Object[]数组长度增加大于或等于minCapacity
  • void trimToSize():调整ArrayListVector集合的Object[]数组长度为当前元素的个数。调用该方法可以减少集合对象占用的存储空间

ArrayList是线程不安全的,Vector是线程安全的,但是ArrayList的性能比Vector要好的多,所以ArrayList被大部分使用。

Vector提供一个Stack子类,它用于模拟“栈”这种数据接口,“栈”通常是指“后进先出(LIFO)”的容器。最先push进栈的元素,将最先被pop出栈。Stack提供了一下几个方法:

  • Object peak():返回“栈”的第一个元素,但并将该元素pop出栈
  • Object pop():返回“栈”的第一个元素,并将该元素pop出栈
  • void push(Object item):将一个元素push进栈,最后一个进“栈”的元素总是位于"栈"顶

需要指出的是,由于Stack继承了Vector,因此他也是一个非常古老的Java集合类,它同时是线程安全、性能较差的,因此要尽量少用Stack类。如果程序需要使用“栈”这种数据结构,则可以考虑后面介绍的ArrayDequeArrayDeque也是List的实现类,ArrayDeque即实现了List接口,也实现了Deque接口。

3.3 固定长度的 List

Arrays工具类里提供了asLIst(Object…a)方法,该方法可以把一个数组或指定个数的对象转换成一个List集合,这个List集合既不是ArrayList实现类的实例,也不是Vector实现类的实例,而是Arrays的内部类ArrayList的实例。

Arrays.ArrayList是一个固定的长度的List集合,程序只能遍历访问该集合里的元素,不可增加、删除该集合里的元素。

	@Test
	public void test(){
		List fixedList = Arrays.asList("AA","BB",123);
		//获取fixedList的实现类,将输出Arrays$ArrayList
		System.out.println("fixedList.getClass()");
		//使用方法遍历集合元素
		fixedList.forEach(System.out::println);
		//试图增加、删除元素都会引发异常,该ArrayList不可被操作
	}

3.4 ArrayList 扩容

通过ArrayList空参的构造器创建对象。底层会创建一个长度为10的数组。当我们向数组中添加第11个元素时底层会进行扩容,扩容为原来的1.5倍(创建一个新的数组长度为原来的1.5倍并将原数组中的内容添加到新的数组中)

3.5 各线性表的性能分析

Java提供的List就是一个线性表接口,而ArrayListLinkedList又是线性表的两种典型表现;基于数组的线性表和基于链的线性表。Queue代表了队列,Deque代表了双端队列。

关于使用List集合有如下的建议:

  1. 如果需要遍历List集合元素,对于ArrayListVector集合,应该使用随机访问方法(get)来遍历集合元素,这样性能更好;对于LinkedList集合,则应该采用迭代器来遍历集合元素
  2. 如果需要经常执行插入、删除操作来改变包含大量数据的List集合的大小,可考虑使用LinkedList集合,使用ArrayListVector集合可能需要经常重新分配内部数组大小,效果肯能较差
  3. 如果有多个线程要同时访问List集合中的元素,开发者可考虑使用Collections将集合包装成线程安全的集合

四、Queue 集合

4.1 Queue 解析

Queue用于模拟队列这种数据结构,队列通常是指先进先出(FIFO)的容器。队列的头部保存在队列中存放时间最长的元素,队列的尾部保存在队列中存放时间最短的元素。新元素插入(offer)到队列的尾部,访问元素(poll)操作会返回队列头部的元素。通常,队列不允许随机访问队列中的元素。

Queue接口定义了如下几个方法:

  • void add(Object e):将指定元素加入此队列的尾部
  • Object element():获取队列头部的元素,但是不删除该元素
  • boolean offer(Object e):将指定元素加入此队列的尾部。当使用有容量限制的队列时,此方法通常比add(Object e)方法更好
  • Object peak():获取队列头部的元素,但是不删除该元素。若队列为空,则返回null
  • Object pull():获取队列头部的元素,并删除该元素。若队列为空,则返回null
  • Object remove():获取队列头部的元素,并删除该元素

4.2 PriotityQueue

PriotityQueue保存队列元素的顺序并不是按加入队列的顺序,而是按队列元素的大小进行排序。因此当调用peek()或者poll()方法取出队列中的元素时,并不是取出最先进入队列的元素,而是取出队列中最小的元素。其违反了队列的最基本的规则:先进先出(FIFO)。

	@Test
	public void test(){
		PriorityQueue pq = new PriotityQueue();
		pq.offer(6);
		pq.offer(-3);
		pq.offer(20);
		pq.offer(18);
		//输出pq队列,并不是按元素的加入顺序排列
		System.out.println(pq); //输出[-3,6,20,18]
		//访问队列的第一个元素,其实就是队列中最小的元素:-3
		System.out.println(pq.poll);
	}

运行上面程序直接输出PriotityQueue集合时,可能看到该队列中的元素并没有很好地按大小进行排序,但这只是受到PriotityQueuetoString()方法的返回值的影响。实际上,程序多次调用poll方法,即可看到元素按小到大的顺序移除队列。

同时PriotityQueue也有两种排序:自然排序和定制排序。

4.3 Deque 和 ArrayDeque

Deque接口是Queue接口的子接口,它表示一个双端队列,Deque接口里定义了 一些双端队列的方法,这些方法允许从两端来操作队列的元素(操作last方法省略,将first改为last即可)

  • void addFirst(Object e):将指定元素插入到该双端队列的开头
  • void addLast(Object e):将指定元素插入该双端队列的末尾
  • Iterator descendingIterator():返回该双端队列对应的迭代器,该迭代器将以逆向顺序来迭代队列中的元素
  • Object getFirst(Object e):获取但不删除双端队列的第一个元素
  • boolean offerFirst(Object e):将指定元素插入到该双端队列的开头
  • Object peakFirst():获取双端队列头部的元素,但是不删除该元素。若队列为空,则返回null
  • Object pullFirst():获取双端队列头部的元素,并删除该元素。若队列为空,则返回null
  • Object pop()(栈方法):pop出该双端队列所表示的栈顶元素。相当于removeFirst()方法
  • Object push()(栈方法):将一个元素push进该双端队列所表示的栈的栈顶。相当于addFirst()方法
  • Object removeFirst():获取并删除该双端队列的第一个元素
  • Object removeFirstOccurrence(Object o):删除该双端队列的第一个出现的元素

ArrayDeque

Deque接口提供了一个典型的实现类:ArrayDeque。它是一个基于数组实现的双端队列。创建Deque时可以指定numElements参数,若不指定,Deque底层数组的长度为16。

ArrayDeque即可以实现栈,也可以实现队列两种数据结构:

  • ArrayDeque:栈(LIFO)–使用push()入栈,使用pop()出栈
  • ArrayDeque:队列(FIFO)—使用offer()入队,使用poll()出队

4.4 LInkedList

LinkedList类是List接口的实现类,同时实现了Deque接口,可以被当做双端队列来实现,因此既可以被可以被当成“栈”来使用,还可以被当成队列使用。

LinkedListArrayListArrayDeque的实现机制完全不同,ArrayListArrayDeque内部以数组的形式来保存集合中的元素,因此随机访问集合元素时有较好的性能;而LinkedList内部以链表的形式来保存集合中的元素,因此随机访问集合时性能较差,但是在插入、删除元素时性能比较出色。

五、Map 集合

5.1 Map 解析

Map用于保存具有映射关系的数据,因此Map集合里保存这两组值,一组值用于保存Map里的Key,一组值用于保存Map里的ValueMap里的Key不允许重复,KeyValue之间存在单向一对一关系。Map包含一个keySet()方法,用于返回Map里所有Key组成的Set集合。Map提供了一个Entry内部类来封装KV对,而计算Entry存储时则只考虑Entry封装的Key。从Java源码来看,Java是先实现了Map,然后通过包装一个所有ValuenullMap就实现了Set集合。

Map接口中定义了常用的接口:

  • void clear():删除该Map对象中的所有KV

  • boolean containsKey(Object key):查询Map中是否包含指定的Key

  • boolean containsValue(Object value):查询Map中是否包含一个或多个Value

  • Set entrySet():返回Map中包含的KV对所组成的Set集合,每个集合元素都是Map.Entry对象

  • Set keySet():返回该Map中所有Key组成的Set集合

  • Collection values():返回该Map里所有Value组成的Collection

  • Object put(Object key,Object value):添加一个KV,若添加的Key已存在,那么会覆盖之前的KV

  • Object get(Object key):返回指定Key所对应的Value

  • boolean isEmpty():查询该Map是否为空

  • void putAll(Map m):将指定Map中的所有KV对赋值到该Map中

  • Object remove(Object key):删除指定Key所对应的KV队,返回被删除所Key所对应的Value

  • int size():返回Map里的KV对的个数

  • Map中包含一个内部类Entry,该类封装了一个KV对。Entry包含3个方法:

    • Object getKey():返回该Entry里包含的Key
    • Object getValue():返回该Entry里包含的Value
    • Object setValue(value):设置该Entry里包含的Value值,并返回新设置的Value

遍历Map

	//遍历keySet
    for(Object key:map.keySet()){
			//map.get(key)方法获取指定key对应的value
			System.out.println(key+"-->"+map.get(key));
		}
	//遍历entrySet
	for(Object o :map.entrySet()){
			Map.Entry entry = (Map.Entry)o;
			System.out.println(entry.getKey()+"-->"+entry.getValue());
		}

5.2 HashMap 和 HashTable

HashMapHashTable的关系就像ArrayListVector,后者差不多被淘汰了,很少被使用。后者线性安全不能插入null作为元素,通Vector一样。

HashMapKeySetSet集合中的对象必须实现equals()hashCode()方法。为了成功在HashMap中存储、获取对象,用做key的对象必须实现equals()hashCode()方法。

5.3 LinkedHashMap 实现类

LinkedHashMapHashMap的子类;LinkedHashMap也使用双向链表来维护KV对的次序,该链表负责维护Map的迭代顺序,迭代顺序与KV对的插入顺序保持一致。

LinkedHashMap可以避免对HashMapHashable里的KV对进行排序(只要插入KV对时保持顺序即可),同时又避免使用TreeMap所增加的成本

LinkedHashMap需要维护元素的插入顺序,因此性能略低于HashMap的性能。但因为它以链表来维护内部顺序,所以在迭代访问Map里的全部元素时将有较好的性能。

5.4 Properties

Properties类是Hashtable类的子类,正如它的名字所暗示,该对象在处理属性文件时特别方法(Windows操作平台上的ini文件就是一种属性文件)。Properties类可以把Map对象和属性文件关联起来,从而可以把Map对象中的KV对写入属性文件中,也可以把属性文件中“属性名=属性值”加载到Map对象。由于属性文件里的属性名、属性值只能是字符串类型,所以Properties里的keyvalue都是字符串类型。该类提供了如下三个方法来修改Properties里的keyvalue

Properties相当于一个keyvalue都是String类型的Map

  • String getProperty(String key):获取Properties中指定属性名,类似于Mapget(Object key)方法
  • String getProperty(String key,String defaultValue):该方法与前一个方法基本类似。该方法多一个功能,如果Properties中不存在指定的key时,则该方法指定默认值
  • Object setProperty(String key,String value):设置属性值,类似于Hashtableput()方法。除此之外它还提供了两个读写属性文件的方法
  • void load(InputStream inStream):从属性文件(以输入流表示)中加载KV对,把加载到的KV对追加到Properties里(PropertiesHashtable的子类,它不保证KV对之间的次序)
  • void store(OutputStream out,String comments):将Properties中的KV对输出到指定的属性文件中(以输出流表示)中
@Test
public void test(){
	Properties props = new Properties();
	//向Properties中添加属性
	props.setProperty("username","yeeku");
	props.SetProperty("password","123456");
	//将Properties中的key-value对保存到a.ini文件中
	pops.store(new FileOutputStream("a.ini"),"comment line");//-----------1
	//新建一个Properties对象
	Properties props2 =new Properties();
	//向Properties中添加属性
	props.setProperty("gender","male");
	///将a.ini文件中的key-value对追加到props2中
	props2.load(new FileInputStream("a.ini"));//-----------2
	System.out.println(props2);
}

上面程序示范了Properties类的用法 ,其中1代码处将Properties对象中的key-value对写入a.ini文件中。2代码处则从a.ini文件中读取key-value对,并添加到props2对象中。编译、运行上面程序,输出结果为:[password=123456,gender=male,username=yeeku]

上面程序还在当前路径下生成了一个a.ini文件,该文件的内容如下:

#comment line
#Thu Apr 17 00:40:22 CST 2014
password=123456
username=yeeku

5.5 SortedMap 和 TreeMap

    TreeMap是SortedMap的一个实现类
    TreeMap就是一个红黑树数据结构,每个key-value对即作为红黑树的一个节点。TreeMap存储key-value对(节点)时,需要根据key对节点进行排序。TreeMap可以保证所有key-value对处于有序状态。TreeMap也有两种排序方式:自然排序和定制排序。
    Set和Map的关系非常密切,Java源码也就是先实现了HashMap、TreeMap等集合,然后通过包装一个所有value都为null的Map集合实现了Set集合类。

        TreeMap中也提供了一系列根据key顺序访问key-value对的方法。(first->last)
        Map.Entry firstEntry():返回该Map中最小的key所对应的key-value对
        Object firstKey():返回该Map中的最小key值
        Map.Entry higherEntry(Object key):返回该Map中位于key后一位的key-value
        Object higherKey(Object key):返回该Map中位于key后一位的key
        NavigableMap subMap(Object formKey,boolean fromInclusive,Object toKey,boolean toInclusive):返回该Map的子Map(从fromKey到toKey,是否包含取决第2个和第4个参数是否为true)
        SortedMap tailMap(Object fromKey):返回该Map的子Map,其key的范围为>=fromKey
        SortedMap headMap(Object toKey):返回该Map的子Map,其key的范围是<toKey

5.6 WeakHashMap

WeakHashMapHashMap的用法基本相似。与HashMap的区别是在于,HashMapkey保留了队实际对象的强引用,这意味着只要该HashMap对象不被销毁,该HashMap的所有key所引用的对象就不会被垃圾回收,HashMap也不会自动删除这些key所对应的KV对;但WeakHashMapkey只保留了对实际对象的弱引用,这意味着如果WeakHashMap对象的key所引用的对象没有被其他强引用变量所引用,则这些key所引用的对象可能被垃圾回收,WeakHashMap也可能自动删除这些key所对应的KV

	WeakHashMap whm = new WeakHashMap();
	whm.put(new String("AAA"),new String("123"));
	//通知系统立即进行垃圾回收
	System.gc();
	System.runFinalization();
	//-------此时whm中已没有key-value

5.7 IndentityHashMap

IndentityHashMap在判断key是否相等时和HashMap不同,HashMap需要重写的equals()hashCode()都相等才判定是相等,而IndentityHashMap只需要判断key1==key2,如果相等就是相同的。

5.8 EnumMap

EnumMap中所有的key都必须是单个枚举类的枚举值,EnumMap有如下特点:

  • EnumMap内部以数组形式保存,所以这种实现形式非常紧凑、高效
  • EnumMap根据key的自然顺序(即枚举值在枚举类中的定义顺序)来维护KV对的顺序
  • EnumMap不允许nullkey,但允许nullvalue
	//创建Enum类
	EnumMap enumMap = new EnumMap(Season.class);

5.9 各 Map 实性能分析

对于Map的常用类而言,虽然HashMapHashtable的实现机制几乎一样,但由于Hashtable是一个古老的、线程安全的集合,因此HashMap通常比Hashtable更快。

TreeMap通常比HashMap要慢(尤其在插入、删除KV对时更慢),因为TreeMap底层采用红黑树来管理KV对。使用TreeMap有一个好处:TreeMap中的KV对总是处于有序状态,无须专门进行排序操作,当TreeMap被填充之后,就可以调用keySet(),取得由key组成的Set,然后使用toArray()方法生成key的数组,接下来使用ArraysbinarySearch()方法在已排序中快速查询对象。

对于一般的应用场景,程序应该多考虑使用HashMap,因为HashMap正是为快速查询设计的。但如果需要一个总是排序好的Map,则可以考虑使用TreeMap

5.10 HashSet、HashMap 扩容

对于HashSet来说,他们通过采用Hash算法来决定集合中元素的存储位置,并通过Hash算法来控制集合的大小;对于HashMap来说,它采用Hash算法来决定Mapkey的存储,并通过Hash算法来增加key集合的大小。

Hash表里可以存储元素的位置被称为“桶”(busket),在通常情况下,单个“桶”存储一个元素,此时有最好的性能;Hash算法可以根据hashCode值计算出“桶”的存储位置。
HashSetHashMapHash表包含如下属性:

  • 容量(capacity):Hash表中桶的数量
  • 初始化容量(initial capacity):创建Hash表的桶的数量
  • 尺寸(size):当前Hash表记录的元素
  • 负载因子(load factor):负载因子等于capacit/size、负载因子为0,表示空的Hash表,0.5表示半满的Hash表。轻负载的Hash表具有冲突,适宜插入与查询的特点(但是迭代时较慢)。HashSetHashMap默认的负载极限为0.75,当Hash表中75%被填满时,Hash表会发生rehashing

六、集合类工具类 Collections

① 排序操作

  • void shuffle(List list):对List集合元素进行随机排序(类似于洗牌)
  • void sort(List list):根据元素的自然顺序按升序进行排序
  • void sort(List list,Compatator c):对List进行升序排序
  • void swap(List list,int i,int j):交换元素
  • void rotate(List list,int distance)distance>0时,将list后面的distance个元素放到前面;distance<0时,将list前面的distance个元素放到后面

② 查询、替换操作

  • int binarySearch(List list,Object key):使用二分搜索法搜索指定的List集合,以获得指定对象在List集合的索引
  • Object max(Collection coll):根据自然顺序,返回集合中最大元素
  • Object max(Collection coll,Comparator comp):根据定制顺序,返回集合中最大元素
  • void fill(List list,Object obj):使用指定元素obj替换List集合中的所有元素
  • int frequency(Collection c,Object o):返回集合中指定元素出现的次数
  • int indexOfSubList(List source,List target):返回子List对象在父List对象中第一次出现的位置索引
  • int lastIndexOfSubList(List source,List target):返回子List对象在父List对象中最后一次出现的位置索引
  • boolean replaceAll(List list,Object oldVal,Object newVal):使用一个新值newVal替换List对象的所有旧值oldVal

③ 同步控制

Collections类提供了多个synchronizedXxx()方法,经方法可以将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题

④ 设置不可变集合

  • emptyXxx():返回一个空的、不可变的集合对象
  • singletonXxx():返回一个只包含指定对象的、不可变的集合对象
  • unmodifiableXxx():返回指定集合对象的不可变视图
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HuCheng1997

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值