Java面试题汇总答案之容器

1、Java 容器都有哪些? 

Java中容器有 Collection 和 Map 两大类,其下又有很多子类,如下:

  • Collection
    • List
      • ArrayList
      • LinkList
      • Vector
      • Stack
    • Set
      • HashSet
      • TreeSet
  • Map
    • HashMap
      • LinkedHashMap
    • TreeMap
    • ConcurrentHashMap
    • HashTable

2、Collection 和 Collections 有什么区别?

  • Collection是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法,所有集合都是它的子类,比如List、Set等。
  • Collections是一个包装类,包含了很多静态方法,不能被实例化,就像一个工具类,比如提供排序方法:Collections.sort(list)。

3、List、Set、Map 之间的区别是什么?

List、Set、Map、的区别主要体现在两个方面:元素是否有序、是否允许元素重复。

List集合元素元素有序且允许重复;

AbstractSet 和 HashSet 元素无序且不允许重复;

TreeSet 元素有序但不允许重复;

Map 集合 key 值唯一但 value 值可以不唯一;

AbstractMap 和 HashMap 元素无序,TreeMap 元素有序;

TreeSet 和 TreeMap 元素均通过二叉树进行排序。

4、HashMap 和 Hashtable 有什么区别?

  • 存储:HashMap 允许 key 和 value 值为 null ,而 HashTable 不允许。
  • 线程安全:HashTable 是线程安全的,而 HashMap 是非线程安全的。
  • 推荐使用:在 HashTable 的类注释可以看到,HashTable 是保留类不建议使用,推荐在单线程环境下使用 HashMap 替代,如果需要多线程则使用ConcurrentHashMap替代。

5、如何决定使用 HashMap 还是 TreeMap?

对于在Map中插入、删除、定位一个元素这类操作,HashMap是最好的选择,因为相对而言 HashMap 的插入会更快,但如果要对一个 key 集合进行有序的便利,那么 TreeMap 是更好的选择。

6、说一下 HashMap 的实现原理?

HashMap 是基于 Hash 算法实现的,我们通过 put(key,value) 存储,get(key) 来获取。当传入 key 时,HashMap 会根据 key.hashCode() 计算出 Hash 值,根据 hash 值将 value 保存在 bucket 里。当计算出的 hash 值相同时,我们称为 hash 冲突,HashMap 的做法是用链表和红黑树存储相同 hash 值的value 。当 hash 冲突的个数比较少时,使用链表,否则使用红黑树。

7、说一下 HashSet 的实现原理?

HashSet 是基于 HashMap 实现的,HashSet 底层使用 HashMap 来保存所有元素,因此 HashSet 实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层 HashMap 的相关方法来完成,HashSet 不允许有重复的值。

8、ArrayList 和 LinkedList 的区别是什么?

  • 数据结构实现:ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现的。
  • 随机访问效率:ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。
  • 增加和删除效率:在非首尾的增加和删除操作,LinkedList 要 ArrayList 效率要高,因为 ArrayList 增加删除操作要影响数组内其他元素的下标。

综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList ,而在插入和删除操作较多时,更推荐 LinkedList。

9、如何实现数组和 List 之间的转换?

数组转 List:使用Arrays.asList(array)进行转换。

List转数组:使用 List 自带的toArray()方法。

示例代码:

List<String> nameList = new ArrayList<String>();
nameList.add("lisi");
nameList.add("zhangsan");
nameList.toArray();//返回值为Object类型

Integer age[] = new Integer[2];
age[0] = 21;
age[1] = 23;
Arrays.asList(age);

10、ArrayList 和 Vector 的区别是什么?

  • 线程安全:Vector使用 Synchronized 来实现线程同步,是线程安全的,而 ArrayList 是非线程安全的。
  • 性能:ArrayList 在性能方面优于 Vector。
  • 扩容:ArrayList 和 Vector 都会根据实际的需要动态的调整容量,只不过在 Vector 扩容每次会增加 1 倍,而 ArrayList 只会增加 50% 。

11、Array 和 ArrayList 有何区别?

  • Array 可以存储基本数据类型和对象,ArrayList 只能存放对象。
  • Array 是指定大小的,而 ArrayList 大小是自动扩展的。
  • Array 内置方法没有 ArrayList 多,比如 addAll、removeAll、iteration 等方法只有 ArrayList 有。

12、在Queue 中 poll()和 remove()有什么区别?

  • 相同点:都是返回第一个元素,并在队列中删除返回的对象。
  • 不同点:如果没有元素 poll() 会返回 null ,而 remove() 会直接抛出 NoSuchElementException 异常。

13、哪些集合类是线程安全的?

  • 线程安全:就是当多线程访问时,采用加锁机制对数据进行保护,防止出现数据不一致或数据被污染的情况。
  • 线程不安全:多线程访问时不提供数据保护。对于线程不安全的问题,一般使用 synchronized 关键字加锁同步控制。
  • 线程安全的集合类:Vector、HashTable。

14、迭代器 Iterator 是什么?

迭代器(Iterator)是一个对象,它的作用是遍历并选择集合中的对象,它提供了一种访问一个容器对象的各个元素,而又不必该对象内部细节的方法。

通过迭代器,开发人员不需要了解容器底层的结构,就可以实现对容器的遍历。

由于创建迭代器的代价小,因此迭代器通常别称为轻量级的容器。

15、Iterator 怎么使用?有什么特点?

  1. 使用容器的 iterator() 方法返回一个 Iterator ,然后通过 Iterator 的 next() 方法返回第一个元素。
  2. 使用 Iterator 的 hasNext() 方法判断容器中是否还有元素,如果有,可以使用 next() 方法获取下一个元素。
  3. 可以通过 Iterator 的 remove()删除迭代器返回的元素。
List<String> nameList = new ArrayList<String>();
nameList.add("lisi");
nameList.add("zhangsan");
nameList.add("wanger");
nameList.add("zhaowu");

//while循环遍历
Iterator  iterator = nameList.iterator();
while (iterator.hasNext()){
    System.out.println(iterator.next());
}

//for循环遍历
for(Iterator iterator1 = nameList.iterator();iterator1.hasNext();){
    System.out.println(iterator1.next());
}

特点:

Iterator 模式是用于遍历结合的标准访问方法,它可以把访问逻辑重不同类型的集合中抽象出来,从而避免向客户端暴露集合的内部结构。

Iterator遍历容器时,开发人员不需要理解容器的底层结构;

创建 Iterator 迭代器的代价非常小,是一个轻量级的容器;

客户端不直接和集合打交道,客户端自身不维护遍历集合的“指针”。

16、Iterator 和 ListIterator 有什么区别?

List 集合中可以通过 iterator() 方法和 listIterator() 两个方法取得 Iterator 迭代器,两种迭代器在有些情况下是不能通用的,Iterator 和 ListIterator 的主要区别在以下一个方面:

  1. ListIterator 有add() 方法,可以向 List 中添加对象,而 Iterator 不能;
  2. ListIterator 和 Iterator 都有 hasNext() 和 next() 方法,可以实现顺序向后遍历;但是 ListIterator有 hasPrevious() 和 previous() 方法,可以实现逆序遍历;Iterator 不能;
  3. ListIterator 可以定位当前的索引位置,nextIndex() 和 previousIndex()可以实现。Iterator 没有此功能;
  4. ListIterator 和 Iterator 都可以实现对象的删除,但是 ListIterator 可以通过 set() 方法实现对象的修改;

因为 ListIterator 的这些功能,可以实现对 LinkList 等 List 数据结构的操作。

ListIterator 常用API

ListIterator listIterator = nameList.listIterator();
while (listIterator.hasNext()){
    listIterator.next();//object,下一个
    listIterator.previous();//void,前一个

    listIterator.set("suxing");// void,修改
    listIterator.remove();//void,移除
    listIterator.add("suxing_1");//void,添加

    listIterator.hasPrevious();//boolean,是否有前一个

    listIterator.nextIndex();//int,下一个的索引
    listIterator.previousIndex();//int,前一个索引
}

//不能这样循环添加,否则会内存溢出
//java.lang.OutOfMemoryError: Java heap space
ListIterator listIterator = nameList.listIterator();
while (listIterator.hasNext()){
    listIterator.add("suxing_a");
}

17、怎么确保一个集合不能被修改?

首先,我们会想到 final 关键字,final 关键字修饰的类不能被继承,final 修饰的方法不能被重写,final 修饰的变量是常量,必须赋初值;

但是,final 修饰基本类型的变量时表示变量的值不能被改变,final 修饰引用类型的变量时,表示这个引用的地址不能改变,但是这个引用所指向的对象里面的内容是可以被改变的;

那么,怎么确保一个集合不能被修改?首先我们清楚,集合(Map,Set,List)都是引用类型,使用 final 关键字是不能达到目的的;

示例:

//final 失败测试
final Map<Integer,String> map = new HashMap<Integer,String>();      
map.put(1,"suxing");
map.put(2,"zhangsan");
System.out.println(map);//输出:{1=suxing, 2=zhangsan}
map.put(1,"lisi");
System.out.println(map);//输出:{1=lisi, 2=zhangsan}

由输出结果得出:final 修饰的map集合的内容是可以被修改的。 

我们可以使用 Collections 包装类下的 unmodifiableMap() 方法,通过这个方法返回的 Map 集合是不能被修改的,修改时会报java.lang.UnsupportedOperationException 异常。

同理:Collections 包装类也提供了对 List 和 Set 集合的操作方法。

Collections.unmodifiableList(List)

Collections.unmodifiableSet(Set)

示例:

//Collections 包装类成功示例
Map<Integer,String> map = new HashMap<Integer,String>();
map.put(1,"suxing");
map.put(2,"zhangsan");
System.out.println(map);//输出:{1=suxing, 2=zhangsan}
        
Map map_a  = Collections.unmodifiableMap(map);
map_a.put(1,"lisi");//报UnsupportedOperationException异常
        
//List 和 Set 方法
//List list_a = Collections.unmodifiableList(list);
//Set set_a = Collections.unmodifiableSet(set);

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值