面试_java_容器

89 阅读6分钟

容器一览


集合总的来说分成Collection和Map两大部分。Collection包括List,Set和Queue三大接口。List接口下最常用的实现类是ArrayList,Vector以及和Queue接口共同实现的LinkedList;Set接口下常用的实现类是HashSet和TreeSet;Map本身就是一个大的接口,最常用的实现类是HashMap和TreeMap。值得注意的是,HashSet是套壳版的HashMap,而TreeSet是套壳版的TreeMap。

他们的特性:List有序可重复;对于Map和Set,都是不重复的,他们如果用Hash实现,就是无序的;如果用Tree实现,就是有序的。

线程安全的集合类:Vector、HashTable、和Stack。





List 和 Set 有哪些区别?和Map?

List、Set 都是继承自Collection 接口,区别主要有以下几点:

  • 关于重复:list方法可以允许重复的对象,而set方法不允许重复对象。
  • 关于有序:list是一个有序的容器,保持了每个元素的插入顺序,即输出顺序就是输入顺序;而set是无序容器,无法保证每个元素的存储顺序,TreeSet通过 Comparator 或 Comparable 维护了一个元素值大小顺序。
  • null元素:list可以插入多个null元素,而set只允许插入一个null元素。
  • 效率:List的查找效率高,插入删除元素效率低;Set相反。
  • 遍历方式:List 支持for循环,也就是通过下标来遍历,也可以用迭代器(Iterator);但是Set只能用迭代器,因为他无序,无法用下标来取得想要的值。

Map本身就是一个大接口,key值必须唯一,但value可以重复。如果是HashMap实现类,元素无序;如果是TreeMap实现类,元素有序。




Map和Set的去重以及排序策略


对于 HashMap HashSet (无重复,无序)

HashMap的去重策略:

  1. 计算 key 的 hash 值,再通过 (数组长度 - 1) & hash 得出数组下标 index;
  2. 查看 table[index] 是否存在数据,没有数据就构造一个包含key-value的Node节点存放在这里
  3. 若已存在数据,说明发生了冲突(即hash值一样),判断key是否相等,若相等,用equals比较value,若不同就用新value替换旧value,若相同就不添加了,break;
  4. 如果key不相等,则在链表或红黑树中追加数据

HashSet的去重策略:

HashSet就是套壳版的HashMap,它表面上存的是单个数据而非键值对,其实源码里是统一拿一个final 类型的 Object 对象作为value,只起占位作用。也就是说,自身存的“单个数据”是key,value是统一的 Object,只是为了和HashMap形式上保持一致!

HashSet源码中add方法就是调用的HashMap的put方法,add(x)=put(x, Object))

从而方便全盘借鉴HashMap的去重策略,因此只研究ashMap就够了。



对于 TreeMap TreeSet(无重复,有序)

去重策略:

TreeSet借鉴的是TreeMap,直接分析TreeMap 就够了。

在TreeMap 的 add方法中可以看到,TreeMap底层的数据结构是一个红黑树,如果新增一个key进来,首先会判断这key的值在树中是否存在,若存在则替换掉,若不存在则插入其中。


排序策略:

仍然是只研究TreeMap就够了。在存入数据的时候,TreeMap按照Comprator排序。要么key所属的类实现Comparable接口,要么自定义一个实现了Comparator接口的比较器。




迭代器 Iterator 是什么?怎么使用?有什么特点?

迭代器提供一种遍历Collection 集合中元素的方法。

使用hasNext()方法作为循环条件;再用next()方法得到下一个元素;遍历过程中不能直接调用该集合的删除方法,那样会报异常,可以使用迭代器对象的remove()方法来删除集合元素。

Iterator遍历集合元素时,只能单向遍历,而ListIterator可以双向进行遍历。




Java中如何确保一个集合不能被修改

  • 错误的方案1:使用final修饰

    集合类的对象是引用类型,用final修饰只能保证其指向不能变,但指向的对象中的内容是可以变的

  • 错误的方案2:使用Arrays.asList创建

    虽然不能再add(),但是可以set(),不满足不可改变的要求

  • 正确的方案:使用Collections.unmodifiableCollection()方法来创建一个只读集合

    // Map集合
    Map<Integer, String> map = new HashMap<>();
    map.put(1, "s");
    map = Collections.unmodifiableMap(map);
    map.put(2, "d");    // 异常: Java.lang.UnsupportedOperationException
    
    // List集合
    List<String> list = new ArrayList<>();
    list.add("s");
    list = Collections.unmodifiableList(list);
    list.add("d");      // 异常: Java.lang.UnsupportedOperationException
    
    // Set集合
    Set<String> set = new HashSet<>();
    set.add("s");
    set = Collections.unmodifiableSet(set);
    set.add("d");       // 异常: Java.lang.UnsupportedOperationException
    



ArrayList 和 Array 的区别?

  1. 数组可以包含基本数据类型和引用类型,但要求是同一种;ArrayList只能存引用类型,但可以是多种引用类型,因为底层的数组类型是Object(如果存基本数据类型,存的将是他们的包装类)。
  2. Array 是指定固定大小的,而 ArrayList 是可以自动扩容的。
  3. ArrayList是List接口的实现类,相比数组支持更多的方法和特性,比如比如 addAll、removeAll、iteration。

数组Array 与 列表List 之间相互转换?

String[] strs = new String[] {"aaa", "bbb", "ccc"};

List<String> list = Arrays.asList(strs);   				  // 数组转化为list
String[] array = list.toArray(new String[list.size()]);   // list转化为数组



ArrayList 和 Vector的区别?

相同点:

  • 都实现了List接口,都是动态数组可扩容。

不同点:

  • Vector是线程安全的,每个方法上都套上了synchronized,但也正因此性能会差一些;ArrayList是不安全的。

    可通过 Collections 的 synchronizedList 方法将ArrayList变成安全的:

    List<String> syncList = Collections.synchronizedList(arraylist);
    
  • Vecto扩容时空间增加为原来的2倍;ArrayList是1.5倍。




ArrayList 和 LinkedList 的区别?

  • ArrayList更擅长查询和更新,因为顺序存储的特性支持用下标随机访问。对于插入删除,效率较低,因为要移动数组内其他元素,但如果是在末尾操作,效率大体相同。
  • LinkedList更擅长插入和删除,只修改指针指向即可。对于查询和更新,需要从某一端遍历,效率很低。另外,指针除了存元素值之外,还存了前后两个元素的引用,因此LinkedList要更占内存。
  • ArrayList支持扩容,LinkedList不支持也不需要。二者都是线程不安全。



HashMap和HashTable的区别

1、线程安全

首先HashMap是线程不安全的,而HashTable是线程安全的,因为get和put方法都使用了synchronized关键字来修饰,通过锁机制来保证线程安全。


2、性能

既然HashTable用到了锁,那就有可能导致线程阻塞,因此性能较差。如果要线程安全又要保证性能,建议使用 JUC 包下的 ConcurrentHashMap。


3、继承的父类不同

HashMap继承的是AbstractMap类;HashTable继承的是Dictionary类。


4、初始容量和扩容尺度

HashMap 的初始容量为:16,Hashtable 初始容量为:11 HashMap 扩容规则为当前容量翻倍,Hashtable 扩容规则为当前容量翻倍 + 1

5、NULL的存储

Hashtable 的键和值都不允许为 null ,否则报空指针异常。

HashMap 的值可以为null,但只能有一个键为null;后续再有key=null,会覆盖之前的键值对。





www.cnblogs.com/fengzheng/p… github.com/Snailclimb/… blog.cnkj.site/Interview/I…