首页 > 其他分享 >面试官:集合使用时应该注意哪些问题?我:应该注意该注意的问题!

面试官:集合使用时应该注意哪些问题?我:应该注意该注意的问题!

时间:2024-03-04 09:02:07浏览次数:32  
标签:面试官 应该 return ArrayList list 注意 集合 null size

写在开头

面试官:“小伙子,java的集合学过吗?”
我:“肯定学过呀!”,这时候的我自信满满,手撕集合八股文嘛,早已背的滚瓜烂熟了呀。
面试官:“那你来讲讲集合使用时,应该注意哪些问题吧”
我:“额,这,我想想哈。”,什么!这面试官不按套路出牌,上来就问注意事项,打我一个措手不及啊。
我:“嗯 ~,我觉得应该注意该注意的问题!”
面试官:“下一位!”

集合使用注意事项

经过了十几篇博客的总结,java集合部分的知识点,大致上就学完了,当然,Collection与Map拥有着大量的子集,我们无法通过短短的五六万字就可以全部讲解完,后续会持续性的完善,现阶段呢,我们就先讲那么多哈。
今天,我们结合《阿里巴巴 Java 开发手册》,来对集合日常开发使用过程中的注意事项进行总结,大致可以分为以下几点。

集合判空

判空是集合在使用时必须要做的操作,我们得保证我们所创建的,或者所调用的别人创建的集合对象可用(不为null,不为空),才能进行下一步业务逻辑的开发。
那么,如何进行判空处理呢?我们这里以ArrayList为例,去列举一下它的判空处理方式。

【代码示例1】

public class Test {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        //方式一,list != null && !list.isEmpty()
        if (list != null && !list.isEmpty()) {
            for (Integer integer : list) {
                System.out.println("方式1:"+list);
            }
        } else {
            System.out.println("ArrayList读取异常!");
        }
        //方式二,list != null && list.size() > 0
        if (list != null && list.size() > 0) {
            for (Integer integer : list) {
                System.out.println("方式2:"+list);
            }
        } else {
            System.out.println("ArrayList读取异常!");
        }
        //方式三,org.apache.commons.collections包下的 CollectionUtils工具类
        if (CollectionUtils.isNotEmpty(list)) {
            for (Integer integer : list) {
                System.out.println("方式2:"+list);
            }
        } else {
            System.out.println("ArrayList读取异常!");
        }
    }
}

我们在这里列举了3种判空方式,那这3种方式之间又有何区别呢?让俺来分析一波。

第一点: 我们要知道null与空的区别,这是两个概念,很多初学者会混淆,为null表示这个list还没有分配内存,也就在堆中不存在,而空表示list的初始化工作已经完成,只不过里面没有任何元素。
我们在判空的时候需要注意,!=null 要放在&&逻辑与的前面判断,因为,我们首先要保证list的初始化完成,才能去判断集合元素的是否存在,否则会报nullException。
第二点: list.isEmpty() 与 list.size() == 0功能实现上一致,但在《阿里巴巴 Java 开发手册》中指出:

判断所有集合内部的元素是否为空,使用 isEmpty() 方法,而不是 size()==0 的方式

这是因为 isEmpty() 方法的可读性更好,并且时间复杂度为 O(1)。绝大部分我们使用的集合的 size() 方法的时间复杂度也是 O(1),不过,也有很多复杂度不是 O(1) 的,比如 java.util.concurrent 包下的某些集合(ConcurrentLinkedQueue、ConcurrentHashMap)。
以ConcurrentHashMap为例,我们可以看一下它底层关于size()与isEmpty()的实现

【源码解析1】

public int size() {
    long n = sumCount();
    return ((n < 0L) ? 0 :
            (n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE :
            (int)n);
}
final long sumCount() {
    CounterCell[] as = counterCells; CounterCell a;
    long sum = baseCount;
    if (as != null) {
        for (int i = 0; i < as.length; ++i) {
            if ((a = as[i]) != null)
                sum += a.value;
        }
    }
    return sum;
}
public boolean isEmpty() {
    return sumCount() <= 0L; // ignore transient negative values
}

集合去重

很多场景下,我们都要求数据的唯一性,也就是不可重复,所以集合的去重本领我们也要掌握,在《阿里巴巴 Java 开发手册》中这样说道:

可以利用 Set 元素唯一的特性,可以快速对一个集合进行去重操作,避免使用 List 的 contains()
进行遍历去重或者判断包含操作。

这是为什么呢?我们依旧需要透过源码去分析问题,分别选择HashSet和ArrayList,其实两者的差别主要体现在对contains()的实现上。

【HashSet去重核心】

private transient HashMap<E,Object> map;
public boolean contains(Object o) {
    return map.containsKey(o);
}

HashSet 的 contains() 方法底部依赖的 HashMap 的 containsKey() 方法,时间复杂度接近于 O(1)(没哈希冲突下)。

【ArrayList去重核心】

public boolean contains(Object o) {
    return indexOf(o) >= 0;
}
public int indexOf(Object o) {
    if (o == null) {
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

而对于ArrayList来说,它的contains是通过遍历元素实现,时间复杂度O(n),两者一比,高下立现!

集合遍历

集合元素的遍历,可以说是只要用集合,就无法避免的,之前写了一篇关于HashMap的遍历,还有一篇关于java中迭代器的文章,推荐大家去看看。
《HashMap的7种遍历方式》
《java中的迭代器实现原理》

不过对于集合遍历,在手册中有个额外的规约

不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator 方式,如果并发操作,需要对Iterator 对象加锁。

强行修改,会导致Iterator遍历出错,报ConcurrentModificationException异常。

集合转数组

对于集合转为数组的场景,《阿里巴巴 Java 开发手册》也给了要求,如下:

使用集合转数组的方法,必须使用集合的 toArray(T[] array),传入的是类型完全一致、长度为 0 的空数组。

【代码示例2】

String [] s= new String[]{
    "I Love", "JavaBuild"
};
List<String> list = Arrays.asList(s);
Collections.reverse(list);
//没有指定类型的话会报错
s=list.toArray(new String[0]);

注意:new String[0]就是起一个模板的作用,指定了返回数组的类型,0 是为了节省空间,因为它只是为了说明返回的类型。

集合转Map

集合除了会转为数组外,还可能会转为Map,所以,我们在转Map的时候,《阿里巴巴 Java 开发手册》也给了约束。

在使用 java.util.stream.Collectors 类的 toMap() 方法转为 Map 集合时,一定要注意当 value 为
null 时会抛 NPE 异常。

class Person {
    private String name;
    private String phoneNumber;
     // getters and setters
}
//test main()
List<Person> bookList = new ArrayList<>();
bookList.add(new Person("1","JavaBuild"));
bookList.add(new Person("2",null));
// 空指针异常
bookList.stream().collect(Collectors.toMap(Person::getName, Person::getPhoneNumber));

这是为啥呢,我们跟入toMap中发现,内部调用了Map的merge()方法,跟入这个方法后,我们会发现

【源码解析】

default V merge(K key, V value,
        BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
    Objects.requireNonNull(remappingFunction);
    Objects.requireNonNull(value);
    V oldValue = get(key);
    V newValue = (oldValue == null) ? value :
               remappingFunction.apply(oldValue, value);
    if(newValue == null) {
        remove(key);
    } else {
        put(key, newValue);
    }
    return newValue;
}

这源码里首先执行了 Objects.requireNonNull(remappingFunction);这一句代码,用来判断value值非空,并且做了抛出NPE处理。

总结

以上就是结合开发手册和自己平时开发经验,写的六点注意事项,希望所有小伙伴都能够在日后的开发工作中,保持良好的开发规范与习惯,强烈建议每个人必看《阿里巴巴 Java 开发手册》,这是很多互联网企业,新员工入职必看书籍,虽然里面有些内容,个人感觉有点矫枉过正,但90%以上的约定都非常必要!

结尾彩蛋

如果本篇博客对您有一定的帮助,大家记得留言+点赞+收藏呀。原创不易,转载请联系Build哥!

如果您想与Build哥的关系更近一步,还可以关注俺滴公众号“JavaBuild888”,在这里除了看到《Java成长计划》系列博文,还有提升工作效率的小笔记、读书心得、大厂面经、人生感悟等等,欢迎您的加入!

标签:面试官,应该,return,ArrayList,list,注意,集合,null,size
From: https://www.cnblogs.com/JavaBuild/p/18051111

相关文章

  • 什么是自注意力机制?
    自注意力机制(Self-AttentionMechanism)是一种在自然语言处理和计算机视觉等领域中广泛使用的技术,它可以帮助模型在处理序列数据时更好地理解上下文信息。在自注意力机制中,输入序列被表示为一组向量(比如说在自然语言处理中,可以将一句话中的每个单词表示为一个向量),每个向量都被称为......
  • 直播系统app源码,Android端与屏幕相关的几个注意事项
    直播系统app源码,Android端与屏幕相关的几个注意事项Android端的宽屏适配、禁止截屏和保持屏幕常亮,是直播系统app源码开发时需要注意的三个重要事项。宽屏适配越来越多的手机厂商趋向于全面屏设计,屏幕比例均超过过去常见的16:9比例。超大屏幕比例的设计对于AndroidAp......
  • PIMPL的优点和注意事项
    优点降低耦合度:使用PIMPL可以减少头文件的依赖,降低编译时的耦合度。隐藏实现细节:实现细节对使用者是不可见的,有利于封装。编译依赖减少:当实现改变时,不需要重新编译依赖于接口的代码,只需重新编译实现代码即可。注意事项:运行时性能:每次通过指针访问实现类可能会有轻微的性能......
  • 面试官上来就让手撕HashMap的7种遍历方式,当场愣住,最后只写出了3种
    写在开头今天有个小伙伴私信诉苦,说面试官上来就让他手撕HashMap的7种遍历方式,最终只写出3种常用的,怀疑面试官是在故意***难。这个问题大家怎么看?反正我个人感觉这肯定不是***难,“手撕遍历方式”算是一个比较简单的考验方式了,而且集合的遍历又是日常开发的必备!至于要一下写出7......
  • 自注意力和混合特征名词解释
    引言子带变换特征将原始信号分解成不同频带的子信号基本原理是利用一组带通滤波器将信号分割成若干个子频带也可采用离散小波变换,利用小波基函数将信号分解成不同尺度的子信号以便在不同的尺度上分析信号或图像的特性和池化(pooling)同对信号进行降采样可......
  • JAVA基础:方法的其他形式 方法使用需要注意的常见问题
                方法使用需要注意的常见问题 packagecom.itheima.Method;publicclassMethod2{publicstaticvoidprinthelloworld1(){System.out.println("helloworld!");}publicstaticvoidmain(String[]args){......
  • Vue Router系列之(三)几个注意点
    几个注意点路由组件通常存放在pages文件夹,一般组件通常存放在components文件夹。通过组件的使用方式不同将组件划分为不同的分类:路由组件和一般组件路由组件:靠路由规则匹配出来,由路由器帮我们渲染的组件【我们没有亲自写过这种组件标签】一般组件:我们亲自写的组件标签路由......
  • Vue 2x 系列之(二十)一些注意点
    一些注意点vue基础vue-cli:工程化开发vue-router:在Vue中实现前端路由vuex:应用足够复杂时,用于保管数据element-uivue3Angular==》React==》Vue生命周期函数中的this都是vm开发中自行向vm身上追加属性时【场景:比如methods中要访问mounted钩子中的变量】,避免追加敏感......
  • Flask使用装饰器注意点
    一装饰器,需要放在路由装饰器下面'''在执行视图函数之前,做判断--》路由的装饰器是控制路由匹配的--》需要先执行,所以登录认证装饰器,需要放在下面'''二需要直接指定路由别名原因'''直接添加会报错————每个路由,都会有个别名,如果不写,默认以函数名作为别名如果视图......
  • 面试官:如何设计一个高并发系统?
    面试题如何设计一个高并发系统?面试官心理剖析面试官对高并发系统的深入询问,实际上是对求职者专业技能和经验的精准检验。在许多公司发布的职位描述(JD)中,高并发经验被视作一项重要的加分项,这反映了当前互联网行业对高并发处理能力的极高要求。对于那些真正在互联网公司中负责过......