JAVA三种集合LIST、SET、MAP ——详解

1. 集合框架介绍 

咱们知道,计算机的优点在于处理大量的数据,在编程开发中,为处理大量的数据,必须具有相应的存储结构,以前学习的数组能够用来存储并处理大量类型相同的数据,可是经过上面的课后练习,会发现数组在应用中的限制:数组长度一旦肯定,就没法更改;除非采用创建新数组,再将原数组内容拷贝过来;数组中只能存放指定类型的数据,操做不方便。在实际开发中,为了操做方便,JDK中提供了List集合。java

List集合与数组的用途很是类似,都是用来存储大量数据的,不一样处有两点:web

1. 数组长度在使用前必须肯定,一旦肯定不能改变。而List集合长度可变,无需定义。编程

2. 数组中必须存放同一类型的数据,List集合中能够存放不一样类型的数据。数组

List集合是Java集合框架中的一种,另外两种集合Set和Map会在下面介绍。List集合在JDK中被封装称为接口,针对List接口,有若干种实现,经常使用的有三个子类,即ArrayList、Vector和LinkedList。这三个类的功能与用法相同,但内部实现方式不一样。下面以ArrayList为例介绍集合的经常使用操做,Vector和LinkedList的使用方法与ArrayList相似。安全

数组与List集合的常规操做相似,下面经过代码对比二者的用法:框架

代码演示:数组的基本操做性能

public class ArrayDemo {学习

public static void main(String[] args) {this

String[] array = new String[3];spa

for (int i = 0; i < 3; i++) {

array[i] = "Hello";

}

String a = array[0];

}

}

代码演示:List集合的基本操做

import java.util.ArrayList;    

public class ListDemo {

public static void main(String[] args) {

ArrayList list = new ArrayList();    

for (int i = 0; i < 3; i++) {

list.add("Hello");    

}

String a = (String)list.get(0);    

}

}

代码解析:

① 集合框架在java.util包中,使用前必须使用import语句引入对应的类。

② List集合的定义时不须要指定大小,也不用指定集合中保存的数据类型。

③ List集合中添加数据时不须要制定下标,List集合会自动生成下标。

④ 获取List集合的元素时使用get方法并传入下标,而后强制类型转换为实际类型。

代码演示:使用集合记录学员姓名

public static void main(String[] args) {

System.out.println("请输入班级学员姓名,输入OVER结束");

java.util.Scanner scanner = new java.util.Scanner(System.in);

ArrayList list = new ArrayList();

do {

String name = scanner.next();

if (name.equalsIgnoreCase("OVER")) break;

list.add(name);

while (true);

System.out.println(list);    

}

代码解析:

① List集合重写了toString方法,能够将集合中的元素依次输出。

2. List集合的经常使用方法

下表列出了List集合的经常使用方法:

返回类型

方法名称

说明

boolean

add(Object obj)

加入元素,返回是否添加成功

boolean

clear()

清除集合中的元素

boolean

contains(Object obj)

查找集合中是否存在传入的元素

Object

get(int index)

获取指定位置的元素

boolean

isEmpty()

判断集合是否为空

Object

remove(int index)

删除制定位置的元素,并返回该元素

int

size()

获取集合大小

Object[]

toArray()

将集合转换成一个数组

  List集合的经常使用方法

下面经过实例演示各个方法的用途:

代码演示:List集合的经常使用方法

import java.util.ArrayList;

public class Demo {

public static java.util.Scanner scanner = new java.util.Scanner(System.in);

public static void main(String[] args) {

ArrayList listA = new ArrayList();

ArrayList listB = new ArrayList();

System.out.println("请输入A班学员姓名,输入OVER结束");

inputName(listA);

System.out.println("请输入B班学员姓名,输入OVER结束");

inputName(listB);

//合并集合listA与listB

listA.addAll(listB);

System.out.println("请输入要查找的学员姓名");

String name = scanner.next();

int pos = listA.indexOf(name);

if (pos==-1) {

System.out.println("没有找到");

else {

System.out.println("找到了,位置是:" + pos);

}

System.out.println("请输入要删除的学员姓名");

String delName = scanner.next();

if (listA.remove(delName)) {

System.out.println("删除成功!");

else {

System.out.println("没有该学员");

}

}

public static void inputName(ArrayList list) {

do {

String name = scanner.next();

if (name.equalsIgnoreCase("OVER")) break;

list.add(name);

while (true);

}

}

3. 使用List集合保存对象

使用List集合保存对象时,主要注意如下几点:

1. 集合中保存的是对象的引用,观察如下代码:

代码演示:使用集合保存对象

import java.util.ArrayList;

class Student {

String name;

int age;

public Student(String name, int age) {

this.name = name;

this.age = age;

}

public String toString() {

return name + "/" + age;

}

}

public class Demo {

public static void main(String[] args) {

ArrayList list = new ArrayList();

Student stu = new Student("Tom" , 10);

for (int i = 0; i < 3; i++) {

stu.age = 10 + i;

list.add(stu);

}

System.out.println(list);

}

}

上面代码的原意是在集合中保存三个Student对象,age分别为十、十一、12,但实际输出的age值均为12。这是由于list集合中保存的是stu对象的引用,而在循环中stu的引用并无变化,因此循环结束后集合中的三个元素都指向stu对象,age的值天然也是最后的12。将代码“Student stu = new Student("Tom" , 10);”放入循环内能够解决这一问题。

2. 使用remove、contains、indexOf等方法时,应该重写类的equals方法,观察如下代码:

代码演示:未重写equals方法的代码

//省略了Student类的定义

public class Demo {

public static void main(String[] args) {

ArrayList list = new ArrayList();

list.add(new Student("Tom" , 11));

list.add(new Student("Jerry" , 22));

list.add(new Student("Alice" , 33));

System.out.println(list.contains(new Student("Tom" , 11)));

System.out.println(list.indexOf(new Student("Jerry" , 22)));

System.out.println(list.remove(new Student("Alice" , 33))?"成功":"无此项");

}

}

在上例中,咱们但愿判断学员Tom是否存在,查找学员Jerry,删除学员Alice,可是输出的结果倒是不存在,找不到,删不掉。这是由于List集合会调用元素的equals方法来判断对象是否相等,而Student类没有重写equals方法,默认是按引用地址比较,而每一个学员对象的地址又不相同,因此出现这个现象。经过给Student类添加equals方法能够解决这个问题:

代码演示:重写equals方法后的Student类

class Student {

String name;

int age;

public Student(String name, int age) {

this.name = name;

this.age = age;

}

public boolean equals(Object obj) {

if (obj == nullreturn false;

if (!(obj instanceof Student)) return false;

Student stu = (Student) obj;

return stu.name.equals(this.name) && stu.age == this.age;

}

}

4. 三种List集合的比较

咱们说过,ArrayList、Vector与LinkedList的使用方法相同,内部实现方式不一样。而内部实现方式的不一样又决定了三种集合的适用范围,了解三种集合的内部实现,才能正确的选择使用类型。

ArrayList与Vector比较

ArrayList与Vector的内部实现相似,Vector设计为线程安全,ArrayList设计为非线程安全。为了保证线程安全,Vector在性能方面稍逊于ArrayList,目前咱们编写的都是单线程应用程序,应选择使用ArrayList。

ArrayList与LinkedList

ArrayList与LinkedList均设计为非线程安全,ArrayList内部采用数组实现(与Vector相同),LinkedList内部采用链表结构实现。

ArrayList采用数组保存元素,意味着当大量添加元素,数组空间不足时,依然须要经过新建数组、内存复制的方式来增长容量,效率较低;而当进行对数组进行插入、删除操做时,又会进行循环移位操做,效率也较低;只有进行按下标查询时(get方法),使用数组效率很高。

LinkedList采用链表保存元素,在添加元素时只须要进行一次简单的内存分配便可,效率较高;进行插入、删除操做时,只需对链表中相邻的元素进行修改便可,效率也很高;但进行按下标查询时,须要对链表进行遍历,效率较低。下图演示了链表结构的特性:

图:  链表结构,每一个元素引用后面的元素

图:  向链表中插入元素,只需修改两处引用

图:  删除链表中的元素,也只须要修改两处引用

能够总结出ArrayList在进行数据的新增、插入、删除时效率较低,按下标对数据进行查找时效率较高;LinkedList正好相反。通常来讲ArrayList保存常常进行查询操做的集合,LinkedList适用于保存经常进行修改操做的集合。

5. 章节概述

1. List集合与数组的区别。

2. List集合实际上包含了3个经常使用的集合类,即ArrayList、Vector和LinkedList。

3. List集合的经常使用操做。

4. ArrayList采用数组保存元素,意味着当大量添加元素,数组空间不足时,依然须要经过新建数组、内存复制的方式来增长容量,效率较低;而当进行对数组进行插入、删除操做时,又会进行循环移位操做,效率也较低;只有进行按下标查询时(get方法),使用数组效率很高。

5. ArrayList与Vector的内部实现相似,Vector设计为线程安全,ArrayList设计为非线程安全。为了保证线程安全,Vector在性能方面稍逊于ArrayList,目前咱们编写的都是单线程应用程序,应选择使用ArrayList。

6. ArrayList与LinkedList均设计为非线程安全,ArrayList内部采用数组实现(与Vector相同),LinkedList内部采用链表结构实现。

7. LinkedList采用链表保存元素,在添加元素时只须要进行一次简单的内存分配便可,效率较高;进行插入、删除操做时,只需对链表中相邻的元素进行修改便可,效率也很高;但进行按下标查询时,须要对链表进行遍历,效率较低。

1. Map集合

在上面讲的List集合中,可用经过List集合提供的各类方法来对其中的元素进行操做,从而能够方便用户操做,可是若是要从List集合中获取一个特定的对象,操做是比较繁琐的。

在类Person中有cardId和name两个属性,分别表明编号和姓名,建立两个Person对象并存储到ArrayList集合中 ,若是要从集合中获取指定的对象,则必需要经过迭代整个集合来得到,以下所示:

代码演示:Person类 

public class Person {

String cardId;

String name;

public Person(String cardId, String name) {

this.cardId = cardId;

this.name = name;

}

}

代码演示:从ArrayList中获取特定的对象 

public class ArrayListTest {

public static void main(String[] args) {

Person personA = new Person("001", "Tom");

Person personB = new Person("002", "Jack");

ArrayList list = new ArrayList();

list.add(personA);

list.add(personB);

for (int i = 0; i < list.size(); i++) {

Person person = (Person) list.get(i);

if (person.cardId.equals("002")) { 

System.out.println(person.name);

}

}

}

}

从上面的示例中,咱们看到从list集合中获取一个对象的繁琐,有没有简单的方法呢?在JDK中专门提供了Map集合来存储上面这种一对一映射关系的对象。

Map集合用于保存具备映射关系的数据,即以键值对(key->value)的方式来存储数据。所以在Map集合内部有两个集合,一个集合用于保存Map中的key(键),一个集合用于保存Map中的value(值),其中key和value能够是任意数据类型数据。

图:  Map集合

Map集合中的经常使用类有Hashtable和HashMap,两个类的功能和用法类似,下面以HashMap为例介绍Map集合的用法。

代码演示:Map集合使用

public class MapTest {

public static void main(String[] args) {

HashMap map = new HashMap();  

map.put("001", "Tom");  

map.put("002", "Jack");

String name = (String) map.get("002");  

System.out.println(name);

}

}

代码解析:

① 建立HashMap对象。

② 利用HashMap中的put方法将键值对形式的对象进行存储,put方法中的第一个参数为映射关系中key的值,put方法的第二个参数为映射关系中value的值。

③ 利用HashMapget方法获取key对应的value,而后强制类型转换为实际类型。get中的参数为key,返回值为key对应的value

下表列出了HashMap中经常使用的方法:

返回类型

方法名称

做用

Object

put(Object key,Object value)

加入元素返回与此key关联的原有的value,不存在则返回null

void

clear()

从集合中移除全部的元素

boolean

containsKey(Object key)

根据key从集合中判断key是否存在

boolean

containsValue(Object value)

根据value从集合中判断value是否存在

Object

get(Object key)

根据key返回key对应的值

Set

keySet()

返回Map集合中包含的键集合

Object

remove(Object key)

从集合中删除key对应的元素,返回与key对应的原有value,不存在则返回null

int 

size()

返回集合中的元素的数量

表:  HashMap经常使用方法

Map集合的综合示例:

代码演示:Map集合综合演示

import java.util.HashMap;

import java.util.Scanner;

public class TestMap {

public static void main(String[] args) {

HashMap map = new HashMap();

Scanner scanner = new Scanner(System.in);

for (int i = 0; i < 5; i++) {  

System.out.println("请输入身份证号:");

String id = scanner.next();

System.out.println("请输入姓名:");

String name = scanner.next();

map.put(id, name);  

}

int size = map.size();  

System.out.println("数据输入完毕!共" + size

+ "条数据!\n---------------------------------");

String answer = "no";

do {

System.out.println("请输入你要查找的用户的身份证号:");

String id = scanner.next();

if (map.containsKey(id)) {  

String name = (String) map.get(id);  

System.out.println("您查找的用户姓名为:" + name);

else {

System.out.println("您查找的用户不存在!");

}

System.out.println("您还要继续查找吗?(yes/no)");

answer = scanner.next();

while ("yes".equalsIgnoreCase(answer));

System.out.println("请输入要删除的用户的身份证号:");

String id = scanner.next();

if (map.containsKey(id)) {

String name = (String) map.remove(id);  

System.out.println("用户" + name + "删除成功!");

else {

System.out.println("您要删除的用户不存在!");

}

System.out.println("要格式化系统吗?(yes/no)");

String format = scanner.next();

if ("yes".equalsIgnoreCase(format)) {

map.clear();  

System.out.println("系统格式化完毕!当前系统中数据为" 

+ map.size() + "条");

}

System.out.println("程序运行结束!");

}

}

代码解析:

① 使用put方法将身份证号和姓名存入Map集合中。

② 使用size方法得到集合中的映射关系条数。

③ 使用containsKey方法判断集合是否存在与key对应的映射关系。

④ 使用get方法得到身份证号对应的姓名。

⑤ 使用remove方法删除身份证号对应的用户,返回身份证号对应的姓名。

⑥ 使用clear方法删除Map集合中全部的映射关系。

来看下面的一个示例: 

代码演示:Map集合中重复key

import java.util.HashMap;

public class DemoMap {

public static void main(String[] args) {

HashMap map = new HashMap();

map.put("001", "小美");

map.put("002", "阿聪");  

map.put("002", "小莉");  

String name = (String) map.get("002");

System.out.println(name);

}

}

注意①和②处的代码,在向集合中添加值的时候,使用了重复的key,可是value不一样,在下面得到key002value为多少呢?程序运行的结果是小莉。从结果能够中能够知道,Map集合中的key不能是重复的,若是重复,那么后面添加的映射关系会覆盖前面的映射关系。致使这样状况的出现主要是由于Map集合中的key的维护是依靠Set集合(立刻会学习到)完成的。

HashMapHashtable的操做是相同的,他们的区别以下: 

Hashtable是线程安全的,HashMap是非线程安全的。全部HashMapHashtable的性能更高。

Hashtable不容许使用使用null值做为keyvalue,可是HashMap是能够的。

2. Set集合

Set集合和List集合的不少的用法是相同的。可是Set集合中的元素是无序的,元素也是不能重复的。Set集合中经常使用类为HashSet。

HashSet类中经常使用的方法以下:

返回类型

方法名称

做用

boolean

add(Object obj)

加入元素

void

clear()

移除Set集合中全部元素

boolean

contains(Object obj)

判断Set集合中是否包含指定元素

boolean

isEmpty()

判断Set集合是否为空

Iterator

iterator()

返回Set集合中对元素迭代的迭代器

boolean

remove(Object obj)

从集合中删除元素

Int

size()

返回集合中的元素数量

表:  HashSet类经常使用方法

经过上面的表,能够清楚的看到Set集合的用法和List集合是类似的,可是须要注意Set集合的迭代和List集合是不一样的,List的集合的迭代能够经过for循环得到索引来进行,可是Set集合的迭代必需要经过迭代器进行。

代码演示:Set集合的迭代

import java.util.HashSet;

import java.util.Iterator;

public class SetIterator {

public static void main(String[] args) {

HashSet set = new HashSet();

set.add("a");

set.add("b");

set.add("c");

Iterator iter = set.iterator();  

while (iter.hasNext()) {  

String str = (String) iter.next();  

System.out.println(str);

}

}

}

代码解析:

① 经过Set集合的iterator()方法得到该集合的迭代器,迭代器是Iterator类的实例。

② 根据迭代器的hasNext()方法判断集合中是否还有元素,若是有就返回true。

③ 根据迭代器的next()方法得到集合中的元素,并强制类型转换为目标类型。

从上面例子的运行结果,能够看出Set中的元素是无序的。经过Set集合的迭代再来学习Map集合的迭代。

代码演示:Map集合的迭代

import java.util.HashMap;

import java.util.Iterator;

import java.util.Map;

import java.util.Set;

public class MapIter {

public static void main(String[] args) {

HashMap map = new HashMap();

map.put("001", "小美");

map.put("002", "阿聪");

map.put("003", "小黑");

HashSet keys = (HashSet) map.keySet();  

Iterator iter = keys.iterator();

while (iter.hasNext()) {

String key = (String) iter.next();  

String value = (String) map.get(key);  

System.out.println(key + ":" + value);

}

}

}

代码解析:

① 调用Map集合的keySet方法得到Map集合中的key的集合。

② 得到key

③ 根据key得到对应的value

下面经过一个示例演示Set集合中不容许元素重复的特性:

代码演示:向Set集合中添加剧复元素

import java.util.HashSet;

import java.util.Iterator;

public class Demo2 {

public static void main(String[] args) {

HashSet set = new HashSet();

set.add("a");

set.add("a");

set.add("c");

System.out.println("集合长度为:" + set.size());

Iterator iter = set.iterator();

while (iter.hasNext()) {

String str = (String) iter.next();

System.out.println(str);

}

}

}

上面的代码输出集合的长度为2,并且集合中的元素只有一个a和c,从结果中能够看出Set集合中的元素是不能重复的。带着这个结论再来看下面的示例:

代码演示:向Set集合中添加剧复元素

import java.util.HashSet;

import java.util.Iterator;

public class Demo3 {

public static void main(String[] args) {

Person personA = new Person("001", "Tom");

Person personB = new Person("001", "Tom");

HashSet set = new HashSet();

set.add(personA);

set.add(personB);

System.out.println("集合中元素个数:" + set.size());

Iterator iter = set.iterator();

while (iter.hasNext()) {

Person p = (Person) iter.next();

System.out.println(p.cardId + ":" + p.name);

}

}

}

程序的运行结果以下:

集合中元素个数:2

001:Tom

001:Tom

发现程序的运行结果是有“问题”的,由于Set集合中是不容许存放重复的元素的,可是两个Person对象的属性值是彻底相同的,怎么还都能存放进去呢?要找到问题的答案,须要了解下Set集合的存放原理。

图:  Set集合存储

从上图中能够看到,Set集合中的元素的无序性,可是Set集合是怎么判断每一个元素的存放的位置呢?在向Set集合中存放元素时,Set集合根据元素的hashCode()方法来获取一个int类型的数据,而后根据这个数据来计算元素在集合中的位置。可是在存储元素时会出现两个元素的hashCode()方法返回值相同的状况,好比上图的对象C和D就出现了这种状况,致使计算出元素在集合中的位置相同,这种状况称之为“冲突”。若是发生了冲突,Set集合会根据发生冲突元素之间调用equals()方法进行比较,若是equals()返回值为true,说明两个元素为相同的元素,这样会致使添加操做无效。若是equals()返回值为false,说明两个元素不相同,这样Set集合会将该元素进行偏移存储。“冲突”发生的频率越高,Set集合的性能就越低,要尽量的避免冲突的发生, 就要在类中重写hashCode()方法,而且要尽量的保证hashCode()方法返回值是惟一的。在重写hashCode()方法时有个技巧,就是让对象中的数值属性和一个素数相乘,并将积相加,对于对象类型,调用其hashCode()方法便可。

对于hashCode()和equals()两个方法有这样的规律,hashCode()方法返回值相同时,equals()方法比较并不必定相等,可是equals()方法比较相等,hashCode()方法返回值是相同的。

代码演示:实现Person对象的equals和hashCode方法 

public class Person {

String cardId;

String name;

public Person(String cardId, String name) {

this.cardId = cardId;

this.name = name;

}

public int hashCode() {

return cardId.hashCode() + name.hashCode();

}

public boolean equals(Object obj) {

if (obj == null){

return false;

}

if(obj instanceof Person){

Person other = (Person) obj;

if (cardId == null) {

if (other.cardId != null){

return false;

}

else if (!cardId.equals(other.cardId)){

return false;

}

if (name == null) {

if (other.name != null){

return false;

}

else if (!name.equals(other.name)){

return false;

}

}else{

return false;

}

return true;

}

}

再次运行Demo3的代码,运行结果以下:

集合中元素个数:1

001:Tom

3. 本章总结

Map集合用于保存具备映射关系的数据,即以键值对(key->value)的方式来存储数据。所以在Map集合内部有两个集合,一个集合用于保存Map中的key(键),一个集合用于保存Map中的value(值),其中key和value能够是任意数据类型数据。

Set集合的特色。

Set集合的使用及迭代。

Map集合的迭代。

相关文章
相关标签/搜索