问题及答案来源自《Java程序员面试笔试宝典》第四章 Java基础知识 4.9容器

1、Java Collections框架是什么?

Java Collections框架中包含了大量集合接口以及这些接口的实现类和操作它们的算法(比如排序、查找、反转、替换等)

具体而言主要提供了List(列表)、Queue(队列)、Set(集合)、Stack(栈)、Map(映射表)等数据结构

其中List(列表)、Queue(队列)、Set(集合)、Stack(栈)都继承自Collection接口

Collection的作用只是提供维护一组对象的基本接口而已

Collection是整个集合框架的基础,它里面存储一组对象,表示不同类型的Collections

Collection框架类图如下:

下面主要介绍Set、List和Map这三个接口:

Set:表示数学意义上的集合,主要特点是集合中的元素不能重复,因此存入Set的每个元素都必须定义equals方法来确保对象的

唯一性。该接口有两个实现类:HashSet和TreeSet,TreeSet中的元素是有序的

List:又称为有序的Collection,是按按对象进入的顺序保存对象,所以它对列表中每个元素的插入和删除位置进行精确的控制。

同时它可以保存重复的对象。LinkedList、ArrayList和Vector都实现了List接口

Map:提供一个从键映射到值得数据结构,用于保存键值对,值可以重复但键不能重复。实现它的类:HashMap、TreeMap、

LinkedHashMap、WeakHashMap、IdentityHashMap。虽然它们实现了相同的接口,但是执行效率不是完全相同。具体而言,

HashMap基于散列表实现,可以快速查询元素,LinkedHashMap采用列表来维护内部的顺序,TreeMap基于红黑树的数据结构

实现,内部元素是按需排列的

2、什么是迭代器?

迭代器是什么:

迭代器(Iterator)是一个对象,它的工作是遍历并选择序列中的对象,它提供了一种访问容器对象中的元素,而又不必暴露

该对象内部细节的方法。通过迭代器,开发人员不需要了解容器底部结构就可以实现对容器的遍历

迭代器的使用:

使用iterator()方法返回一个Iterator,然后通过Iterator的next方法返回第一个元素

使用Iterator的hasNext方法判断是否还有元素,如果有可以使用next方法获取下一个元素

实例如下:

1 public classIteratorDemo {2 public static voidmain(String[] args) {3 Collection c = new ArrayList();4 c.add("1");5 c.add("2");6 c.add("3");7 c.add("4");8 c.add("5");9 c.add("6");10 Iterator it = c.iterator(); //获取集合的迭代器对象

11 while(it.hasNext()){ //反复判断有没有下一个元素

12 String s = it.next(); //取出下一个元素

13 System.out.println(s);14 }15 }16 }

并发修改异常:

迭代的常规用法中我们要尽量避免在迭代过程中为集合添加/删除数据。否则会报错,原因是Java抛出了并发修改异常

迭代过程中并发修改异常的原因为:

迭代器中”记忆”的集合长度与集合中实际长度不同,而导致出现索引与实际元素不符甚至无限循环的情况发生

所以在使用Iterator时,避免类似操作,for循环底层为迭代器实现,所以也需要避免类似操作

有些迭代器避免了这样的问题,如ListIterator,但该类并不通用也不常用,实际开发中很少使用,只需要简单了解

1 //java规定: 如果一个集合使用迭代器遍历,那么在遍历的过程中不允许修改集合的长度(增加或删除)

2 public classConcurrentModificationExceptionDemo {3 public static voidmain(String[] args) {4 Collection c = new ArrayList();5 c.add("1");6 c.add("2");7 c.add("itcast");8 c.add("4");9 c.add("5");10 c.add("6");11 Iterator it = c.iterator(); //获取集合的迭代器对象

12 while(it.hasNext()){ //反复判断有没有下一个元素

13 String s = it.next(); //取出下一个元素

14 if("itcast".equals(s)){15 //如果相等添加一个大写ITCAST

16 c.add("ITCAST"); //报异常 => ConcurrentModificationException

17 }18 System.out.println(s);19 }20 }21 }

并发修改异常解决方法(单线程):

把要删除或添加的元素保存到一个集合中,遍历结束后调用removeAll方法或addAll方法来进行删除或添加

并发修改异常解决方法(多线程):

使用线程安全的容器:比如ConcurrentHashMap和CopyOnWriteArrayList等

使用迭代器遍历容器时对容器的操作放到synchronized代码块中

引申 - Iterator和ListIterator有什么区别?

Iterator只能正向遍历,适用于获取移除元素。而ListIterator继承自Iterator,专门针对List,可以从两个方向来遍历List,

同时也支持元素的修改

3、ArrayList、Vector和LinkedList有什么区别?

三者共同点:

三者均在java.util包中,均可以作为伸缩数组,即可以动态改变长度的数组

ArrayList和Vector的相同点:

都基于存储元素的Object[] array来实现的,会在内存中开辟一块连续的空间来存储,由于数据存储是连续的,因此它们

支持用下标访问元素,访问数据速度比较快、插入数据比较慢(要移动元素)

ArrayList和Vector的不同点:

Vector扩充空间默认扩充为原来的2倍(每次扩充空间的大小是可以设置的)

ArrayList扩充空间默认扩充为用来的1.5倍(没有提供方法来设置空间扩充的方法)

Vector是线程安全的(Vector的大部分方法是直接或间接同步的)

ArrayList不是线程安全的(没有一个ArrayList的方法是同步的)

关于LinkedList:

LinkedList是采用双向列表来实现的,对数据的索引需要从列表头开始遍历,因此用于随机访问则效率比较低,但是插入元素

时不需要对数据进行移动,故插入效率比较高,同时LinkedList是非线程安全的容器

实际使用选择:

当对数据的主要操作为索引或只在集合的末端增加、删除元素时:ArrayList或Vector

当对数据的操作主要为指定位置的插入或删除操作时:LinkedList

当在多线程中使用容器时(即多个线程会访问容器):Vector

4、HashMap、HashTable、TreeMap和WeakHashMap有哪些区别?

Map是什么:

Java为数据结构中的映射定义了一个接口java.util.Map,它包括三个实现类:HashMap、HashTable和TreeMap

Map是用来存储键值对的数据结构,在数组中通过数组下标来对其内容索引的,而在Map中则是通过对象来进行

索引,用来索引的对象叫做key,其对于的对象叫做value

HashMap:

HashMap是最常用的Map,是根据键的HashCode值存储数据,根据键可以直接获得它的值,具有很快的访问速度

HashMap和HashTable的区别:

HashMap是HashTable的轻量级实现(非线程安全的实现),都实现了Map接口

HashMap中允许存在null键值(最多只能有一条),而HashTable中不允许存在null键值的

HashMap把HashTable的contains方法改成了containsValue和containsKey

HashTable是线程安全的,而HashMap不支持线程的同步(不是线程安全的)

HashMap效率比HashTable高(但多线程访问HashMap时开发人员要提供额外的同步机制)

HashTable使用Enumeration,而HashMap使用Iterator

HashMap和TreeMap和LinkedHashMap:

HashMap里存入的键值对在取出时是随机的,一般而已在Map中插入、删除和定位元素最好用HashMap

TreeMap实现了SortMap接口,能把它保存的记录根据键排序,因此需要排序的键值对可以使用TreeMap

LinkedHashMap是HashMap的子类,如果需要输出的顺序和输入的顺序相同,可以使用LinkedHashMap

WeakHashMap和HashMap:

WeakHashMap里面的key采用的是弱引用的方式,只要key不再被外部引用就可以被垃圾回收期回收,而HashMap

中的key采用的是强引用的方式,就算没有被外部引用,但只有这个key从HashMap删除后才能被垃圾回收器回收

在HashTable上下文中,同步指什么?

同步意味着一个时间点只能有一个线程可以修改hash表,任何线程在执行HashTable的更新操作前都需要获取

对象锁,其他线程则等待锁的释放

如何实现HashTable的同步?

可以通过Map m = Collections.synchronizedMap(new HashMap())来达到同步的效果。具体而言,该方法返回一个

同步的Map,该Map封装了底层的HashMap的所有方法,使得底层的HashMap即使是在多线程的环境中也是安全的

5、用自定义类型作为HashMap或HashTable的key需要注意哪些问题?

HashMap和HashTable是用来存放键值对的一种容器,在使用这个容器时不能存储重复的键,也就是说每个键只能

映射一个值,当有重复的键时不会创建新的映射关系,而会使用先前的键值

但是当用自定义的类的对象作为HashMap中的key时会给人造成一种假象:key是可以重复的,示例如下:

1 classPerson {2 String id;3 String name;4

5 publicPerson(String id, String name) {6 this.id =id;7 this.name =name;8 }9

10 publicString toString() {11 return "id=" + id + ", name=" +name;12 }13

14 }15

16 public classHashMapTest {17 public static voidmain(String[] args) {18 System.out.println("Use String as key: ");19 HashMap hm = new HashMap();20 Person p1 = new Person("111", "name1");21 Person p2 = new Person("111", "name1");22 hm.put(p1, "address1");23 hm.put(p2, "address2");24

25 Iterator iter =hm.entrySet().iterator();26 while(iter.hasNext()) {27 Map.Entry entry =(Map.Entry) iter.next();28 Person key =(Person) entry.getKey();29 String val =(String) entry.getValue();30 System.out.println(key + " - " +val);31 //输出结果如下:32 //id=111, name=name1 - address133 //id=111, name=name1 - address2

34 }35 }36 }

上面这种现象的原因是:

虽然这两个对象有着同样的内容,但是存在在内存中不同的地址里,向hashMap中添加对象时调用equals方法

的返回值为false,HashMap会认为它们是两个不同的对象,就会分别创建不同的映射关系,因此为了实现在向

HashMap中添加键值对时可根据对象的内容来判断两个对象是否相等,就需要重写equals方法和hashCode方法:

1 classPerson {2 String id;3 String name;4

5 public inthashCode(){6 returnid.hashCode();7 }8

9 publicPerson(String id, String name) {10 this.id =id;11 this.name =name;12 }13

14 publicString toString() {15 return "id=" + id + ", name=" +name;16 }17

18 public booleanequals(Object obj){19 Person p =(Person)obj;20 if(p.id.equals(this.id)){21 return true;22 } else{23 return false;24 }25 }26

27 }28

29 public classHashMapTest {30 public static voidmain(String[] args) {31 System.out.println("Use String as key: ");32 HashMap hm = new HashMap();33 Person p1 = new Person("111", "name1");34 Person p2 = new Person("111", "name1");35 hm.put(p1, "address1");36 hm.put(p2, "address2");37

38 Iterator iter =hm.entrySet().iterator();39 while(iter.hasNext()) {40 Map.Entry entry =(Map.Entry) iter.next();41 Person key =(Person) entry.getKey();42 String val =(String) entry.getValue();43 System.out.println(key + " - " +val);44 //输出结果如下:45 //id=111, name=name1 - address2

46 }47 }48 }

总结 - 开发者在使用自定义类作为HashMap的key时,需要注意以下几点:

如果想根据对象的相关属性来自定义对象是否相等的逻辑就要重写equals方法和hashCode方法

当自定义类的对象作为HashMap(hashTable)的key时,最好把这个类设置为不可变类

从HashMap的工作原理可以看出如果两个对象相等,那么这两个对象有相同的hashCode值,反之则不成立

6、Collection和Collections有什么区别?

Collection是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法。实现该接口的类主要有List和Set,

该接口的主要设计目标是为各种具体的集合提供最大化统一的操作方式

Collections是针对集合类的一个包装类,它提供了一系列的静态方法以实现对各种集合的搜索、排序、线程安全化

等操作,其中绝大多数方法都是用来处理线性表,Collections类如同工具类,不能被实例化(类似Math类)

使用示例:

1 public classCollectionsDemo {2 public static voidmain(String[] args) {3 List list = new LinkedList();4 int array[] = {1, 7, 3, 2};5 for(intnum: array){6 list.add(newInteger(num));7 }8

9 for(int i=0; i< array.length; i++){10 System.out.print(list.get(i));11 }12 System.out.println();13 //1732

14

15 Collections.sort(list);16 for(int i=0; i< array.length; i++){17 System.out.print(list.get(i));18 }19 System.out.println();20 //1237

21 }22 }

java用来存储键值的容器是_Java容器 - osc_y0caef0i的个人空间 - OSCHINA - 中文开源技术交流社区...相关推荐

  1. java下拉框及响应函数_MVC 实现下拉框 - osc_88wjsceo的个人空间 - OSCHINA - 中文开源技术交流社区...

    MVC动态实现下拉框的方式有很多种,但是方便快捷的却是很少,现在记录一种常用的下拉框实现方式: 1.先看看视图代码是怎么写的 这是一个下拉框: @Html.DropDownList("sel ...

  2. java 0l是多少_Java 构造器 - osc_0ltyoebk的个人空间 - OSCHINA - 中文开源技术交流社区...

    Java 基础构造器 构造器: 构造对象. 1.构造方法的名字必须和类名字保持一致. 2.构造方法没有返回类型.(不是没有返回值!!!!!!!!!!!!!) 3.构造方法可以定义参数,也可以不定义,参 ...

  3. java关键字汉化_Java关键字 - 乱流的个人空间 - OSCHINA - 中文开源技术交流社区

    static关键字 (1)静态的意思.可以修饰成员变量和成员方法. (2)静态的特点: A:随着类的加载而加载 B:优先与对象存在 C:被类的所有对象共享 这其实也是我们判断该不该使用静态的依据. 举 ...

  4. java 原子量_Java原子量 - Rickxue的个人空间 - OSCHINA - 中文开源技术交流社区

    所谓的原子量即操作变量的操作是"原子的",该操作不可再分,因此是线程安全的. 为何要使用原子变量呢,原因是多个线程对单个变量操作也会引起一些问题.在Java5之前,可以通过vola ...

  5. java空格转义_java转义字符 - simpower的个人空间 - OSCHINA - 中文开源技术交流社区...

    1. 注意regex的写法 String   newName=name.replaceFirst("d:\\\\racke\\\\","http://192.168.0. ...

  6. java中类型转换的造型_Java-类型转换 - 吉胖子很瘦的个人空间 - OSCHINA - 中文开源技术交流社区...

    类型转换 在实际开发过程中,存在类型转换,类型转换分为两种,自动类型转换和强制类型转换. 自动类型转换 程序编译或运行过程中,jvm可以发生自动类型转换.转换规则: [1]容量小的数据类型可以自动转换 ...

  7. java完成九宫格数独_数独(简易九宫格) - 小黑202的个人空间 - OSCHINA - 中文开源技术交流社区...

    一.ViewController.h中代码 // //  ViewController.h //  ThirdSignleView // //  Created by on 15/6/10. //   ...

  8. java代码中的缓存类怎么找,JAVA缓存的实现 - dreamcloudz的个人空间 - OSCHINA - 中文开源技术交流社区...

    缓存可分为二大类: 一.通过文件缓存,顾名思义文件缓存是指把数据存储在磁盘上,不管你是以XML格式,序列化文件DAT格式还是其它文件格式: 二.内存缓存,也就是实现一个类中静态Map,对这个Map进行 ...

  9. 5.0 java集合框架中的接口collection属于_JAVA集合框架 - osc_cyo2dovg的个人空间 - OSCHINA - 中文开源技术交流社区...

    一.为什么要使用集合 单个数据,可以用变量保存: 多个数据,可以用数组保存: 但是对于存储多个数据且数量不确定的情况,使用集合: 二.集合和数组的区别 (1)数组: 1.只能保存同一种类型的数据: 2 ...

最新文章

  1. 避免成为垃圾邮件_如何避免犯垃圾
  2. 解决ExcelReport导出Excel报Number of rules must not exceed 3错误的问题
  3. 2016/7/29作业
  4. Qt Creator连接通用远程Linux设备
  5. 圆形渐变shader_Flutter 中渐变的高级用法
  6. 将非事务性资源绑定到JTA事务中的几种模式
  7. 基于JavaFX的Linux进程树
  8. IoC容器Autofac(2) - 一个简单示例(附demo源码)
  9. stm32f103r8t6的晶振频率_STM32F103R8T6[1]
  10. 使用WndProc来处理消息
  11. C语言工程实践--物业费管理系统
  12. c语言 格雷码构造问题,格雷码剖析
  13. bom_clear.php,金蝶KIS专业版常用SQL语句
  14. oracle 关联查询两个表,两张表关联查询,该怎么处理
  15. 认识IL代码---从开始到现在 第二篇
  16. 关于es6 async函数中reject状态的promise处理
  17. POJ3295 Tautology
  18. HIVE如何进行随机抽样
  19. 小象学院之python实例
  20. ES学习笔记十-数据建模

热门文章

  1. vue中的自定义分页插件组件
  2. Linux平台下裸设备的绑定:
  3. 二维平面坐标系中,判断某点是否在正六边形内 | python 实现 + 数学推导(已知正六边形六个顶点坐标)
  4. lbs与gis的计算机存储模式,基于GIS的移动终端LBS系统建设与实现
  5. Oracle如何根据SQL_TEXT生成SQL_ID
  6. C++语言基础 例程 派生类的声明与构成
  7. 伺服系统 计算机仿真,减摇鳍电伺服系统的计算机仿真研究-应用科技-哈尔滨工程大学.PDF...
  8. git 远程仓库回滚
  9. 三维点云学习(2)中-Kd-tree (k-dimensional tree)
  10. Failed to execute goal org.apache.maven.plugins:maven-deploy-plugin:2.8.2:deploy