`
zwdsmileface
  • 浏览: 152395 次
社区版块
存档分类
最新评论

Java 常用的三个集合类

阅读更多
讲集合collection之前,我们先分清三个概念:

colection 集合,用来表示任何一种数据结构
Collection 集合接口,指的是 java.util.Collection接口,是 Set、List 和 Queue 接口的超类接口
Collections 集合工具类,指的是 java.util.Collections 类。

我们这里说的集合指的是小写的collection,集合有4种基本形式,其中前三种的父接口是Collection。

List 关注事物的索引列表
Set 关注事物的唯一性
Queue 关注事物被处理时的顺序
Map 关注事物的映射和键值的唯一性


一、Collection 接口

Collection接口是 Set 、List 和 Queue 接口的父接口,提供了多数集合常用的方法声明,包括 add()、remove()、contains() 、size() 、iterator() 等。

add(E e) 将指定对象添加到集合中
remove(Object o)将指定的对象从集合中移除,移除成功返回true,不成功返回false
contains(Object o) 查看该集合中是否包含指定的对象,包含返回true,不包含返回flase
size() 返回集合中存放的对象的个数。返回值为int
clear() 移除该集合中的所有对象,清空该集合。
iterator() 返回一个包含所有对象的iterator对象,用来循环遍历
toArray() 返回一个包含所有对象的数组,类型是Object
toArray(T[] t) 返回一个包含所有对象的指定类型的数组


我们在这里只举一个把集合转成数组的例子,因为Collection本身是个接口所以,我们用它的实现类ArrayList做这个例子:
	import java.util.ArrayList;
	import java.util.Collection;
	 
	public class CollectionTest {
	 
	    public static void main(String[] args) {
	 
	        String a = "a",b="b",c="c";
	        Collection list = new ArrayList();
	        list.add(a);
	        list.add(b);
	        list.add(c);
	 
	        String[] array =  list.toArray(new String[1]);
	 
	        for(String s : array){
	            System.out.println(s);
	        }
	    }
	}

二、几个比较重要的接口和类简介

1、List接口


List 关心的是索引,与其他集合相比,List特有的就是和索引相关的一些方法:get(int index) 、 add(int index,Object o) 、 indexOf(Object o) 。

ArrayList 可以将它理解成一个可增长的数组,它提供快速迭代和快速随机访问的能力。

LinkedList 中的元素之间是双链接的,当需要快速插入和删除时LinkedList成为List中的不二选择。

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

2、Set接口

Set关心唯一性,它不允许重复。

HashSet 当不希望集合中有重复值,并且不关心元素之间的顺序时可以使用此类。

LinkedHashset 当不希望集合中有重复值,并且希望按照元素的插入顺序进行迭代遍历时可采用此类。

TreeSet 当不希望集合中有重复值,并且希望按照元素的自然顺序进行排序时可以采用此类。(自然顺序意思是某种和插入顺序无关,而是和元素本身的内容和特质有关的排序方式,譬如“abc”排在“abd”前面。)

3、Queue接口

Queue用于保存将要执行的任务列表。

LinkedList 同样实现了Queue接口,可以实现先进先出的队列。

PriorityQueue 用来创建自然排序的优先级队列。番外篇中有个例子http://android.yaohuiji.com/archives/3454你可以看一下。

4、Map接口

Map关心的是唯一的标识符。他将唯一的键映射到某个元素。当然键和值都是对象。

HashMap 当需要键值对表示,又不关心顺序时可采用HashMap。

Hashtable 注意Hashtable中的t是小写的,它是HashMap的线程安全版本,现在已经很少使用。

LinkedHashMap 当需要键值对,并且关心插入顺序时可采用它。

TreeMap 当需要键值对,并关心元素的自然排序时可采用它。

三、ArrayList的使用

ArrayList是一个可变长的数组实现,读取效率很高,是最常用的集合类型。

1、ArrayList的创建

在Java5版本之前我们使用:

1
List list = new ArrayList();

在Java5版本之后,我们使用带泛型的写法:

1
List<String> list = new ArrayList<String>();

上面的代码定义了一个只允许保存字符串的列表,尖括号括住的类型就是参数类型,也成泛型。带泛型的写法给了我们一个类型安全的集合。关于泛型的知识可以参见这里。

2、ArrayList的使用:
      
 List<String> list = new ArrayList<String>();
	list.add("nihao!");
	list.add("hi!");
	list.add("konikiwa!");
	list.add("hola");
	list.add("Bonjour");
	System.out.println(list.size());
	System.out.println(list.contains(21));
	System.out.println(list.remove("hi!"));
	System.out.println(list.size());

关于List接口中的方法和ArrayList中的方法,大家可以看看JDK中的帮助。

3、基本数据类型的的自动装箱:

我们知道集合中存放的是对象,而不能是基本数据类型,在Java5之后可以使用自动装箱功能,更方便的导入基本数据类型。

List<Integer> list = new ArrayList<Integer>();
	list.add(new Integer(42));
	list.add(43);

4、ArrayList的排序:

ArrayList本身不具备排序能力,但是我们可以使用Collections类的sort方法使其排序。我们看一个例子:

import java.util.ArrayList;
	import java.util.Collections;
	import java.util.List;
	 
	public class Test {
	 
	    public static void main(String[] args) {
	        List<String> list = new ArrayList<String>();
	        list.add("nihao!");
	        list.add("hi!");
	        list.add("konikiwa!");
	        list.add("hola");
	        list.add("Bonjour");
	 
	        System.out.println("排序前:"+ list);
	 
	        Collections.sort(list);
	 
	        System.out.println("排序后:"+ list);
	    }
	} 

编译并运行程序查看结果:

排序前:[nihao!, hi!, konikiwa!, hola, Bonjour]

排序后:[Bonjour, hi!, hola, konikiwa!, nihao!]

5、数组和List之间的转换

从数组转换成list,可以使用Arrays类的asList()方法:
	import java.util.ArrayList;
	import java.util.Collections;
	import java.util.List;
	 
	public class Test {
	 
	    public static void main(String[] args) {
	 
	            String[] sa = {"one","two","three","four"};
	            List list = Arrays.asList(sa);
	            System.out.println("list:"+list);
	            System.out.println("list.size()="+list.size());
	    }
	 
	}

6、Iterator和for-each

在for-each出现之前,我们想遍历ArrayList中的每个元素我们会使用Iterator接口:

import java.util.Arrays;
	import java.util.Iterator;
	import java.util.List;
	 
	public class Test {
	 
	    public static void main(String[] args) {
	 
	        // Arrays类为我们提供了一种list的便捷创建方式
	        List<String> list = Arrays.asList("one", "two", "three", "four");
	 
	        // 转换成Iterator实例
	        Iterator<String> it = list.iterator();
	 
	        //遍历
	        while (it.hasNext()) {
	            System.out.println(it.next());
	        }
	 
	    }	 
	}

在for-each出现之后,遍历变得简单一些:

	import java.util.Arrays;
	import java.util.Iterator;
	import java.util.List;
	 
	public class Test {
	 
	    public static void main(String[] args) {
	 
	        // Arrays类为我们提供了一种list的便捷创建方式
	        List<String> list = Arrays.asList("one", "two", "three", "four");
	 
	        for (String s : list) {
	            System.out.println(s);
	        }	 
            }	 
	}

一、Map接口

Map接口的常用方法如下表所示:

put(K key, V value) 向集合中添加指定的键值对
putAll(Map <? extends K,? extends V> t) 把一个Map中的所有键值对添加到该集合
containsKey(Object key) 如果包含该键,则返回true
containsValue(Object value) 如果包含该值,则返回true
get(Object key) 根据键,返回相应的值对象
keySet() 将该集合中的所有键以Set集合形式返回
values() 将该集合中所有的值以Collection形式返回
remove(Object key) 如果存在指定的键,则移除该键值对,返回键所对应的值,如果不存在则返回null
clear() 移除Map中的所有键值对,或者说就是清空集合
isEmpty() 查看Map中是否存在键值对
size() 查看集合中包含键值对的个数,返回int类型

因为Map中的键必须是唯一的,所以虽然键可以是null,只能由一个键是null,而Map中的值可没有这种限制,值为null的情况经常出现,因此get(Object key)方法返回null,有两种情况一种是确实不存在该键值对,二是该键对应的值对象为null。为了确保某Map中确实有某个键,应该使用的方法是 containsKey(Object key) 。

二、HashMap

HashMap是最常用的Map集合,它的键值对在存储时要根据键的哈希码来确定值放在哪里。

1、HashMap的基本使用:

import java.util.Collection;
	import java.util.HashMap;
	import java.util.Map;
	import java.util.Set;
	 
	public class Test {	 
	    public static void main(String[] args) {
	 
	        Map<Integer,String> map = new HashMap<Integer,String>();
	 
	        map.put(1, "白菜");
	        map.put(2, "萝卜");
	        map.put(3, "茄子");
	        map.put(4, null);
	        map.put(null, null);
	        map.put(null, null);
	 
	        System.out.println("map.size()="+map.size());
	        System.out.println("map.containsKey(1)="+map.containsKey(2));	                System.out.println("map.containsKey(null)="+map.containsKey(null));
	        System.out.println("map.get(null)="+map.get(null));
	 
	        System.out.println("map.get(2)="+map.get(2));
	        map.put(null, "黄瓜");
	        System.out.println("map.get(null)="+map.get(null));
	 
	        Set set = map.keySet();
	        System.out.println("set="+set);
	 
	        Collection<String> c = map.values();
	 
	        System.out.println("Collection="+c);
	 
	    }
	 
	}

编译并运行程序,查看结果:
	map.size()=5
	map.containsKey(1)=true
	map.containsKey(null)=true
	map.get(null)=null
	map.get(2)=萝卜
	map.get(null)=黄瓜
	set=[null, 1, 2, 3, 4]
	Collection=[黄瓜, 白菜, 萝卜, 茄子, null]

2、HashMap 中作为键的对象必须重写Object的hashCode()方法和equals()方法

下面看一个我花了1个小时构思的例子,熟悉龙枪的朋友看起来会比较亲切,设定了龙和龙的巢穴,然后把它们用Map集合对应起来,我们可以根据龙查看它巢穴中的宝藏数量,例子只是为了说明hashCode这个知识点,所以未必有太强的故事性和合理性,凑合看吧:

import java.util.HashMap;
	import java.util.Map;
	 
	public class Test {
	 
	    public static void main(String[] args) {
	 
	        // 龙和它的巢穴映射表
	        Map<dragon , Nest> map = new HashMap<dragon , Nest>();
	 
	        // 在Map中放入四只克莱恩大陆上的龙
	        map.put(new Dragon("锐刃", 98), new Nest(98));
	        map.put(new Dragon("明镜", 95), new Nest(95));
	        map.put(new Dragon("碧雷", 176), new Nest(176));
	        map.put(new Dragon("玛烈", 255), new Nest(255));
	 
	        // 查看宝藏
	        System.out.println("碧雷巢穴中有多少宝藏:" + map.get(new Dragon("碧雷", 176)).getTreasure());
	    }
	 
	}
	 
	// 龙
	class Dragon {
	 
	    Dragon(String name, int level) {
	        this.level = level;
	        this.name = name;
	    }
	 
	    // 龙的名字
	    private String name;
	 
	    // 龙的级别
	    private int level;
	 
	    public int getLevel() {
	        return level;
	    }
	 
	    public void setLevel(int level) {
	        this.level = level;
	    }
	 
	    public String getName() {
	        return name;
	    }
	 
	    public void setName(String name) {
	        this.name = name;
	    }
	 
	}
	 
	// 巢穴
	class Nest {
	 
	    //我研究的龙之常数
    final int DRAGON_M = 4162;
	 
	    // 宝藏
    private int treasure;
	 
	    // 居住的龙的级别
	    private int level;
	 
	    Nest(int level) {
	        this.level = level;
	        this.treasure = level * level * DRAGON_M;
	    }
	 
	    int getTreasure() {
	        return treasure;
	    }
	 
	    public int getLevel() {
	        return level;
	    }
	 
	    public void setLevel(int level) {
	        this.level = level;
	        this.treasure = level * level * DRAGON_M;
	    }
	 
	}

编译并运行查看结果:

Exception in thread "main" java.lang.NullPointerException
	    at Test.main(Test.java:18)

我们发现竟然报了错误,第18行出了空指针错误,也就是说get方法竟然没有拿到预期的巢穴对象。

在这里我们就要研究一下为什么取不到了。我们这里先解释一下HashMap的工作方式。

假设现在有个6张中奖彩票的存根,放在5个桶里(彩票首位只有1-5,首位是1的就放在一号桶,是2的就放在2号桶,依次类推),现在你拿了3张彩票来兑奖,一个号码是113,一个号码是213,一个号码是313。那么现在先兑第一张,取出一号桶里的存根发现存根号码和你的号码不符,所以你第一张没中奖。继续兑第二张,二号桶里就没存根所以就直接放弃了,把三号桶里的所有彩票存根都拿出来对应一番,最后发现有一个存根恰好是313,那么恭喜你中奖了。

HashMap在确定一个键对象和另一个键对象是否是相同时用了同样的方法,每个桶就是一个键对象的散列码值,桶里放的就是散列码相同的彩票存根,如果散列码不同,那么肯定没有相关元素存在,如果散列码相同,那么还要用键的equals()方法去比较是否相同,如果相同才认为是相同的键。简单的说就是 hashCode()相同 && equals()==true 时才算两者相同。

到了这里我们应该明白了,在没有重写一个对象的hashcode()和equals()方法之前,它们执行的是Object中对应的方法。而Object的hashcode()是用对象在内存中存放的位置计算出来的,每个对象实例都不相同。Object的equals()的实现更简单就是看两个对象是否==,也就是两个对象除非是同一个对象,否则根本不会相同。因此上面的例子虽然都是名字叫碧雷的龙,但是HashMap中却无法认可它们是相同的。

因此我们只有重写Key对象的hashCode()和equals()方法,才能避免这种情形出现,好在Eclipse可以帮我们自动生成一个类的hashCode()和equals(),我们把上面的例子加上这两个方法再试试看:
      
 import java.util.HashMap;
	import java.util.Map;
	 
	public class Test {
	 
	    public static void main(String[] args) {
	        // 龙和它的巢穴映射表
	        Map<dragon , Nest> map = new HashMap<dragon , Nest>();
	        // 在Map中放入四只克莱恩大陆上的龙
	        map.put(new Dragon("锐刃", 98), new Nest(98));
	        map.put(new Dragon("明镜", 95), new Nest(95));
	        map.put(new Dragon("碧雷", 176), new Nest(176));
	        map.put(new Dragon("玛烈", 255), new Nest(255));
	        // 查看宝藏
	        System.out.println("碧雷巢穴中有多少宝藏:" + map.get(new Dragon("碧雷", 176)).getTreasure());
	    }
	 
	}
	// 龙
	class Dragon {
	    Dragon(String name, int level) {
	        this.level = level;
	        this.name = name;
	    }
	    // 龙的名字
	    private String name;
	    // 龙的级别
	    private int level;
	 
	    public int getLevel() {
	        return level;
	    }
	    public void setLevel(int level) {
	        this.level = level;
	    }
 
	    public String getName() {
	        return name;
	    }
	 
	    public void setName(String name) {
	        this.name = name;
            }	 
	    @Override
	    public int hashCode() {
	        final int PRIME = 31;
	        int result = 1;
	        result = PRIME * result + level;
	        result = PRIME * result + ((name == null) ? 0 : name.hashCode());
	        return result;
	    }
	 
	    @Override
	    public boolean equals(Object obj) {
	        if (this == obj)
	            return true;
	        if (obj == null)
	            return false;
	        if (getClass() != obj.getClass())
	            return false;
	        final Dragon other = (Dragon) obj;
	        if (level != other.level)
	            return false;
	        if (name == null) {
	            if (other.name != null)
	                return false;
	        } else if (!name.equals(other.name))
	            return false;
	        return true;
	    }
	 
	}
	 
	// 巢穴
	class Nest {
	 
	    //我研究的龙之常数
	    final int DRAGON_M = 4162;
	 
	    // 宝藏
	    private int treasure;
	 
	    // 居住的龙的级别
	    private int level;
	 
	    Nest(int level) {
	        this.level = level;
	        this.treasure = level * level * DRAGON_M;
	    }
	 
	    int getTreasure() {
	        return treasure;
	    }
	 
	    public int getLevel() {
	        return level;
	    }
	 
	    public void setLevel(int level) {
	        this.level = level;
	        this.treasure = level * level * DRAGON_M;
	    }
	 
	}

编译并运行查看结果:

碧雷巢穴中有多少宝藏:128922112

这一次正常输出了
2
2
分享到:
评论
1 楼 ganbo 2015-04-11  
强大。。。

相关推荐

    java常用工具类的使用

    在Java开发类库中,提供了很多工具类,我们即将学习最常见的工具类,比如对日期的操作,对集合的操作等。具体更多的工具类,请参考JavaDoc文档。 2. java.util.Date类 Date类包装了毫秒值,毫秒值表示自1970年1月1...

    Java开发技术大全(500个源代码).

    invokeMethod.java 同一个类中调用方法示例 invokeOther.java 类的外部调用方法示例 invokeStaticMethod.java 调用静态方法示例 localVariable.java 演示局部变量 localVSmember.java 局部变量与成员变量同名...

    Java第三章习题

    阐述Java语言是如何支持面向对象的抽象和封装概念? 2.在Java程序中可以通过哪几个途径对成员变量初始化?

    java源代码:三个图书馆管理系统集合

    三个图书馆管理系统,难度和复杂度都是有所不同,从简单到相对复杂(数据库),很适合初学者进阶学习。

    java面试要点集合

    数据库的题,有三个表,分别是学生信息,课程信息,选课信息,(1)用SQL语句列出分数在90以上,课程名为‘数据库原理’的学生信息。(2)用SQL 语句列出至少有一个不及格的学生的班级以及不及格学生的数量,不许用...

    java实验报告.docx

    java实验报告,一共16个实验,包含代码截图。目录 实验一 使用 Java 开发环境 Myeclipse 和 1 实验二 Java 结构化程序设计应用 3 实验三 Java 数组和方法应用 10 实验四 类和对象;类的继承和派生;多态性;接口;...

    Java集合框架测试

    现在假定有三个分组:“我的好友”,“我的同学”, “我的亲人”,每个分组包括若干好友(一个数组)。 现做如下定义: Map, List&lt;Friend&gt;&gt; data = new HashMap, List&lt;Friend&gt;&gt;(); List&lt;Friend&gt; friends =...

    Java集合类——前言

    emmmm大致可以看出,上面有三巨头,即Iterator、Collection、Map(虚线框是接口,实线框是类),Java的集合类主要就是由Collection和Map两个接口派生而出,而Iterator主要起的是遍历器的作用。 先说Collection ...

    java基础工具类iceroot

    iceroot是一个java基础工具类.封装了很多有用的方法. 该类库无任何第三方依赖. 涵盖了 字符串操作 时间转化 读取配置文件 等方面. 基础工具类对于java代码的编写是非常必要的,然 而很多常见的操作在很多第三方类库...

    Java集合

    Java的集合类是一种特别有用的工具类,它可以用于存储数量不等的多个对象,并可以实现常用数据结构,如栈,队列等,除此之外,Java集合还可用于保存具有映射关系的关联数组。 Java的集合大致上可分为:Set,List和...

    JAVA_API1.6文档(中文)

    javax.transaction 包含解组期间通过 ORB 机制抛出的三个异常。 javax.transaction.xa 提供定义事务管理器和资源管理器之间的协定的 API,它允许事务管理器添加或删除 JTA 事务中的资源对象(由资源管理器驱动程序...

    java集合框架

    集合框架是一个用来代表和操纵集合的统一架构。所有的集合框架都包含如下内容: 接口:是代表集合的抽象数据类型。接口允许集合独立操纵其代表的细节。在面向对象的语言,接口通常形成一个层次。 实现(类):是集合...

    Java高级程序设计:第7章-集合框架.pptx

    了解集合框架中的其它集合类 集合框架(Collection Framework) java.util包中定义了各种用于集合操作的类和接口,这些类和接口构成了Java语言的集合框架(Collection Framework)。 Java集合中可以放对象,不能存放基础...

    基于集合的学生信息管理系统(含GUI)JAVA版

    是基于集合的学生信息管理系统,因此在批量处理信息时采用集合类对象来储存,本题采用的是ArrayList。 该系统有三大功能,分别是管理员、老师、学生。 管理员除了要实现自己的登录、查看个人信息和修改教师密码以外...

    java学习型小项目集合

    7. **标准化库**:Java提供了丰富的标准库,包括各种类和接口,提供了很多常用功能的实现,方便开发人员快速开发应用程序。 8. **可扩展性**:Java支持模块化开发和插件机制,允许开发人员扩展和集成第三方库和框架...

    Java集合容器面试题(2020最新版)

    文章目录集合容器概述什么是集合集合的特点集合和数组的区别使用集合框架的好处常用的集合类有哪些?List,Set,Map三者的区别?List、Set、Map 是否继承自 Collection 接口?List、Map、Set 三个接口存取元素时,各...

    疯狂Java讲义 第3版 PDF电子书下载 带书签目录 完整版.rar

    本书深入介绍了Java编程的相关方面,全书内容覆盖了Java的基本语法结构、Java的面向对象特征、Java集合框架体系、Java泛型、异常处理、JavaGUI编程、JDBC数据库编程、Java注释、Java的IO流体系、Java多线程编程、...

    Java 1.6 API 中文 New

    javax.transaction 包含解组期间通过 ORB 机制抛出的三个异常。 javax.transaction.xa 提供定义事务管理器和资源管理器之间的协定的 API,它允许事务管理器添加或删除 JTA 事务中的资源对象(由资源管理器驱动程序...

    java面试题及技巧4

    │ 164个完整Java代码.zip │ J2EE综合--Struts常见错误的全面汇总.txt │ java程序员面试资料.zip │ JAVA笔试题(上海释锐).pdf │ MIME简介.txt │ SCJP试题详解.pdf │ SQL面试题_心灵深处.htm │ Struts+...

Global site tag (gtag.js) - Google Analytics