java面试集合相关

准备知识点:

  • java中涉及到集合的接口与实现类都位于java.util包下
  • 集合中接口继承体系如下图所示:


    java集合接口体系.png
  • 集合中各个接口所对应的主要实现类如下图所示:


    集合中接口所对应的主要实现类.png

笔试题目一:

代码如下,问控制台为什么只打印出abc、xyz?

class People {
    String name;
    public People(String name) {
        this.name = name;
    }
    
    public String getName() {
        return this.name;
    }
}
public class HashSetTest {

    public static void main(String[] args) {
        Set<String> set = new HashSet<String>();
        set.add(new String("abc"));
        set.add(new String("xyz"));
        set.add(new String("abc"));
        for (Iterator<String> ite = set.iterator(); ite.hasNext();) {
            String next = ite.next();
            System.out.println(next);
        }
        
    }
}

答案:因为HashSet集合的特性是元素唯一性,而HashSet底层是通过hashcode以及equals方法来判定对象是否唯一的,而String类覆写了equals方法以及hashcode方法这让new String("abc")与new String("abc")的hashcode以及equals方法的比较都为true从而被判定是重复元素无法添加到集合中。

准备知识点:

  • HashSet底层是通过HashMap来实现的
  • HashMap底层是通过Entry数组存储Entry链表来实现的

HashSet增加元素时java底层的操作流程如下:

  • 调用HashMap.put方法来存储key、value
  • 使用hash算法对key.hashCode()计算出一个hash值
  • 让得到的hash值与数组长度 - 1进行&运算得到Entry数组索引位置(该位置用来存储Entry对象,该Entry对象封装了所要添加的key-value对)
  • 得到Entry数组索引后,遍历该索引位置所指向的Entry链表中的Entry元素,如果计算得到的hash值与链表中对象的hash值相等且(key对象是同一个对象或者key对象的内容相等)那么当前所添加的value值覆盖链表中匹配到的元素的value值并返回老的value值,至此HashSet.add方法结束,如果遍历了Entry链表后发现没有一个元素符合条件那么就会将所添加的key对象与value对象封装成一个新的Entry对象并将所计算出来的Entry数组索引指向这个新的Entry对象,而这个新的Entry对象的next属性指向旧的链表,换言之将新生成的Entry对象插入到Entry数组所计算出的索引处所指向的链表头部。

总结:当向集合HashSet中增加对象时,首先集合计算要增加对象的hashCode码,根据该值来得到一个Entry数组索引位置用来存放当前的对象,当在该位置没有一个对象的话,那么HashSet认为该对象在集合中不存在,直接增加进去。如果在该位置有一个Entry对象存在的话,接着将准备增加到集合中的对象与该位置上的Entry链表中的各个Entry对象进行equals方法和hashcode值比较,如果迭代完毕后发现没有一个Entry对象的equals方法以及hashcode值与新增对象相符(注:即比较结果为true),那么集合认为集合中不存在该对象,直接将该对象封装成Entry对象并将其做为数组索引处所指向的Entry链表的头部元素,如果在迭代过程中发现有元素与新增的元素的equals比较以及hashcode值得比较都为true,那么集合认为集合中已经存在该对象了,不会再将该对象增加到集合中去了(注:不过对于HashMap它会将对象的value值替换匹配到的对象的value值并将旧的value值返回)

笔试题目二:

代码如下,控制台会输出什么,并进行相应的解释:

class People {
    String name;
    public People(String name) {
        this.name = name;
    }
    
    public String getName() {
        return this.name;
    }
}
public class HashSetTest {

    public static void main(String[] args) {
        Set<People> set = new HashSet<People>();
        set.add(new People("zhangsan"));
        set.add(new People("lisi"));
        set.add(new People("zhangsan"));
        for (Iterator<People> ite = set.iterator(); ite.hasNext();) {
            People next = ite.next();
            System.out.println(next.getName());
        }
    }
}

答案:控制台会输出zhangsan、lisi、zhangsan。因为People类没有覆写equals以及hashcode方法,从而导致HashSet底层判定它们为不同元素,最终被存储到了集合中。

笔试题目三:

代码如下,控制台会输出什么?为什么?


class People {
    String name;
    public People(String name) {
        this.name = name;
    }
    
    public String getName() {
        return this.name;
    }
    
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (null == obj) return false;
        if (obj instanceof People) {
            People p = (People)obj;
            return p.name.equals(this.name);
        }
        return false;
    }
    
    @Override
    public int hashCode() {
        return this.name.hashCode();
    }
   
}
public class HashSetTest {

    public static void main(String[] args) {
        Set<People> set = new HashSet<People>();
        set.add(new People("zhangsan"));
        set.add(new People("lisi"));
        set.add(new People("zhangsan"));
        for (Iterator<People> ite = set.iterator(); ite.hasNext();) {
            People next = ite.next();
            System.out.println(next.getName());
        }
    }
}

答案:控制台会输出lisi、zhangsan。因为People类重写了equals方法以及hashcode方法,从而在HashSet底层new People("zhangsan")与new People("zhangsan")被判定为重复元素,故无法添加到集合中。

知识点罗列:

  • 当重写equals方法的时候必须重写hashcode方法 (HashMap/HashSet都需要用到这个特性,否则这些对象要存储于HashMap/HashSet集合中的时候会出现重复元素存储的问题,即无法维持元素的唯一性。)
  • 如果一个类的两个对象进行equals方法比较的时候返回true,那么这两个对象必须具有相同的hashcode。(否则存储到HashMap/HashSet里的时候还是无法维持元素的唯一性。注:判定元素重复的条件是:hashcode以及equals方法都返回true)
  • transient修饰的变量无法被序列化到文件中,信息会丢失
  • HashMap对于key为null的存储规则是将它存储在HashMap所维护的Entry数组的第一个元素的链表中(即:key为null的key-value永远存储在数组的第一个元素的链表中)
  • java约定用关键字final修饰的常量名要大写
  • 对于HashSet、HashMap来说,这样做就是为了提高查找效率,使得查找时间不随着set或者map的大小而改变
  • HashMap所维护的Entry数组的默认长度是16
  • HashMap负载因子默认为0.75f(根据经验得到,数组现存储元素个数/数组长度),主要是降低冲突的可能性,当超过该负载因子的时候会扩容数组,不让数组中的Entry链表长度过长而违背HashMap设计的初衷
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容