Java集合类

  
  
  
  
  
  
  1. colection 集合,用来表示任何一种数据结构
  2. Collection 集合接口,指的是 java.util.Collection接口,是 Set、List 和 Queue 接口的超类接口
  3. Collections 集合的工具类,指的是 java.util.Collections 类通过该类,我们可以对集合类进行一些排序和查找最大最小操作。类似于数组的java.util.Arrays类。

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

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

Java集合类与数组的区别:Java的集合类的长度是动态的,数组则是固定长度的。

Java集合类与数组的联系:使用相应的toArray()和Arrays.asList()方法可以互相转换。

存放在集合类中的对象在存放的时候如果没有声明继承自哪个类,那么它的自动继承自Object,所以当你取容器的的对象的时候,你得到的只是Object的应用,必须将其转型为你所要的类型!
Properties:该类也是集合类,里面的键和值都是String类型,通过它我们可以获取系统属性,读取配置文件。


一、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做这个例子:

importjava.util.ArrayList;
public class ListToArry {
public static void main(String[] args){
String a="a",b="b",c="c";
ArrayList aList=new ArrayList();
aList.add(a);
aList.add(b);
aList.add(c);
String[] array= (String[]) aList.toArray(new String[1]);
for(String s: array)
System.out.println(s);
}
}
运行结果:
a
b
c

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

首先来看看Java集合框架中的接口:

Collection接口实现类中存放的是元素和Map接口实现类中存放的是键值对。

SortedSet和SortedMap提供了排序的功能。 

1、List接口

List 关心的是索引,与其他集合相比,List特有的就是和索引相关的一些方法:get(int index) 、 add(int index,Object o) 、 indexOf(Object o) 。List接口维护元素的顺序,而且元素可以重复。

ArrayList 底层采用数组实现,具有较高的查询速度。可以将它理解成一个可增长的数组,它提供快速迭代和快速随机访问的能力。

LinkedList 中的元素之间是双链接的,当需要快速插入和删除时LinkedList成为List中的不二选择。底层采用双向循环列表实现,进行插入和删除操作时具有较高的速度,我们还可以使用LinkedList来实现队列和栈。

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

2、Set接口

Set关心唯一性,它不允许重复。不保证维护元素的顺序。

HashSet 当不希望集合中有重复值,并且不关心元素之间的顺序时可以使用此类。为快速查找而设计的Set。存入HashSet的对象必须实现hashCode()和equals()。

LinkedHashset 当不希望集合中有重复值,并且希望按照元素的插入顺序进行迭代遍历时可采用此类。且内部使用链表维护元素的顺序(插入的次序),具有HashSet的查询速度。

TreeSet 当不希望集合中有重复值,并且希望按照元素的自然顺序进行排序时可以采用此类。(自然顺序意思是某种和插入顺序无关,而是和元素本身的内容和特质有关的排序方式,譬如“abc”排在“abd”前面。)底层为树结构 。相比较HashSet其查询速度低,如果只是进行元素的查询,我们一般使用HashSet。

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版本之前我们使用:

1List list = new ArrayList();

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

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

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

2、ArrayList的使用:

01List<String> list = new ArrayList<String>();
02list.add("nihao!");
03list.add("hi!");
04list.add("konikiwa!");
05list.add("hola");
06list.add("Bonjour");
07System.out.println(list.size());
08System.out.println(list.contains(21));
09System.out.println(list.remove("hi!"));
10System.out.println(list.size());

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

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

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

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

4、ArrayList的排序:

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

01import java.util.ArrayList;
02import java.util.Collections;
03import java.util.List;
04

05public class Test {
06

07    public static void main(String[] args) {
08        List<String> list = new ArrayList<String>();
09        list.add("nihao!");
10        list.add("hi!");
11        list.add("konikiwa!");
12        list.add("hola");
13        list.add("Bonjour");
14

15        System.out.println("排序前:"+ list);
16

17        Collections.sort(list);
18

19        System.out.println("排序后:"+ list);
20    }
21

22}

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

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

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

5、数组和List之间的转换

从数组转换成list,可以使用Arrays类的asList()方法:

01import java.util.ArrayList;
02import java.util.Collections;
03import java.util.List;
04

05public class Test {
06

07    public static void main(String[] args) {
08

09            String[] sa = {"one","two","three","four"};
10            List list = Arrays.asList(sa);
11            System.out.println("list:"+list);
12            System.out.println("list.size()="+list.size());
13    }
14

15}

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

01import java.util.Arrays;
02import java.util.Iterator;
03import java.util.List;
04

05public class Test {
06

07    public static void main(String[] args) {
08

09        // Arrays类为我们提供了一种list的便捷创建方式
10        List<String> list = Arrays.asList("one""two""three""four");
11

12        // 转换成Iterator实例
13        Iterator<String> it = list.iterator();
14

15        //遍历
16        while (it.hasNext()) {
17            System.out.println(it.next());
18        }
19

20    }
21

22}

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

01import java.util.Arrays;
02import java.util.Iterator;
03import java.util.List;
04

05public class Test {
06

07    public static void main(String[] args) {
08

09        // Arrays类为我们提供了一种list的便捷创建方式
10        List<String> list = Arrays.asList("one""two""three""four");
11

12        for (String s : list) {
13            System.out.println(s);
14        }
15

16    }
17

18}

本讲内容:Map HashMap

前面课程中我们知道Map是个接口,它关心的是映射关系,它里面的元素是成对出现的,键和值都是对象且键必须保持唯一。这一点上看它和Collection是很不相同的。

一、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的基本使用: 

01import java.util.Collection;
02import java.util.HashMap;
03import java.util.Map;
04import java.util.Set;
05

06public class Test {
07

08    public static void main(String[] args) {
Map<Integer,String> map=new HashMap<Integer,String>();
String[] a1={"白菜",""};
map.put(1,"白菜");
map.put(2,"萝卜");
map.put(3,"茄子");
map.put(4,null);
map.put(null,null);
map.put(null,null); //重复的key值,新value会覆盖原有value,size不变
System.out.println("map.size()="+map.size());
System.out.println("map.containsKey(4)="+map.containsKey(4));
System.out.println("map.get(4)="+map.get(4));
System.out.println("map.containsKey(6)="+map.containsKey(6));
System.out.println("map.get(6)="+map.get(6));
System.out.println("map.containsKey(null)="+map.containsKey(null));
System.out.println("map.get(null)="+map.get(null));
map.put(null,"黄瓜");
System.out.println("map.get(null)="+map.get(null));

Set<Integer> set = map.keySet(); //取得map的所有key,返回的是Set<>类型
System.out.println(set);

Collection<String> co= map.values(); //取得map的所有value,返回的是一个Collection<>类型
System.out.println(co);

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

map.size()=5
map.containsKey(4)=true
map.get(4)=null
map.containsKey(6)=false
map.get(6)=null
map.containsKey(null)=true
map.get(null)=null
map.get(null)=黄瓜
[null, 1, 2, 3, 4]
[黄瓜, 白菜, 萝卜, 茄子, null]

HashMap的value是ArrayList时:
  Map<Integer,ArrayList> map=new HashMap<Integer,ArrayList>();
ArrayList v1= new ArrayList();
v1.add("白菜");
map.put(1,v1);
map.put(2,new ArrayList());
String[] st={"萝卜","茄子",null,null};

for (String s : st) {
if (map.containsKey(null)) {
map.get(null).add(s);
} else {
map.put(null, new ArrayList());
map.get(null).add(s);
}
}

System.out.println("map.size()="+map.size());
System.out.println("map.containsKey(1)="+map.containsKey(1));
System.out.println("map.get(1)="+map.get(1));
System.out.println("map.containsKey(2)="+map.containsKey(2));
System.out.println("map.get(2)="+map.get(2));
System.out.println("map.containsKey(null)="+map.containsKey(null));
System.out.println("map.get(null)="+map.get(null));

Set<Integer> set = map.keySet(); //取得map的所有key,返回的是Set<>类型
System.out.println(set);
输出:
map.size()=3
map.containsKey(1)=true
map.get(1)=[白菜]
map.containsKey(2)=true
map.get(2)=[]
map.containsKey(null)=true
map.get(null)=[萝卜, 茄子, null, null]
[null, 1, 2]


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

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

01import java.util.HashMap;
02import java.util.Map;
03

04public class Test {
05

06    public static void main(String[] args) {
07

08        // 龙和它的巢穴映射表
09        Map<dragon , Nest> map = new HashMap<dragon , Nest>();
10

11        // 在Map中放入四只克莱恩大陆上的龙
12        map.put(new Dragon("锐刃"98), new Nest(98));
13        map.put(new Dragon("明镜"95), new Nest(95));
14        map.put(new Dragon("碧雷"176), new Nest(176));
15        map.put(new Dragon("玛烈"255), new Nest(255));
16

17        // 查看宝藏
18        System.out.println("碧雷巢穴中有多少宝藏:" + map.get(new Dragon("碧雷"176)).getTreasure());
19    }
20

21}
22

23// 龙
24class Dragon {
25

26    Dragon(String name, int level) {
27        this.level = level;
28        this.name = name;
29    }
30

31    // 龙的名字
32    private String name;
33

34    // 龙的级别
35    private int level;
36

37    public int getLevel() {
38        return level;
39    }
40

41    public void setLevel(int level) {
42        this.level = level;
43    }
44

45    public String getName() {
46        return name;
47    }
48

49    public void setName(String name) {
50        this.name = name;
51    }
52

53}
54

55// 巢穴
56class Nest {
57

58    //我研究的龙之常数
59    final int DRAGON_M = 4162;
60

61    // 宝藏
62    private int treasure;
63

64    // 居住的龙的级别
65    private int level;
66

67    Nest(int level) {
68        this.level = level;
69        this.treasure = level * level * DRAGON_M;
70    }
71

72    int getTreasure() {
73        return treasure;
74    }
75

76    public int getLevel() {
77        return level;
78    }
79

80    public void setLevel(int level) {
81        this.level = level;
82        this.treasure = level * level * DRAGON_M;
83    }
84

85}

编译并运行查看结果:

1Exception in thread "main" java.lang.NullPointerException
2    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(),我们把上面的例子加上这两个方法再试试看:

001import java.util.HashMap;
002import java.util.Map;
003

004public class Test {
005

006    public static void main(String[] args) {
007

008        // 龙和它的巢穴映射表
009        Map<dragon , Nest> map = new HashMap<dragon , Nest>();
010

011        // 在Map中放入四只克莱恩大陆上的龙
012        map.put(new Dragon("锐刃"98), new Nest(98));
013        map.put(new Dragon("明镜"95), new Nest(95));
014        map.put(new Dragon("碧雷"176), new Nest(176));
015        map.put(new Dragon("玛烈"255), new Nest(255));
016

017        // 查看宝藏
018        System.out.println("碧雷巢穴中有多少宝藏:" + map.get(new Dragon("碧雷"176)).getTreasure());
019    }
020

021}
022

023// 龙
024class Dragon {
025

026    Dragon(String name, int level) {
027        this.level = level;
028        this.name = name;
029    }
030

031    // 龙的名字
032    private String name;
033

034    // 龙的级别
035    private int level;
036

037    public int getLevel() {
038        return level;
039    }
040

041    public void setLevel(int level) {
042        this.level = level;
043    }
044

045    public String getName() {
046        return name;
047    }
048

049    public void setName(String name) {
050        this.name = name;
051    }
052

053    @Override
054    public int hashCode() {
055        final int PRIME = 31;
056        int result = 1;
057        result = PRIME * result + level;
058        result = PRIME * result + ((name == null) ? 0 : name.hashCode());
059        return result;
060    }
061

062    @Override
063    public boolean equals(Object obj) {
064        if (this == obj)
065            return true;
066        if (obj == null)
067            return false;
068        if (getClass() != obj.getClass())
069            return false;
070        final Dragon other = (Dragon) obj;
071        if (level != other.level)
072            return false;
073        if (name == null) {
074            if (other.name != null)
075                return false;
076        else if (!name.equals(other.name))
077            return false;
078        return true;
079    }
080

081}
082

083// 巢穴
084class Nest {
085

086    //我研究的龙之常数
087    final int DRAGON_M = 4162;
088

089    // 宝藏
090    private int treasure;
091

092    // 居住的龙的级别
093    private int level;
094

095    Nest(int level) {
096        this.level = level;
097        this.treasure = level * level * DRAGON_M;
098    }
099

100    int getTreasure() {
101        return treasure;
102    }
103

104    public int getLevel() {
105        return level;
106    }
107

108    public void setLevel(int level) {
109        this.level = level;
110        this.treasure = level * level * DRAGON_M;
111    }
112

113}

编译并运行查看结果:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值