当你有很多书时,你会考虑买一个书柜,将你的书分门别类摆放进入。使用了书柜不仅仅使房间变得整洁,也便于以后使用书时方便查找。在计算机中管理对象亦是如此,当获得多个对象后,也需要一个容器将它们管理起来,这个容器就是集合。

  集合本质是基于某种数据结构数据容器。常见的数据结构:数组(Array)、集(Set)、队列(Queue)、链表(Linkedlist)、树(Tree)、堆(Heap)、栈(Stack)和映射(Map)等结构。本文将为大家介绍Java中的集合。

一、集合概述

  Java中提供了丰富的集合接口和类,它们来自于java.util包。如下图所示是Java主要的集合接口和类,从图中可见Java集合类型分为:Collection和Map,Collection子接口有:Set、Queue和List等接口。每一种集合接口描述了一种数据结构。

本文重点介绍List、Set和Map接口,因此下图中只列出了这三个接口的具体实现类,事实上Queue也有具体实现类,由于很少使用,这里不再赘述,读者感兴趣可以自己查询API文档。

              

Tips:在Java SE中List名称的类型有两个,一个是java.util.List,另外一个是java.awt.List。java.util.List是一个接口,这本章介绍的List集合。而java.awt.List是一个类,用于图形用户界面开发,它是一个图形界面中的组件。

Tips:学习Java中的集合,首先从两大接口入手,重点掌握List、Set和Map三个接口,熟悉这些接口中提供的方法。然后再熟悉这些接口的实现类,并了解不同实现类之间的区别。

二、List集合

  List集合中的元素是有序的,可以重复出现。如下图是一个班级集合数组,这个集合中有一些学生,这些学生是有序的,顺序是他们被放到集合中的顺序,可以通过序号访问他们。这就像老师给进入班级的人分配学号,第一个报到的是“张三”,老师给他分配的是0,第二个报到的是“李四”,老师给他分配的是1,以此类推,最后一个序号应该是“学生人数-1”。

             

Tips:List集合关心的元素是否有序,而不关心是否重复,请大家记住这个原则。例如,上图所示的班级集合中就有两个“张三”。

  List接口的实现类有:ArrayList 和 LinkedList。它们二者的区别比较如下:

    ●  ArrayList是基于动态数组数据结构的实现;

    ●  ArrayList是基于动态数组数据结构的实现;

    ●  LinkedList是基于链表数据结构的实现;

    ●  ArrayList访问元素速度优于LinkedList;

    ●  LinkedList占用的内存空间比较大;

    ●  LinkedList在批量插入或删除数据时优于ArrayList。

  不同的结构对应于不同的算法,有的考虑节省占用空间,有的考虑提高运行效率,对于程序员而言,它们就像是“熊掌”和“鱼肉”,不可兼得!提高运行速度往往是以牺牲空间为代价的,而节省占用空间往往是以牺牲运行速度为代价的。

2.1 常用方法

  List接口继承自Collection接口,List接口中的很多方法都继承自Collection接口的。List接口中常用方法如下。

  1.操作元素

    ●  get(int index):返回List集合中指定位置的元素。

    ●  set(int index, Object element):用指定元素替换List集合中指定位置的元素。

    ●  add(Object element):在List集合的尾部添加指定的元素。该方法是从Collection集合继承过来的。

    ●  add(int index, Object element):在List集合的指定位置插入指定元素。

    ●  remove(int index):移除List集合中指定位置的元素。

    ●  remove(Object element):如果List集合中存在指定元素,则从List集合中移除第一次出现的指定元素。该方法是从Collection集合继承过来的。

    ●  clear():从List集合中移除所有元素。该方法是从Collection集合继承过来的。

  2.判断元素

    ●  isEmpty():判断List集合中是否有元素,没有返回true,有返回false。该方法是从Collection集合继承过来的。

    ●  contains(Object element):判断List集合中是否包含指定元素,包含返回true,不包含返回false。该方法是从Collection集合继承过来的。

  3.查询元素

    ●  indexOf(Object o):从前往后查找List集合元素,返回第一次出现指定元素的索引,如果此列表不包含该元素,则返回-1。

    ●  lastIndexOf(Object o):从后往前查找List集合元素,返回第一次出现指定元素的索引,如果此列表不包含该元素,则返回-1。

  4.其它

    ●  iterator():返回迭代器(Iterator)对象,迭代器对象用于遍历集合。该方法是从Collection集合继承过来的。

    ●  size():返回List集合中的元素数,返回值是int类型。该方法是从Collection集合继承过来的。

    ●  subList(int fromIndex, int toIndex):返回List集合中指定的 fromIndex(包括 )和 toIndex(不包括)之间的元素集合,返回值为List集合。

示例代码如下:

 //HelloWorld.java文件
package com.Kevin; import java.util.ArrayList;
import java.util.List; public class HelloWorld { public static void main(String[] args) { List list = new ArrayList(); String b = "B"; //向集合中添加元素
list.add("A");
list.add(b);
list.add("C");
list.add(b);
list.add("D");
list.add("E"); //打印集合元素个数
System.out.println("集合size = " + list.size());
//打印集合
System.out.println(list); //从前往后查找集合中的"B"元素
System.out.println("indexOf(\"B\") = " + list.indexOf(b));
//从后往前查找集合中的"B"元素
System.out.println("lastIndexOf(\"B\") = " + list.lastIndexOf(b)); //删除集合中第一个"B"元素
list.remove(b);
System.out.println("remove(3)前: " + list);
//判断集合中是否包含"B"元素
System.out.println("是否包含\"B\":" + list.contains(b)); //删除集合第4个元素
list.remove(3);
System.out.println("remove(3)后: " + list);
//判断集合是否为空
System.out.println("list集合是空的:" + list.isEmpty()); System.out.println("替换前:" + list);
//替换集合第2个元素
list.set(1, "F");
System.out.println("替换后:" + list); //清空集合
list.clear();
System.out.println(list); // 重新添加元素
list.add(1);//发生自动装箱
list.add(3); int item = (Integer)list.get(0);//发生自动拆箱
}
}

运行结果如下:

集合size = 6
[A, B, C, B, D, E]
indexOf("B") = 1
lastIndexOf("B") = 3
remove(3)前: [A, C, B, D, E]
是否包含"B":true
remove(3)后: [A, C, B, E]
list集合是空的:false
替换前:[A, C, B, E]
替换后:[A, F, B, E]
[]

代码第11行声明List类型集合变量list,使用ArrayList类实例化list,List是接口不能实例化。添加集合元素过程中可以添加重复的元素,见代码第17行和第19行。代码第51行list.clear()是清空集合,但需要注意的是变量list所引用的对象还是存在的,不是null,只是集合中没有了元素。

TIps:在Java中任何集合中存放的都是对象,即引用数据类型,基本数据类型不能放到集合中。但上述代码第56行却将整数1放到集合中,这是因为这个过程中发生了自动装箱,整数1被封装成Integer对象1,然后再放入到集合中。相反从集合中取出的也是对象,代码第59行从集合中取出的是Integer对象,之所以能够赋值给int类型,是因为这个过程发生了自动拆箱。

2.2 遍历集合

  集合最常用的操作之一是遍历,遍历就是将集合中的每一个元素取出来,进行操作或计算。List集合遍历有三种方法:

  1. 使用for循环遍历:List集合可以使用for循环进行遍历,for循环中有循环变量,通过循环变量可以访问List集合中的元素。

  2. 使用for-each循环遍历:for-each循环是针对遍历各种类型集合而推出的,笔者推荐使用这种遍历方法。

  3. 使用迭代器遍历:Java提供了多种迭代器,List集合可以使用Iterator和ListIterator迭代器。

示例代码如下:

 //HelloWorld.java文件
package com.Kevin; import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; public class HelloWorld { public static void main(String[] args) { List list = new ArrayList(); String b = "B";
// 向集合中添加元素
list.add("A");
list.add(b);
list.add("C");
list.add(b);
list.add("D");
list.add("E"); // 1.使用for循环遍历
System.out.println("--1.使用for循环遍历--");
for (int i = 0; i < list.size(); i++) {
System.out.printf("读取集合元素(%d): %s \n", i, list.get(i));
} // 2.使用for-each循环遍历
System.out.println("--2.使用for-each循环遍历--");
for (Object item : list) {
String s = (String) item;
System.out.println("读取集合元素: " + s);
} // 3.使用迭代器遍历
System.out.println("--3.使用迭代器遍历--");
Iterator it = list.iterator();
while (it.hasNext()) {
Object item = it.next();
String s = (String) item;
System.out.println("读取集合元素: " + s);
}
}
}

上述代码采用三种方法遍历List集合,采用for循环遍历需要通过List集合的get方法获得元素,代码第26行的list.get(i)。代码第31行采用for-each循环遍历list集合,从集合中取出的元素都是Object类型,代码第32行是强制转换为String类型。使用迭代器遍历,首先需要获得迭代器对象,代码第38行list.iterator()方法可以返回迭代器对象。代码第39行调用迭代器hasNext()方法可以判断集合中是否还有元素可以迭代,有返回true,没有返回false。代码第40行调用迭代器的next()返回迭代的下一个元素,该方法返回的Object类型需要强制转换为String类型,见代码第41行。

三、Set集合

Set集合是由一串无序的,不能重复的相同类型元素构成的集合。下图是一个班级的Set集合。这个Set集合中有一些学生,这些学生是无序的,不能通过类似于List集合的序号访问,而且不能有重复的同学。

              

Tips:List集合中的元素是有序的、可重复的,而Set集合中的元素是无序的、不能重复的。List集合强调的是有序,Set集合强调的是不重复。当不考虑顺序,且没有重复元素时,Set集合和List集合可以互相替换的。

  Set接口直接实现类主要是HashSet,HashSet是基散列表数据结构的实现。

3.1 常用方法

  Set接口也继承自Collection接口,Set接口中大部分都是继承自Collection接口,这些方法如下。

 1.操作元素

   ●  add(Object element):在Set集合的尾部添加指定的元素。该方法是从Collection集合继承过来的。

   ●  remove(Object element):如果Set集合中存在指定元素,该方法是从Collection集合继承过来的。

   ●  clear():从Set集合中移除所有元素。该方法是从Collection集合继承过来的。

 2.判断元素

   ●  isEmpty():判断Set集合中是否有元素,没有返回true,有返回false。该方法是从Collection集合继承过来的

   ●  contains(Object element):判断Set集合中是否包含指定元素,包含返回true,不包含返回false。该方法是从Collection集合继承过来的。

 3.其他

   ●  iterator():返回迭代器(Iterator)对象,迭代器对象用于遍历集合。该方法是从Collection集合继承过来的。

   ●  size():返回Set集合中的元素数,返回值是int类型。该方法是从Collection集合继承过来的。

示例代码如下:

 //HelloWorld.java文件
package com.Kevin; import java.util.HashSet;
import java.util.Set; public class HelloWorld { public static void main(String[] args) { Set set = new HashSet(); String b = "B"; // 向集合中添加元素
set.add("A");
set.add(b);
set.add("C");
set.add(b);
set.add("D");
set.add("E"); // 打印集合元素个数
System.out.println("集合size = " + set.size());
// 打印集合
System.out.println(set); // 删除集合中第一个"B"元素
set.remove(b);
// 判断集合中是否包含"B"元素
System.out.println("是否包含\"B\":" + set.contains(b));
// 判断集合是否为空
System.out.println("set集合是空的:" + set.isEmpty()); // 清空集合
set.clear();
System.out.println(set);
}
}

运行结果:

集合size = 5
[A, B, C, D, E]
是否包含"B":false
set集合是空的:false
[]

  代码第11行声明Set类型集合变量set,使用HashSet类实例化set,Set是接口不能实例化。添加集合元素时试图添加重复的元素,见代码第17行和第19行,但是Set集合不能添加重复元素,所有代码第24行打印集合元素个数是5。

3.2 遍历集合

  Set集合中的元素由于没有序号,所以不能使用for循环进行遍历,但可以使用for-each循环和迭代器进行遍历。事实上这两种遍历方法也是继承自Collection集合,也就是说所有的Collection集合类型都有这两种遍历方式。

示例代码如下:

 //HelloWorld.java文件
package com.Kevin; import java.util.HashSet;
import java.util.Iterator;
import java.util.Set; public class HelloWorld { public static void main(String[] args) { Set set = new HashSet(); String b = "B";
// 向集合中添加元素
set.add("A");
set.add(b);
set.add("C");
set.add(b);
set.add("D");
set.add("E"); // 1.使用for-each循环遍历
System.out.println("--1.使用for-each循环遍历--");
for (Object item : set) {
String s = (String) item;
System.out.println("读取集合元素: " + s);
} // 2.使用迭代器遍历
System.out.println("--2.使用迭代器遍历--");
Iterator it = set.iterator();
while (it.hasNext()) {
Object item = it.next();
String s = (String) item;
System.out.println("读取集合元素: " + s);
}
}
}

上述代码采用两种方法遍历Set集合,具体实现与List集合完全一样,这里不再赘述。

四、Map集合

  Map(映射)集合表示一种非常复杂的集合,允许按照某个键来访问元素。Map集合是由两个集合构成的,一个是键(key)集合,一个是值(value)集合。键集合是Set类型,因此不能有重复的元素。而值集合是Collection类型,可以有重复的元素。Map集合中的键和值是成对出现的。

  下图所示是Map类型的“国家代号”集合。键是国家代号集合,不能重复。值是国家集合,可以重复。

       

提示 Map集合更适合通过键快速访问值,就像查英文字典一样,键就是要查的英文单词,而值是英文单词的翻译和解释等。有的时候,一个英文单词会对应多个翻译和解释,这是与Map集合特性对应的。

  Map接口直接实现类主要是HashMap,HashMap是基散列表数据结构的实现。

4.1 常用方法

Map集合中包含两个集合(键和值),所以操作起来比较麻烦,Map接口提供很多方法用来管理和操作集合。主要的方法如下。

 1.操作元素

   ●  get(Object key):返回指定键所对应的值;如果Map集合中不包含该键值对,则返回null。

   ●  put(Object key, Object value):指定键值对添加到集合中。

   ●  remove(Object key):移除键值对。

   ●  clear():移除Map集合中所有键值对。

 2.判断元素

   ●  isEmpty():判断Map集合中是否有键值对,没有返回true,有返回false。

   ●  containsKey(Object key):判断键集合中是否包含指定元素,包含返回true,不包含返回false。

   ●  containsValue(Object value):判断值集合中是否包含指定元素,包含返回true,不包含返回false。

 3.查看集合

   ●  keySet():返回Map中的所有键集合,返回值是Set类型。

   ●  values():返回Map中的所有值集合,返回值是Collection类型。

   ●  size():返回Map集合中键值对数。

示例代码如下:

 //HelloWorld.java文件
package com.Kevin; import java.util.HashMap;
import java.util.Map; public class HelloWorld { public static void main(String[] args) { Map map = new HashMap(); map.put(102, "张三");
map.put(105, "李四");
map.put(109, "王五");
map.put(110, "董六");
//"李四"值重复
map.put(111, "李四");
//109键已经存在,替换原来值"王五"
map.put(109, "刘备"); // 打印集合元素个数
System.out.println("集合size = " + map.size());
// 打印集合
System.out.println(map); // 通过键取值
System.out.println("109 - " + map.get(109));
System.out.println("108 - " + map.get(108)); // 删除键值对
map.remove(109);
// 判断键集合中是否包含109
System.out.println("键集合中是否包含109:" + map.containsKey(109));
// 判断值集合中是否包含 "李四"
System.out.println("值集合中是否包含:" + map.containsValue("李四")); // 判断集合是否为空
System.out.println("集合是空的:" + map.isEmpty()); // 清空集合
map.clear();
System.out.println(map);
}
}

运行结果如下:

集合size = 5
{102=张三, 105=李四, 109=王五, 110=董六, 111=刘备}
109 - 王五
108 - null
是否包含"B":false
值集合中是否包含:true
集合是空的:false
{}

代码第11行声明Map类型集合变量map,使用HashMap类实例化map,Map是接口不能实例化。Map集合添加键值对时候需要注意两个问题:第一,如果键已经存在,则会替换原有值,见代码第20行是109键原来对应的是"王五",该语句会替换为"刘备";第二,如果这个值已经存在,则不会替换,见代码第14行和第18行,添加了两个相同的值"李四"。

代码第29行和第30行是通过键取对应的值,如果不存在键值对,则返回null,代码第30行的108键对应的值不存在,所以这里打印的是null。

4.2 遍历集合

  Map集合遍历与List和Set集合不同,Map有两个集合,因此遍历时可以只遍历值的集合,也可以只遍历键的集合,也可以同时遍历。这些遍历过程都可以使用for-each循环和迭代器进行遍历。

示例代码如下:

 //HelloWorld.java文件
package com.Kevin; import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set; public class HelloWorld { public static void main(String[] args) { Map map = new HashMap(); map.put(102, "张三");
map.put(105, "李四");
map.put(109, "王五");
map.put(110, "董六");
map.put(111, "李四"); // 1.使用for-each循环遍历
System.out.println("--1.使用for-each循环遍历--");
// 获得键集合
Set keys = map.keySet();
for (Object key : keys) {
int ikey = (Integer) key; // 自动拆箱
String value = (String) map.get(ikey); // 自动装箱
System.out.printf("key=%d - value=%s \n", ikey, value);
} // 2.使用迭代器遍历
System.out.println("--2.使用迭代器遍历--");
// 获得值集合
Collection values = map.values();
// 遍历值集合
Iterator it = values.iterator();
while (it.hasNext()) {
Object item = it.next();
String s = (String) item;
System.out.println("值集合元素: " + s);
} }
}

上述代码第25行是获得键集合,返回值Set类型。在遍历键时,从集合里取出的元素类型都是Object,代码第27行是将key强制类型转换为Integer,然后又赋值给int整数,这个过程发生了自动拆箱。代码第28行是通过键获得对应的值。

代码第35行是获得值集合,它是Collection类型。遍历Collection集合与遍历Set集合一样,这里不再赘述。

小结

本篇介绍了Java中的集合,其中包括常用接口Collection、Set、List和Map,重点掌握Set、List和Map三个接口,熟悉具体实现类。熟练几种集合的遍历操作。

[ Java学习基础 ] Java的对象容器 -- 集合的更多相关文章

  1. [ Java学习基础 ] Java的继承与多态

    看到自己写的东西(4.22的随笔[ Java学习基础 ] Java构造函数)第一次达到阅读100+的成就还是挺欣慰的,感谢大家的支持!希望以后能继续和大家共同学习,共同努力,一起进步!共勉! ---- ...

  2. [ Java学习基础 ] Java的抽象类与接口

    一.抽象类 1. 抽象类 Java语言提供了两种类:一种是具体类:另一种是抽象子类. 2. 抽象类概念: 在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的 ...

  3. [ Java学习基础 ] Java构造函数

    构造方法是类中特殊方法,用来初始化类的实例变量,它在创建对象(new运算符)之后自动调用. Java构造方法的特点如下: 构造方法名必须与类名相同. 构造方法没有任何返回值,包括void. 构造方法只 ...

  4. [ Java学习基础 ] Java对象的创建和销毁

    类实例化可生成对象,实例方法就是对象方法,实例变量就是对象属性.一个对象的生命周期包括三个阶段:创建.使用和销毁. 创建对象 创建对象包括两个步骤:声明和实例化. 声明 声明对象与声明普通变量没有区别 ...

  5. [ Java学习基础 ] Java异常处理

    一.异常概述 异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的.比如说,你的代码少了一个分号,那么运行出来结果是提示是错误 java.lang.Error:如果你用Sys ...

  6. [ Java学习基础 ] Java的封装性与访问控制

    Java面向对象的封装性是通过对成员变量和方法进行访问控制实现的,访问控制分为4个等级:私有.默认.保护和公有,具体规则如下表: 1.私有级别 私有级别的关键字是private,私有级别的成员变量和方 ...

  7. Java学习--基础

    java学习 基础 Java三大版本 javase 标准版 占领桌面端(基础) javame 移动版 嵌入式开发.占领手机端 javaee 企业版 占领服务器端 Java的特性和优势 跨平台.可移植性 ...

  8. 第二十六节:复习Java语言基础-Java的概述,匿名对象,封装,构造函数

    Java基础 Java语言概述 Java语言 语言 描述 javaee 企业版 javase 标准版 javame 小型版 JDK JDK(Java开发工具包) Java语言 语言 Java语言 Ja ...

  9. Java 学习(20):Java Applet 基础 & Java 文档注释

    -- Java Applet 基础 -- Java 文档注释 Java Applet 基础 Applet 是一种 Java 程序.它一般运行在支持 Java 的 Web 浏览器内.因为它有完整的 Ja ...

随机推荐

  1. iOS 中如何判断当前是2G/3G/4G/5G/WiFi

    5G 什么的,还得等苹果API更新啊,不过将来还是这个处理过程就是了. 关于判断当前的网络环境是2G/3G/4G,这个问题以前经常看到,最近在一工程里看到了如果判断的API.而在撸WebRTC音视频通 ...

  2. C++对象模型的那些事儿之三:默认构造函数

    前言 继前两篇总结了C++对象模型及其内存布局后,我们继续来探索一下C++对象的默认构造函数.对于C++的初学者来说,有如下两个误解: 任何class如果没有定义default constructor ...

  3. 打Patch实践

    一.找到相应PATCH 确认系统已安装模块版本. SELECTapp.application_short_name, app.application_name, pi.patch_level   FR ...

  4. BIP Requests Are Failing With Error "OPP Error Oracle.apps.xdo.XDOException: Error Creating Lock Fil

    In this Document   Symptoms   Cause   Solution   References Applies to: BI Publisher (formerly XML P ...

  5. 海量数据挖掘MMDS week2: Association Rules关联规则与频繁项集挖掘

    http://blog.csdn.net/pipisorry/article/details/48894977 海量数据挖掘Mining Massive Datasets(MMDs) -Jure Le ...

  6. Qt4项目迁移到Qt5问题:greaterThan(QT_MAJOR_VERSION, 4): QT += widgets .

    文章来源:http://blog.csdn.net/ccf19881030/article/details/18220447 问题一:错误:C1083: 无法打开包括文件:"QApplica ...

  7. UVa - 1616 - Caravan Robbers

    二分找到最大长度,最后输出的时候转化成分数,比较有技巧性. AC代码: #include <iostream> #include <cstdio> #include <c ...

  8. 【翻译】针对多种设备定制Ext JS 5应用程序

    原文:Tailoring Your Ext JS 5 Application for a Multi-Device World 概述 鉴于当今设备和表单因素的扩散,要针对所有这些可能性来优化应用程序已 ...

  9. Python的time(时间戳与时间字符串互相转化)

    strptime("string format")字符串如"20130512000000"格式的 输入处理函数 localtime(float a)时间戳的输入 ...

  10. 移植Cocos2D到Android平台的原理

    幸运的,SpriteBuilder使得适配(安卓)多种多样的屏幕尺寸变得容易起来,因为Android Xcode插件允许你使用任何Cocos2D的特性并且可以继续使用很多iOS的框架(framewor ...