Java集合与框架总结与学习

  林炳文Evankaka原创作品。转载请注明出处 http://blog.csdn.net/evankaka    
      本文将主要讲解Java中集合的使用与区别,主要讲List、Set、Map的原理、使用方法、注意事项等。

一、Collection与Collections的区别

    Java集合框架是Java语言的重要组成部分,它包含了系统而完整的集合层次体系,封装了大量的数据结构的实现。深刻理解Java集合框架的组成结构及其中的实现类和算法,能极大提高程序员编码的能力。本章讲述Java集合框架,主要包括集合框架的概念、集合框架接口,以及列表、集合、映射三种结构还有迭代方法、比较方法和较早以前版本的类和接口。下面就让我们一起来学习这些内容。
     集合有时又称为容器,简单地说,它是一个对象,能将具有相同性质的多个元素汇聚成一个整体。集合被用于存储、获取、操纵和传输聚合的数据。Java集合框架提供了有效的数据结构和算法,因此程序员不需要自己编写代码实现这些功能。而且结合框架对各个接口的实现是可以互换的,因此很容易转换接口。这样就提高了软件的复用性。

注意事项:

(1)Collection是集合类的一个顶级接口,其直接继承接口有List与Set

(2)Collections则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。

  二、List、Set、Map三大集合区别与说明

         Java平台提供了一个全新的集合框架。集合框架的核心接口为Collection、List(列表)、Set(集合)和Map(映射)。


               图1  集合关系图
    从图1中可以看到,Collection是集合继承树中最顶层的接口,几乎所有的Java集合框架成员都继承实现了Collection接口,或者与其有密切关系。Collection提供了关于集合的通用操作。Set接口和List接口都继承了Collection接口,而Map接口没有继承Collection接口。因此,Set对象和List对象都可以调用Collection接口的方法,而Map对象则不可以。
    下面我们对这三种类型接口的结构加以说明:Set有点类似数学中集合的定义,是无序的、没有重复项目的集合。List是位置性集合,加进清单的元素可以加在清单中特定位置或加到末尾,可以保存重复的元素。Map用于关键字/数值对,其中关键字是数值的惟一标识(不可重复),用户可以按关键字存取数据,数据可重复。具体形式如图2所示:

  图2 各集合元素存放示例

   (1)Collection:集合层次中的根接口,JDK 没有提供这个接口直接的实现类。

   (2)Set:不能包含重复的元素。

   (3) List:是一个有序的集合,可以包含重复的元素。提供了按索引访问的方式。

   (4)Map:包含了 key-value 对。Map 不能包含重复的 key。


三、List用法总结

3.1 List接口

   List的主要特征使其元素已先行方式存储,集合中允许存放重复对象。List接口主要的实现类包括:

   (1)ArrayList:代表长度可变的数组。允许对元素进行快速的随机访问,但是向ArrayList中插入与删除元素的速度较慢。

   (2)LinkedList :在实现中采用链表数据结构,元素之间是双链接。对顺序访问进行了优化,向List中插入和删除元素的速度较快,随机访问速度则相对较慢,随机访问是指检索位于特定索引位置元素,当需要快速插入和删除时LinkedList成为List中的不二选择。

   (3)Vector :是ArrayList的线程安全版本,性能比ArrayList要低,现在已经很少使用。


3.2  ArrayList用法

3.2.1、ArrayList是个集合

      集合是可以往里面添东西的,用add(E e)方法往里面加(把E都看做Object

      例子:ArrayList类中的addget方法add方法向集合中添加数据,get方法将集合中指定下标位置的数据取出)

     (1)size()方法,返回集合中的元素个数,类似于数组中的length属性

     (2)clear()方法,将集合中的所有元素都给清除

     (3)isEmpty()方法判断集合是否为空,为空返回true

     (4)remove(int index)方法是删除集合中指定位置的元素,清除的原理是你清除了第一个后,后面的元素往前移,第一个变第0个了,原来第三个元素就没啦

     (5)remove(Object ob)remove的重载,一个是根据索引来删除,一个是根据具体对象来删除,比如remove("Hello");

      (6)indexOf(Obejct ob)判断某个元素在索引的第几个位置上。

在数组中查看数组元素个数,用数组的length属性,在集合中查看集合元素个数,用集合的size()方法。


3.2.2、ArrayList使用范例

import java.awt.List;
import java.util.ArrayList;
import java.util.Iterator;
/**
 * @author 林炳文
 * @time 2015/2/5
 * ArrayList用法示例说明
 * 
 */

public class Main {
	public static void main(String[] args) {
		//ArrayList用法示例
		ArrayList<String> m_ArrayList=new ArrayList<String>();
		m_ArrayList.add("Evankaka");
		m_ArrayList.add("林炳文");
		m_ArrayList.add("德德");
		m_ArrayList.add("Evankaka");
		m_ArrayList.add("小红");
		m_ArrayList.set(2,"文炳林");// 将索引位置为2的对象修改
		m_ArrayList.add(3,"秀秀");// 将对象添加到索引位置为3的位置
		
		//ArrayList遍历方法1
		Iterator<String> it_ArrayList = m_ArrayList.iterator();
		System.out.println("ArrayList遍历方法1");
		while (it_ArrayList.hasNext()) {
		System.out.println(it_ArrayList.next());
		}
		
		//ArrayList遍历方法2
		System.out.println("ArrayList遍历方法2");
		for(Object o:m_ArrayList){
			System.out.println(o);
		}
		
		//ArrayList遍历方法2
		System.out.println("ArrayList遍历方法3");
		for(int i = 0; i<m_ArrayList.size(); i++){
			System.out.println(m_ArrayList.get(i));
			}
		//删除元素
		m_ArrayList.remove("Evankaka");
		it_ArrayList = m_ArrayList.iterator();
		System.out.println("ArrayList删除元素后的遍历");
		while (it_ArrayList.hasNext()) {
			String m_String=it_ArrayList.next();
		 if(m_String.equals("秀秀")){
			 it_ArrayList.remove();
		 }else{
		System.out.println(m_String);
		  }
		}
	}	
}

输出结果:

ArrayList遍历方法1
Evankaka
林炳文
文炳林
秀秀
Evankaka
小红
ArrayList遍历方法2
Evankaka
林炳文
文炳林
秀秀
Evankaka
小红
ArrayList遍历方法3
Evankaka
林炳文
文炳林
秀秀
Evankaka
小红
ArrayList删除元素后的遍历
林炳文
文炳林
Evankaka
小红

3.2.3、ArrayList注意

(1)使用Iterator迭代集合过程中,不可修改集合元素,否则会引发异常。并且Iterator只能向后迭代 

(2)如果你想在循环过程中去掉某个元素,只能调用it.remove方法,    不能使用list.remove方法,    否则一定出并发访问的错误.  

3.3  LinkedList用法

        LinkedList类是链表节点各种操作的实现,LinkedList类实现了一个带有头尾引用的通用型双向链表。注意,此实现不是同步的。如果多个线程同时访问列表,而其中至少一个线程从结构上修改了该列表,则它必须保持外部同步。(结构修改指添加或删除一个或多个元素的任何操作;仅设置元素的值不是结构修改。)这一般通过对自然封装该列表的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedList 方法来“包装”该列表。最好在创建时完成这一操作,以防止对列表进行意外的不同步访问。


四、set接口

       set集合中多个对象之间没有明显的顺序,set与Collection的结构基本上完全一样,不同在于set不能包含重复元素。Set判断两个对象相同不是使用==运算符,而是根据equals方法。也就是说主要两个对象用equals方法比较返回true,Set就不会接受这两个对象。 HashSet是Set接口的典型实现,大多数时候使用Set集合就是使用这个类。

4.1   HashSet说明与范例

      HashSet按hash算法来存储集合中的元素,因此具有很好的存储和查找性能。 
      HaseSet具有以下特点: 
     ● 不能保证元素的排列顺序,顺序有可能发生变化 
    ● HashSet不是同步的,如果多个线程访问同一个HashSet,要注意线程安全问题 
    ● 集合元素值可以为null 
      当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据该值来决定该对象在HashSet中的存储位置。如果有两个元素通过equals方法比较返回true,但它们的hashCode方法返回值不相等,HashSet将会把它们存储在不同的位置。也就是说HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode方法返回值也相等。

使用范例:

package com.collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
public class Main
{
    public static void main(String [] args)
    {
       HashSet h=new HashSet();
       h.add("1st");
       h.add("2nd");
       h.add(new Integer(3));
       h.add(new Double(4.0));
       h.add("2nd");            //重复元素,未被添加
       h.add(new Integer(3));      //重复元素,未被添加
       h.add(new Date());
       System.out.println("开始:size="+h.size());
       Iterator it=h.iterator();
       while(it.hasNext())
       {
           Object o=it.next();
           System.out.println(o);
       }
       h.remove("2nd");
       System.out.println("移除元素后:size="+h.size());
       System.out.println(h);
    }
}
结果:

开始:size=5
4.0
1st
3
2nd
Mon Feb 16 20:52:17 CST 2015
移除元素后:size=4
[4.0, 1st, 3, Mon Feb 16 20:52:17 CST 2015]

4.2 TreeSet

       TreeSet描述的是Set的一种变体——可以实现排序等功能的集合,它在将对象元素添加到集合中时会自动按照某种比较规则将其插入到有序的对象序列中,并保证该集合元素组成时刻按照“升序”排列。

使用范例:

package com.collection;
import java.util.TreeSet;
import java.util.Iterator;
public class Main
{
    public static void main(String [] args)
    {
       TreeSet ts=new TreeSet();
       ts.add("orange");
       ts.add("apple");
       ts.add("banana");
       ts.add("grape");
       Iterator it=ts.iterator();
       while(it.hasNext())
       {
           String fruit=(String)it.next();
           System.out.println(fruit);
      }
    }
}

结果:

apple
banana
grape
orange

 4.3 Set总结

     (1) HashSet和TreeSet是Set的两个典型实现。HashSet的性能总是比TreeSet好(特别是常用的添加、查询等操作),因为TreeSet需要额外的红黑树算法来维护集合元素的次序。只有当需要一个保存排序的Set时,才应该使用TreeSet,否则都应该使用HashSet。
      (2)  LinkedHashSet是HashSet的子类,该集合也是根据元素hashCode值来决定元素存储位置,但它同时使用链表维护元素的次序,这样使得元素看起来是以插入的顺序保存的。LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet的性能,但在迭代访问Set里的全部元素时将有很好的性能,又因为链表的存在,遍历LinkedHashSet会更快。
     (3)TreeSet是SortedSet接口的实现,TreeSet可以确保集合元素处于排序状态,TreeSet并不是根据元素的插入顺序进行排序,而是根据元素的实际值来进行排序的。     
     (4)EnumSet是所有Set实现类中性能最好的,但它只能保存同一个枚举类的枚举值作为集合元素。
     (5)必须指出的是Set的三个实现类HashSet、TreeSet和EnumSet都是线程不安全的,如果有多条线程同时访问一个Set集合,并有超过一条线程修改了该Set集合,则必须手动保证该Set集合的同步性。通常可以通过Collections工具类的synchronizedSortedSet方法来包装该Set集合。此操作最好在创建时进行。


五 Map接口

     将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。此接口取代 Dictionary 类,后者完全是一个抽象类,而不是一个接口。

  Map 接口提供三种collection 视图,允许以键集、值集或键-值映射关系集的形式查看某个映射的内容。映射顺序 定义为迭代器在映射的 collection 视图上返回其元素的顺序。某些映射实现可明确保证其顺序,如 TreeMap 类;另一些映射实现则不保证顺序,如HashMap 类。

  注:将可变对象用作映射键时必须格外小心。当对象是映射中某个键时,如果以影响 equals 比较的方式更改了对象的值,则映射的行为将是不确定的。此项禁止的一种特殊情况是不允许某个映射将自身作为一个键包含。虽然允许某个映射将自身作为值包 含,但请格外小心:在这样的映射上 equals 和 hashCode 方法的定义将不再是明确的。

  所有通用的映射实现类应该提供两个“标准的”构造方法:一个 void (无 参数)构造方法,用于创建空映射;一个是带有单个 Map 类型参数的构造方法,用于创建一个与其参数具有相同键-值映射关系的新映射。实际上,后一个构造方法允许用户复制任意映射,生成所需类的一个等价映射。尽 管无法强制执行此建议(因为接口不能包含构造方法),但是 JDK 中所有通用的映射实现都遵从它。

  此接口中包含的“破坏”方法可修改其操作的映射,如果此映射不支持该操作,这些方法将抛出 UnsupportedOperationException。如果是这样,那么在调用对映射无效时,这些方法可以(但不要求)抛出 UnsupportedOperationException。例如,如果某个不可修改的映射(其映射关系是“重叠”的)为空,则对该映射调用 putAll(Map) 方法时,可以(但不要求)抛出异常。

  某些映射实现对可能包含的键和值有所限制。例如,某些实现禁止 null 键和值,另一些则对其键的类型有限制。尝试插入不合格的键或值将抛出一个未经检查的异常,通常是 NullPointerException 或 ClassCastException。试图查询是否存在不合格的键或值可能抛出异常,或者返回 false;某些实现将表现出前一种行为,而另一些则表现后一种。一般来说,试图对不合格的键或值执行操作且该操作的完成不会导致不合格的元素被插入映射 中时,将可能抛出一个异常,也可能操作成功,这取决于实现本身。这样的异常在此接口的规范中标记为“可选”。

  此接口是 Java Collections Framework 的成员。

  Collections Framework 接口中的很多方法是根据 equals 方法定义的。例如,containsKey(Object key) 方法的规范中写道:“当且仅当此映射包含针对满足 (key==null ? k==null : key.equals(k)) 的键 k 的映射关系时,返回 true”。不 应将此规范解释为:调用具有非空参数 key 的 Map.containsKey 将导致对任意的键 k 调用 key.equals(k)。实现可随意进行优化,以避免调用 equals,例如,可首先比较两个键的哈希码(Object.hashCode() 规范保证哈希码不相等的两个对象不会相等)。一般来说,只要实现者认为合适,各种 Collections Framework 接口的实现可随意利用底层 Object 方法的指定行为。

      

Map 提供了一个更通用的元素存储方法。Map 集合类用于存储元素对(称作“键”和“值”),其中每个键映射到一个值。从概念上而言,您可以将 List 看作是具有数值键的 Map。而实际上,除了 List 和 Map 都在定义 java.util 中外,两者并没有直接的联系。本文将着重介绍核心 Java 发行套件中附带的 Map,同时还将介绍如何采用或实现更适用于您应用程序特定数据的专用 Map。

5.1、了解 Map 接口和方法

Java 核心类中有很多预定义的 Map 类。在介绍具体实现之前,我们先介绍一下 Map 接口本身,以便了解所有实现的共同点。Map 接口定义了四种类型的方法,每个 Map 都包含这些方法。下面,我们从两个普通的方法开始对这些方法加以介绍。

表 1:覆盖的方法。我们将这 Object 的这两个方法覆盖,以正确比较 Map 对象的等价性。

equals(Object o)比较指定对象与此 Map 的等价性
hashCode()返回此 Map 的哈希码

 

5.2、Map 构建

Map 定义了几个用于插入和删除元素的变换方法。

表 2:Map 更新方法: 可以更改 Map 内容。

clear()从 Map 中删除所有映射
remove(Object key)从 Map 中删除键和关联的值
put(Object key, Object value)将指定值与指定键相关联
clear()从 Map 中删除所有映射
putAll(Map t)将指定 Map 中的所有映射复制到此 map

 

尽管您可能注意到,纵然假设忽略构建一个需要传递给 putAll() 的 Map 的开销,使用 putAll() 通常也并不比使用大量的 put() 调用更有效率,但 putAll() 的存在一点也不稀奇。这是因为,putAll() 除了迭代 put() 所执行的将每个键值对添加到 Map 的算法以外,还需要迭代所传递的 Map 的元素。但应注意,putAll() 在添加所有元素之前可以正确调整 Map 的大小,因此如果您未亲自调整 Map 的大小(我们将对此进行简单介绍),则 putAll() 可能比预期的更有效。

5.3、查看 Map

迭代 Map 中的元素不存在直接了当的方法。如果要查询某个 Map 以了解其哪些元素满足特定查询,或如果要迭代其所有元素(无论原因如何),则您首先需要获取该 Map 的“视图”。有三种可能的视图。

  • 所有键值对 — 参见 entrySet()
  • 所有键 — 参见 keySet()
  • 有值 — 参见 values()

 

前两个视图均返回 Set 对象,第三个视图返回 Collection 对象。就这两种情况而言,问题到这里并没有结束,这是因为您无法直接迭代 Collection 对象或 Set 对象。要进行迭代,您必须获得一个 Iterator 对象。因此,要迭代 Map 的元素,必须进行比较烦琐的编码

 

Iterator keyValuePairs = aMap.entrySet().iterator();
Iterator keys = aMap.keySet().iterator();
Iterator values = aMap.values().iterator();

 

值得注意的是,这些对象(Set、Collection 和 Iterator)实际上是基础 Map 的视图,而不是包含所有元素的副本。这使它们的使用效率很高。另一方面,Collection 或 Set 对象的 toArray() 方法却创建包含 Map 所有元素的数组对象,因此除了确实需要使用数组中元素的情形外,其效率并不高。

5.4、遍历map

package com.test; 

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

public class Test {   
  
    public static void main(String[] args) {   
        Map<String, String> map = new HashMap<String, String>();   
        map.put("first", "linlin");   
        map.put("second", "bingbing");   
        map.put("third", "wenwen");  
        map.put("first", "linlin2"); 
  
  
        // 第一种:通过Map.keySet遍历key和value   
        System.out.println("===================通过Map.keySet遍历key和value:===================");   
        for (String key : map.keySet()) {   
            System.out.println("key= " + key + "  and  value= " + map.get(key));   
        }   
           
        // 第二种:通过Map.entrySet使用iterator遍历key和value   
        System.out.println("===================通过Map.entrySet使用iterator遍历key和value:===================");   
        Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();   
        while (it.hasNext()) {   
            Map.Entry<String, String> entry = it.next();   
            System.out.println("key= " + entry.getKey() + "  and  value= "  
                    + entry.getValue());   
        }   
  
        // 第三种:通过Map.entrySet遍历key和value   
        System.out.println("===================通过Map.entrySet遍历key和value:===================");   
        for (Map.Entry<String, String> entry : map.entrySet()) {   
            System.out.println("key= " + entry.getKey() + "  and  value= "  
                    + entry.getValue());   
        }   
  
        // 第四种:通过Map.values()遍历所有的value,但是不能遍历键key   
        System.out.println("===================通过Map.values()遍历所有的value:===================");   
        for (String v : map.values()) {   
            System.out.println("value= " + v);   
        }   
    }   
  
}  

输出结果如下
===================通过Map.keySet遍历key和value:===================
key= third  and  value= wenwen
key= first  and  value= linlin2
key= second  and  value= bingbing
===================通过Map.entrySet使用iterator遍历key和value:===================
key= third  and  value= wenwen
key= first  and  value= linlin2
key= second  and  value= bingbing
===================通过Map.entrySet遍历key和value:===================
key= third  and  value= wenwen
key= first  and  value= linlin2
key= second  and  value= bingbing
===================通过Map.values()遍历所有的value:===================
value= wenwen
value= linlin2
value= bingbing



  • 17
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值