java之集合框架

Java集合框架(Java Collections Framework,JCF)是为表示和操作集合而规定的一种统一的标准的体系结构。

集合框架被设计成要满足以下几个目标:

  • 该框架必须是高性能的。基本集合(动态数组,链表,树,哈希表)的实现也必须是高效的。

  • 该框架允许不同类型的集合,以类似的方式工作,具有高度的互操作性。

  • 对一个集合的扩展和适应必须是简单的。

为此,整个集合框架就围绕一组标准接口而设计。你可以直接使用这些接口的标准实现,诸如: LinkedListHashSet, 和 TreeSet 等,除此之外你也可以通过这些接口实现自己的集合。

目录

集合

集合框架

1、 Collection

2、list

2.1 ArrayList

2.2 LinkedList

3、Set

3.1 HashSet

3.2 LinkedHashSet

3.3 TreeSet

3.4 HashSet和LinkedHashSet判定元素重复的原则

3.5 TreeSet判定元素重复的原则

4 、Map

4.1 HashMap

4.2 TreeMap


集合

        通常情况下,把具有相同性质的一类东西,汇聚成一个整体,就可以称为集合。比如,所有程序员,全体中国人等。通常集合有两种表示法,一种是列举法,比如集合A={1,2,3,4},另一种是性质描述法,比如集合B={X|0<X<100且X属于整数}。集合在小学或者初中都学过了,就不多说了。

集合框架

        有了集合的概念,什么是集合框架呢?集合框架是为表示和操作集合而规定的一种统一的标准的体系结构。最简单的集合如数组、列表和队列等,集合框架最著名的例子如C++标准库(STL)。任何集合框架一般包含:对外的接口、接口的实现和对集合运算的算法。

        接口:即表示集合的抽象数据类型。接口提供了让我们对集合中所表示的内容进行单独操作的可能。例如 Collection、List、Set、Map 等。之所以定义多个接口,是为了以不同的方式操作集合对象

        实现:也就是集合框架中接口的具体实现。实际它们就是那些可复用的数据结构。例如:ArrayList、LinkedList、HashSet、HashMap。

        算法:在一个实现了某个集合框架中的接口的对象身上完成某种有用的计算的方法,例如查找、排序等。这些算法通常是多态的,因为相同的方法可以在同一个接口被多个类实现时有不同的表现。事实上,算法是可复用的函数。如果你学过C++,那C++中的标准模版库(STL)你应该不陌生,它是众所周知的集合框架的绝好例子。

 上图所展示的仅是一部分内容,而且是java2版本之后的才出现的,在java1.2之前,java是没有完整的集合框架的。它只有一些简单的可以自扩展的容器类,比如Vector,Stack,Hashtable等。Vector中包含的元素可以通过一个整型的索引值取得,它的大小可以在添加或移除元素时自动增加或缩小。Stack是一种后进先出(LIFO)的堆栈序列,学过数据结构的都会知道,它的重要特点是先放入的东西最后才能被取出。Hashtable与Java2中的Map类似,可以看成一种关联或映射数组,可以将两个或多个毫无关系的对象相关联,与数组不同的是它的大小可以动态变化。然而Java1容器类库设计存在太多的缺陷。在这里就不详细将这个版本。java2之后,Java2中容器类库设计者对以前的拙劣设计进行了大刀阔斧的整改,从而使Java变得更加完整。

Java2中的容器类库才可以说是一种真正意义上的集合框架的实现。基本完全重新设计,但是又对Java1中的一些容器类库在新的设计上进行了保留,这主要是为了向下兼容的目的,当用Java2开发程序时,还是尽量避免使用它们吧。

Java2的集合框架,其核心主要有三类:List、Set和Map。List和Set继承了Collection,而Map则独成一体。

1、 Collection

Collection 是最基本的集合接口,一个 Collection 代表一组Object,即 Collection 的元素, Java不提供直接继承自Collection的类,只提供继承于的子接口 (如List和set)。Collection 接口存储一组不唯一,无序的对象。Collection接口提供了一组操作成批对象的方法。它提供了基本操作,如添加、删除。它也支持查询操作如是否为空isEmpty()方法等。为了支持对Collection进行独立操作,Java的集合框架给出了一个Iterator,它使得可以用泛型操作一个Collection,而不需知道这个Collection的具体实现类型是什么。

Collection提供的功能
boolean add(Object)确保容器能持有你传给它的那个参数。如果没有把它加进去,就返回false。
boolean addAll(Collection)加入参数Collection所含的所有元素。只要加了元素,就返回true。
void clear()清除容器所保存的所有元素
boolean removeAll(Collection)删除容器里面所有参数Collection所包含的元素。只要删过东西,就返回true。
int size()返回容器所含元素的数量。
Object[] toArray()返回一个包含容器中所有元素的数组。
Object[] toArray(Object[] a)返回一个包含容器中所有元素的数组,且这个数组不是普通的Object数组,它的类型应该同参数数组a的类型相同(要做类型转换)。
boolean contains(Object)如果容器持有参数Object,就返回true。
boolean containsAll(Collection)如果容器持有参数Collection所含的全部元素,就返回true。 boolean isEmpty():如果容器里面没有保存任何元素,就返回true。
boolean retainAll(Collection)只保存参数Collection所包括的元素(集合论中“交集”的概念)。如果发生过变化,则返回true。

2、list

List接口对Collection进行了简单的扩充。List接口是一个有序的 Collection,使用此接口能够精确的控制每个元素插入的位置,能够通过索引(元素在List中位置,类似于数组的下标)来访问List中的元素,第一个元素的索引为 0,而且允许有相同的元素。List 接口存储一组不唯一,有序(插入顺序)的对象。List的最重要的特征就是有序;它会确保以一定的顺序保存元素。List在Collection的基础上添加了大量方法,使之能在序列中间插入和删除元素。List可以制造ListIterator对象,ListIterator继承了Iterator的思想,除了能用它在List的中间插入和删除元素之外,还能用它沿两个方向遍历List。

它的具体实现类常用的有ArrayListLinkedList

2.1 ArrayList

         ArrayList实现了可变大小的数组,所以可以快速随机访问和遍历元素。该类是非同步的,在多线程的情况下不要使用。ArrayList 在元素填满容器时会自动扩充当前容器大小的50%,插入删除效率低。尤其不适合指定位置的插入和删除操作,主要用于数据的查询。Vector与ArrayList基本相同,唯一不同的是Vector是同步的。

ArrayList 类位于 java.util 包中,使用前需要引入它,语法格式如下:

import java.util.ArrayList; // 引入 ArrayList 类

ArrayList<E> objectName =new ArrayList<>();  // 初始化

//E: 泛型数据类型,用于设置 objectName 的数据类型,只能为引用数据类型。
//objectName: 对象名。

 注意:<E>只能为引用类型,我们需要用到基本类型的包装类。后面所说的LinkedList类以及Set接口、Map接口中的类的都是只能使用引用类型。

基本类型引用类型
booleanBoolean
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter

以下代码,用来具体实现ArrayList类,并使用三种遍历方法,对ArrayList类进行性能测试。最后结果迭代器遍历的效果稍微差一点。

import java.util.ArrayList;
import java.util.Iterator;

public class ArrayListTest {
	public static void main(String[] a) {  
	    ArrayList<Integer> al = new ArrayList<Integer>();  
	    al.add(3);  
	    al.add(2);          
	    al.add(1);  
	    al.add(4);  
	    al.add(5);  
	    al.add(6);   
	  
	    System.out.print("The third element is  ");
	    System.out.println(al.get(3));//get()方法,返回指定位置的元素。
	    al.remove(3);  //删除第四个元素,后面的元素往前挪
	    al.add(3, 9);  //将9插入到第三个元素,后面的元素往后挪
	    
	    System.out.println("======遍历方法=============");
	    
	    ArrayList<Integer> as = new ArrayList<Integer>(100000);
	    
	    for (int i=0; i<100000; i++)
	    {
	    	as.add(i);
	    }
	    traverseByIterator(as);//迭代器遍历
	    traverseByIndex(as);//索引值遍历
	    traverseByFor(as);//for-each遍历
	}  
	public static void traverseByIterator(ArrayList<Integer> al)
	{
		long startTime = System.nanoTime();
		System.out.println("============迭代器遍历=============="); 
	    Iterator<Integer> iter1 = al.iterator();  
	    while(iter1.hasNext()){  
	        iter1.next();  
	    }
		long endTime = System.nanoTime();
	    long duration = endTime - startTime;
	    System.out.println(duration + "纳秒");
	}
	public static void traverseByIndex(ArrayList<Integer> al)
	{
		long startTime = System.nanoTime();
		System.out.println("============索引值遍历=============="); 
	    for(int i=0;i<al.size();i++)
	    {
	    	al.get(i);
	    }
		long endTime = System.nanoTime();
	    long duration = endTime - startTime;
	    System.out.println(duration + "纳秒");
	}
	public static void traverseByFor(ArrayList<Integer> al)
	{
		long startTime = System.nanoTime();
		System.out.println("============for-each遍历=============="); 
	    for(Integer i : al)
	    {
	    	;
	    }
		long endTime = System.nanoTime();
	    long duration = endTime - startTime;
	    System.out.println(duration + "纳秒");
	}
}

2.2 LinkedList

LinkedList是以双向链表实现的列表。同样是不同步的,不可用于多线程。顺序访问比较高效,随机访问效率较差。数据中间插入和删除效率比较高。可以被当做堆栈、队列、双向队列进行操作。适用于经常变化的数据。它与ArrayList各有所长,在使用时要考虑他们的优缺点。

LinkedList 类位于 java.util 包中,使用前需要引入它,语法格式如下:

// 引入 LinkedList 类
import java.util.LinkedList; 

LinkedList<E> list = new LinkedList<E>();   // 普通创建方法
或者
LinkedList<E> list = new LinkedList(Collection<? extends E> c); // 使用集合创建链表

以下代码,用来具体实现LinkedList类,并使用三种遍历方法,对LinkedList类进行性能测试。最后结果索引值遍历的效果比较差。因此对于LinkedList来说,从头开始顺序遍历的方法不合适。


import java.util.Iterator;
import java.util.LinkedList;

public class LinkedListTest {

	public static void main(String[] args) {
		LinkedList<Integer> ll = new LinkedList<Integer>();  
	    ll.add(3);  
	    ll.add(2);  
	    ll.add(5);  
	    ll.add(6);  
	    ll.add(6);  
	    System.out.println(ll.size());
	    ll.addFirst(9);  //在头部增加9
	    ll.add(3, 10);   //将10插入到第四个元素,后面的元素向后移动
	    ll.remove(3);    //将第四个元素删除
	    
	    LinkedList<Integer> list = new LinkedList<Integer>();
	    
	    for (int i=0; i<100000; i++)
	    {
	    	list.add(i);
	    }
	    traverseByIterator(list);
	    traverseByIndex(list);
	    traverseByFor(list);    
	}
	
	public static void traverseByIterator(LinkedList<Integer> list)
	{
		long startTime = System.nanoTime();
		System.out.println("============迭代器遍历=============="); 
	    Iterator<Integer> iter1 = list.iterator();  
	    while(iter1.hasNext()){  
	        iter1.next();  
	    }
		long endTime = System.nanoTime();
	    long duration = endTime - startTime;
	    System.out.println(duration + "纳秒");
	}
	public static void traverseByIndex(LinkedList<Integer> list)
	{
		long startTime = System.nanoTime();
		System.out.println("============索引值遍历=============="); 
	    for(int i=0;i<list.size();i++)
	    {
	    	list.get(i);
	    }
		long endTime = System.nanoTime();
	    long duration = endTime - startTime;
	    System.out.println(duration + "纳秒");
	}
	public static void traverseByFor(LinkedList<Integer> list)
	{
		long startTime = System.nanoTime();
		System.out.println("============for-each遍历=============="); 
	    for(Integer item : list)
	    {
	    	;
	    }
		long endTime = System.nanoTime();
	    long duration = endTime - startTime;
	    System.out.println(duration + "纳秒");
	}
}

3、Set

Set接口也是Collection的一种扩展。与List不同的是,Set 不保存重复的元素。Set 接口存储一组唯一、无序的对象。Set的接口就是Collection的,所以不像那两个List,它没有额外的功能。实际上Set确确实实就是一个Collection,只不过行为方式不同罢了。

它的常用具体实现有HashSetTreeSetLinkedHashSet类。另外,需要注意一点的是:remove方法要与List接口区分,List接口中类中remove方法是删除指定位置的值。而Set接口的类中的remove方法是删除指定的值。

3.1 HashSet

HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合。HashSet 允许有 null 值,但只能有一个。HashSet 是无序的,即不会记录插入的顺序。HashSet 不是线程安全的, 如果多个线程尝试同时修改 HashSet,则最终结果是不确定的。 必须在多线程访问时显式同步对 HashSet 的并发访问。HashSet是为优化查询速度而设计的Set。要放进HashSet里面的Object还得定义hashCode()。

HashSet能快速定位一个元素,但是你放到HashSet中的对象需要实现hashCode()方法,它使用了前面说过的哈希码的算法。HashSet保存对象的顺序是和TreeSet和LinkedHashSet不一样的。这是因为它们是用不同的方法来存储和查找元素的。HashSet使用了“专为快速查找而设计”的散列函数。

HashSet 类位于 java.util 包中,使用前需要引入它,语法格式如下:

import java.util.HashSet; // 引入 HashSet 类

//以下实例我们创建一个 HashSet 对象 sites,用于保存字符串元素:

HashSet<String> sites = new HashSet<String>();

以下代码,用来具体实现HashSet类,并使用两种遍历方法,对HashSet类进行性能测试。最后结果迭代器遍历的效果比较差。

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;

public class HashSetTest {
	public static void main(String[] args) {
		HashSet<Integer> hs = new HashSet<Integer>();
		hs.add(null);
		hs.add(1000);
		hs.add(20);
		hs.add(3);
		hs.add(40000);
		hs.add(5000000);
		hs.add(3);                      //3重复
		hs.add(null);                   //null重复
		System.out.println(hs.size());  //6
		if(!hs.contains(6))
		{
			hs.add(6);
		}
		System.out.println(hs.size());  //7
		hs.remove(4);//没有作用,因为前面加入的值没有4
		System.out.println(hs.size());  //7
		//hs.clear();
		//System.out.println(hs.size());  //0
		
		System.out.println("============for-each遍历=============="); 
	    for(Integer item : hs)
	    {
	    	System.out.println(item);
	    }//由此处输出可以看出HashSet遍历顺序是无序的
	    
	    System.out.println("============测试集合交集==============");
	    
	    HashSet<String> set1 = new HashSet<String>();
	    HashSet<String> set2 = new HashSet<String>();

        set1.add("a");
        set1.add("b");
        set1.add("c");

        set2.add("c");
        set2.add("d");
        set2.add("e");

        //交集
        set1.retainAll(set2);
        System.out.println("交集是 "+set1);
        
        System.out.println("============测试多种遍历方法速度==============");
		
		HashSet<Integer> hs2 = new HashSet<Integer>();
		for(int i=0;i<100000;i++)	{
			hs2.add(i);
		}
		traverseByIterator(hs2);
		traverseByFor(hs2);		
	}
	
	public static void traverseByIterator(HashSet<Integer> hs)
	{
		long startTime = System.nanoTime();
		System.out.println("============迭代器遍历=============="); 
	    Iterator<Integer> iter1 = hs.iterator();  
	    while(iter1.hasNext()){  
	        iter1.next();  
	    }
		long endTime = System.nanoTime();
	    long duration = endTime - startTime;
	    System.out.println(duration + "纳米");
	}
	public static void traverseByFor(HashSet<Integer> hs)
	{
		long startTime = System.nanoTime();
		System.out.println("============foreach遍历=============="); 
	    for(Integer item : hs)
	    {
	    	;
	    }
		long endTime = System.nanoTime();
	    long duration = endTime - startTime;
	    System.out.println(duration + "纳米");
	}
}

3.2 LinkedHashSet

基本与HashSet一致,它是一个在内部使用链表的Set,既有HashSet的查询速度,又能保存元素被加进去的顺序(插入顺序)。用Iterator遍历Set的时候,它是按插入顺序进行访问的。LinkedHashSet在内部用散列来提高查询速度,但是它看上去像是用链表来保存元素的插入顺序的。它是通过一个双向链表来维护插入顺序的。

3.3 TreeSet

是一个有序的Set,其底层是一颗树。基于TreeMap实现,不可以容纳null,不支持同步;通过compareTo方法或者Comparator排序。TreeSet用了一种叫红黑树的数据结构来为元素排序。学习过C++的STL的应该听说过红黑树。点击查看红黑树百度百科

红黑树是一种自平衡二叉查找树,是计算机科学中用到的一种数据结构,典型的用途是实现关联数组。可以简单理解为:在插入元素时会自动将元素按键值由小到大的顺序排列。

import java.util.Iterator;
import java.util.TreeSet;

public class TreeSetTest {
	public static void main(String[] args) {
		TreeSet<Integer> ts = new TreeSet<Integer>();
		// ts.add(null);  //不支持null
		ts.add(1000);
		ts.add(20);
		ts.add(3);
		ts.add(40000);
		ts.add(5000000);
		ts.add(3);                      //3重复
		System.out.println(ts.size());  //5
		if(!ts.contains(6))
		{
			ts.add(6);
		}
		System.out.println(ts.size());  //6
		ts.remove(3);
		System.out.println(ts.size());  //5
		//lhs.clear();
		//System.out.println(lhs.size());  //0
		
		System.out.println("============for循环遍历=============="); 
	    for(Integer item : ts)
	    {
	    	System.out.println(item);
	    }
	    
		TreeSet<Integer> ts2 = new TreeSet<Integer>();
		
		for(int i=0;i<100000;i++)
		{
			ts2.add(i);
		}
		traverseByIterator(ts2);
		traverseByFor(ts2);
	}
	public static void traverseByIterator(TreeSet<Integer> hs)
	{
		long startTime = System.nanoTime();
		System.out.println("============迭代器遍历=============="); 
	    Iterator<Integer> iter1 = hs.iterator();  
	    while(iter1.hasNext()){  
	        iter1.next();  
	    }
		long endTime = System.nanoTime();
	    long duration = endTime - startTime;
	    System.out.println(duration + "纳米");
	}
	public static void traverseByFor(TreeSet<Integer> hs)
	{
		long startTime = System.nanoTime();
		System.out.println("============for循环遍历=============="); 
	    for(Integer item : hs)
	    {
	    	;
	    }
		long endTime = System.nanoTime();
	    long duration = endTime - startTime;
	    System.out.println(duration + "纳米");
	}
}

3.4 HashSet和LinkedHashSet判定元素重复的原则

(1)判断两个元素的hashCode返回值是否相同,如果不同,返回false。

(2)若两者hashCode相同,再判定equals方法是否相同,若不同,返回false。否则返回true。

(3)hashCode和equals是所有类都有的,来自于Object类。

以下代码,具体介绍了判断元素重复的原则的过程。

import java.util.HashSet;
import java.util.LinkedHashSet;

class Apple{               //定义一个Apple类
	private int size;      //定义一个私有变量
	public Apple(int size) //构造方法
	{
		this.size = size;
	}
}

class Orange {           //定义一个Orange类
    private int size;
 
    public Orange(int si) {//构造方法
        size = si;
    }      
    public int getSize() {
		return size;
	}

	public boolean equals(Object obj2)   {
    	System.out.println("Orange equals()~~~~~~~~~~~");
    	if(0==size - ((Orange) obj2).getSize()) {
    		return true;
    	} else {
    		return false;
    	}
    }
    
    public int hashCode() {
    	System.out.println("Orange hashCode()~~~~~~~~~~~");
    	return size;
    }
    
    public String toString() {
    	System.out.print("Orange toString()~~~~~~~~~~~");
        return size + "";
    }
}

public class ObjectHashSetTest {

	public static void main(String[] args) {
		System.out.println("==========Apple HashSet ==============");
		HashSet<Apple> hs = new HashSet<Apple>();  
		hs.add(new Apple(1));  
		hs.add(new Apple(2));  
		hs.add(new Apple(3));  
		hs.add(new Apple(4));  
		hs.add(new Apple(5));     //
		hs.add(new Apple(5));     //
		System.out.println(hs.size());  //6
		
		System.out.println("========================");
		LinkedHashSet<Apple> lhs= new LinkedHashSet<Apple>();  
		lhs.add(new Apple(1));  
		lhs.add(new Apple(2));  
		lhs.add(new Apple(3));  
		lhs.add(new Apple(4));  
		lhs.add(new Apple(5));   //
		lhs.add(new Apple(5));	 //	
		System.out.println(lhs.size());  //6
		
		//由于上述结果中我们可以看到,HashSet和LinkedHashSet,对于相同的值却不会判定相同。
		//Apple本身没有hashCode(),而是继承Object类的,
		//而Object类的hashCode()会返回对象信息和内存地址经过运算后的一个int值
		//两个不同的Apple(5)对象,他们的hashCode()返回值是不同的。
		
		System.out.println("==========Orange HashSet ==============");
		HashSet<Orange> hs2 = new HashSet<Orange>();  
		hs2.add(new Orange(1));  
		hs2.add(new Orange(2));  
		hs2.add(new Orange(3));  
		hs2.add(new Orange(4));  
		hs2.add(new Orange(5)); 
		hs2.add(new Orange(5)); 
		System.out.println(hs2.size());  //5
		
		System.out.println("========================");
		LinkedHashSet<Orange> lhs2= new LinkedHashSet<Orange>();  
		lhs2.add(new Orange(1));  
		lhs2.add(new Orange(2));  
		lhs2.add(new Orange(3));  
		lhs2.add(new Orange(4));  
		lhs2.add(new Orange(5));  
		lhs2.add(new Orange(5)); 		
		System.out.println(lhs2.size());  //5
		
		//上述结果我们会发现,可以判定最后两个是相同的,由此可以看出HashSet和LinkedHashSet是如何判定元素重复的。
		//Orange类改写了hashCode()方法,我们手动设置了它的返回值是size,所以两个不同的Orange(5)对象,他们的返回值是一样的。
		//hashCode()方法判定后,会判定equals()方法,我们同样改写后,使得两个不同的Orange(5)对象返回值一样。
                //如果equals()返回值不同,那么两个不同的Orange(5)对象还是不同的。
		//通常情况下,HashSet()方法、equals()方法和ToString()方法是三位一体的,改写时都要改写,而且通常情况下,三个都是相同的。
		

	}
}

3.5 TreeSet判定元素重复的原则

(1)需要元素继承自Comparable接口。

(2)比较两个元素的compareTo方法。

首先写一个Banana类,继承Comparable接口,此接口是可比较的接口,Banana实现Comparable接口,必须实现compareTo方法来比较大小。compareTo方法具体规则为:

int a =  obj1.compareTo(obj2);

如果 a>0, 则 obj1 > obj2 ;
如果 a=0, 则 obj1 = obj2 ;
如果 a<0, 则 obj1 < obj2 ;

//Banana类
@SuppressWarnings("rawtypes")
public class Banana implements Comparable{
	private int size;
	 
    public Banana(int s) {
        size = s;    
    }    
    
    public int getSize() {
		return size;
	}
    
	public int compareTo(Object o) {
    	System.out.println("Tiger compareTo()~~~~~~~~~~~");
        return size - ((Banana) o).getSize();
    }
}
import java.util.TreeSet;

public class ObjectTreeSetTest
{
	public static void main(String[] args) 
	{
		TreeSet<Banana> ts3 = new TreeSet<Banana>();  
		ts3.add(new Banana(1));  
		ts3.add(new Banana(2));  
		ts3.add(new Banana(3));  
		ts3.add(new Banana(4));  
		ts3.add(new Banana(5)); 
		ts3.add(new Banana(5)); 
		System.out.println(ts3.size());  //5
	}
}

4 、Map

map并不继承于Collection,它自成一体。Map 接口存储一组键值对象,提供key(键)到value(值)的映射。像Set一样,一个Map容器中的键对象不允许重复,这是为了保持查找结果的一致性;如果有两个键对象一样,那么那个键对象所对应的值对象时就有问题了,结果会造成混乱,所以键的唯一性很重要,也是符合集合的性质的。对于值对象则没有唯一性的要求。可以将任意多个键都映射到一个值对象上,这不会发生任何问题(不过对你的使用可能会造成不便,因为你不知道你得到的到底是那一个键所对应的值对象)。Java标准类库里有好几种Map:HashMap,TreeMap,LinkedHashMap,WeakHashMap,以及IdentityHashMap。它们都实现了Map的基本接口,但是在行为方式方面有着明显的诧异。这些差异表现为,效率,持有和表示对象pair的顺序,持有对象的时间长短,以及如何决定键的相等性。

4.1 HashMap

HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。HashMap 实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步。HashMap 是无序的,即不会记录插入的顺序。HashMap 继承于AbstractMap,实现了 Map、Cloneable、java.io.Serializable 接口。

HashMap 的 key 与 value 类型可以相同也可以不同,可以是字符串(String)类型的 key 和 value,也可以是整型(Integer)的 key 和字符串(String)类型的 value。

LinkedHashMap与HashMap的区别不大,主要是LinkedHashMap遍历是有序的。按插入顺序或者最先使用的顺序进行访问。

HashMap 类位于 java.util 包中,使用前需要引入它,语法格式如下:

import java.util.HashMap; // 引入 HashMap 类

//以下实例我们创建一个 HashMap 对象 Sites, 整型(Integer)的 key 和字符串(String)类型的 value:

HashMap<Integer, String> Sites = new HashMap<Integer, String>();

以下代码,用来具体实现HashMap类,并使用两种遍历方法,对HashMap类进行性能测试。最后结果Entry迭代器遍历的效果比较差。

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

public class HashMapTest {
	public static void main(String[] args) {
		HashMap<Integer,String> hm =new  HashMap<Integer,String>();
		hm.put(1, null);                             //put()添加键值对
		hm.put(null, "abc");  
		hm.put(1000, "aaa");
		hm.put(2, "bbb");
		hm.put(30000, "ccc");
		System.out.println(hm.containsValue("aaa")); //判定是否包含某一个值
		System.out.println(hm.containsKey(30000));   //判定是否包含某一个Key
		System.out.println(hm.get(30000));           //根据Key获取相应的值
		hm.put(30000, "ddd");  //覆盖ccc
		System.out.println(hm.get(30000));		
		hm.remove(2);  //根据Key删除对应的键值对
		System.out.println("size: " + hm.size());//4	
		hm.clear();    //清空
		System.out.println("size: " + hm.size());//0	
		HashMap<Integer,String> hm2 =new  HashMap<Integer,String>();
		for(int i=0;i<100000;i++)
		{
			hm2.put(i, "aaa");
		}
		traverseByEntry(hm2);
		traverseByKeySet(hm2);		
	}	
	public static void traverseByEntry(HashMap<Integer,String> ht)
	{
		long startTime = System.nanoTime();
		System.out.println("============Entry迭代器遍历==============");
		Integer key;
		String value;
		Iterator<Entry<Integer, String>> iter = ht.entrySet().iterator();
		while(iter.hasNext()) {
		    Map.Entry<Integer, String> entry = iter.next();
		    // 获取key
		    key = entry.getKey();
		    // 获取value
		    value = entry.getValue();
		    //System.out.println("Key:" + key + ", Value:" + value);
		}
		long endTime = System.nanoTime();
	    long duration = endTime - startTime;
	    System.out.println(duration + "纳米");
	}
	public static void traverseByKeySet(HashMap<Integer,String> ht)
	{
		long startTime = System.nanoTime();
		System.out.println("============KeySet遍历=============="); 
		Integer key;
		String value;
		Iterator<Integer> iter = ht.keySet().iterator();
		while(iter.hasNext()) {
		    key = iter.next();		    
		    // 获取value
		    value = ht.get(key);
		    //System.out.println("Key:" + key + ", Value:" + value);
		}
		long endTime = System.nanoTime();
	    long duration = endTime - startTime;
	    System.out.println(duration + "纳米");
	}
}

4.2 TreeMap

基于红黑树数据结构的实现。当你查看键或pair(键值对)时,会发现它们是按顺序(根据Comparable或Comparator)排列的。TreeMap的特点时,你所得到的是一个有序的Map。TreeMap是Map中唯一有subMap()方法的实现。这个方法能让你获取这个树中的一部分。TreeMap可以根据Key的自然排序或者compareTo方法进行排序输出。

以下代码,用来具体实现TreeMap类,并使用两种遍历方法,对TreeMap类进行性能测试。最后结果KeySet遍历的效果比较差。

import java.util.TreeMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

public class TreeMapTest {
	public static void main(String[] args) {
		TreeMap<Integer,String> hm =new  TreeMap<Integer,String>();
		hm.put(1, null); 
		//hm.put(null, "abc");  //会报空指针异常
		hm.put(1000, "aaa");
		hm.put(2, "bbb");
		hm.put(30000, "ccc");
		System.out.println(hm.containsValue("aaa"));//true
		System.out.println(hm.containsKey(30000));  //true 
		System.out.println(hm.get(30000));          //ccc
		hm.put(30000, "ddd");  //覆盖ccc
		System.out.println(hm.get(30000));          //ddd
		//hm.remove(2);
		System.out.println("size: " + hm.size());//4
		//hm.clear();
		//System.out.println("size: " + hm.size());
		System.out.println("==================");
		Integer key;
		String value;
		Iterator<Entry<Integer, String>> iter = hm.entrySet().iterator();
		while(iter.hasNext()) {
		    Map.Entry<Integer, String> entry = iter.next();
		    // 获取key
		    key = entry.getKey();
		    // 获取value
		    value = entry.getValue();
		    System.out.println("Key:" + key + ", Value:" + value);
		}
			
		System.out.println("========遍历测试==========");
		TreeMap<Integer,String> hm2 =new  TreeMap<Integer,String>();
		for(int i=0;i<100000;i++)
		{
			hm2.put(i, "aaa");
		}
		traverseByEntry(hm2);
		traverseByKeySet(hm2);		
	}
	public static void traverseByEntry(TreeMap<Integer,String> ht)
	{
		long startTime = System.nanoTime();
		System.out.println("============Entry迭代器遍历==============");
		Integer key;
		String value;
		Iterator<Entry<Integer, String>> iter = ht.entrySet().iterator();
		while(iter.hasNext()) {
		    Map.Entry<Integer, String> entry = iter.next();
		    // 获取ȡkey
		    key = entry.getKey();
		    // 获取value
		    value = entry.getValue();
		    //System.out.println("Key:" + key + ", Value:" + value);
		}
		long endTime = System.nanoTime();
	    long duration = endTime - startTime;
	    System.out.println(duration + "纳米");
	}
	public static void traverseByKeySet(TreeMap<Integer,String> ht)
	{
		long startTime = System.nanoTime();
		System.out.println("============KeySet遍历=============="); 
		Integer key;
		String value;
		Iterator<Integer> iter = ht.keySet().iterator();
		while(iter.hasNext()) {
		    key = iter.next();		    
		    // 获取value
		    value = ht.get(key);
		    //System.out.println("Key:" + key + ", Value:" + value);
		}
		long endTime = System.nanoTime();
	    long duration = endTime - startTime;
	    System.out.println(duration + "纳米");
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值