Java集合详解8:Java集合类细节精讲,细节决定成败

智能车八邻域图像算法 阅读详情

本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看

https://github.com/h2pl/Java-Tutorial

喜欢的话麻烦点下Star哈

文章首发于我的个人博客:

www.how2playlife.com

本文是微信公众号【Java技术江湖】的《走进JavaWeb技术世界》其中一篇,本文部分内容来源于网络,为了把本文主题讲得清晰透彻,也整合了很多我认为不错的技术博客内容,引用其中了一些比较好的博客文章,如有侵权,请联系作者。

该系列博文会告诉你如何从入门到进阶,从servlet到框架,从ssm再到SpringBoot,一步步地学习JavaWeb基础知识,并上手进行实战,接着了解JavaWeb项目中经常要使用的技术和组件,包括日志组件、Maven、Junit,等等内容,以便让你更完整地了解整个JavaWeb技术体系,形成自己的知识框架。为了更好地总结和检验你的学习成果,本系列文章也会提供每个知识点对应的面试题以及参考答案。

如果对本系列文章有什么建议,或者是有什么疑问的话,也可以关注公众号【Java技术江湖】联系作者,欢迎你参与本系列博文的创作和修订。

文末赠送8000G的Java架构师学习资料,需要的朋友可以到文末了解领取方式,资料包括Java基础、进阶、项目和架构师等免费学习资料,更有数据库、分布式、微服务等热门技术学习视频,内容丰富,兼顾原理和实践,另外也将赠送作者原创的Java学习指南、Java程序员面试指南等干货资源)

今天我们来探索一下Java集合类中的一些技术细节。主要是对一些比较容易被遗漏和误解的知识点做一些讲解和补充。可能不全面,还请谅解。

初始容量

集合是我们在Java编程中使用非常广泛的,它就像大海,海纳百川,像万能容器,盛装万物,而且这个大海,万能容器还可以无限变大(如果条件允许)。当这个海、容器的量变得非常大的时候,它的初始容量就会显得很重要了,因为挖海、扩容是需要消耗大量的人力物力财力的。

同样的道理,Collection的初始容量也显得异常重要。所以:对于已知的情景,请为集合指定初始容量。

    public static void main(String[] args) {
        StudentVO student = null;
        long begin1 = System.currentTimeMillis();
        List<StudentVO> list1 = new ArrayList<>();
        for(int i = 0 ; i < 1000000; i  ){
            student = new StudentVO(i,"chenssy_" i,i);
            list1.add(student);
        }
        long end1 = System.currentTimeMillis();
        System.out.println("list1 time:"   (end1 - begin1));
        long begin2 = System.currentTimeMillis();
        List<StudentVO> list2 = new ArrayList<>(1000000);
        for(int i = 0 ; i < 1000000; i  ){
            student = new StudentVO(i,"chenssy_" i,i);
            list2.add(student);
        }
        long end2 = System.currentTimeMillis();
        System.out.println("list2 time:"   (end2 - begin2));
    }
上面代码两个list都是插入1000000条数据,只不过list1没有没有申请初始化容量,而list2初始化容量1000000。那运行结果如下:
    list1 time:1638
    list2 time:921

从上面的运行结果我们可以看出list2的速度是list1的两倍左右。在前面LZ就提过,ArrayList的扩容机制是比较消耗资源的。我们先看ArrayList的add方法:

    public boolean add(E e) {  
            ensureCapacity(size   1);   
            elementData[size  ] = e;  
            return true;  
        }  
    public void ensureCapacity(int minCapacity) {  
        modCount  ;         //修改计数器
        int oldCapacity = elementData.length;    
        //当前需要的长度超过了数组长度,进行扩容处理
        if (minCapacity > oldCapacity) {  
            Object oldData[] = elementData;  
            //新的容量 = 旧容量 * 1.5   1
            int newCapacity = (oldCapacity * 3)/2   1;  
                if (newCapacity < minCapacity)  
                    newCapacity = minCapacity;  
          //数组拷贝,生成新的数组 
          elementData = Arrays.copyOf(elementData, newCapacity);  
        }  
    }
ArrayList每次新增一个元素,就会检测ArrayList的当前容量是否已经到达临界点,如果到达临界点则会扩容1.5倍。然而ArrayList的扩容以及数组的拷贝生成新的数组是相当耗资源的。所以若我们事先已知集合的使用场景,知道集合的大概范围,我们最好是指定初始化容量,这样对资源的利用会更加好,尤其是大数据量的前提下,效率的提升和资源的利用会显得更加具有优势。

asList的缺陷

在实际开发过程中我们经常使用asList讲数组转换为List,这个方法使用起来非常方便,但是asList方法存在几个缺陷:

避免使用基本数据类型数组转换为列表

使用8个基本类型数组转换为列表时会存在一个比较有味的缺陷。先看如下程序:

    public static void main(String[] args) {
            int[] ints = {1,2,3,4,5};
            List list = Arrays.asList(ints);
            System.out.println("list'size:"   list.size());
        }
    ------------------------------------
    outPut:
    list'size:1
程序的运行结果并没有像我们预期的那样是5而是逆天的1,这是什么情况?先看源码:
    public static <T> List<T> asList(T... a) {
            return new ArrayList<>(a);
        }

asList接受的参数是一个泛型的变长参数,我们知道基本数据类型是无法发型化的,也就是说8个基本类型是无法作为asList的参数的, 要想作为泛型参数就必须使用其所对应的包装类型。但是这个这个实例中为什么没有出错呢?

因为该实例是将int类型的数组当做其参数,而在Java中数组是一个对象,它是可以泛型化的。所以该例子是不会产生错误的。既然例子是将整个int类型的数组当做泛型参数,那么经过asList转换就只有一个int 的列表了。如下:

    public static void main(String[] args) {
        int[] ints = {1,2,3,4,5};
        List list = Arrays.asList(ints);
        System.out.println("list 的类型:"   list.get(0).getClass());
        System.out.println("list.get(0) == ints:"   list.get(0).equals(ints));
    }
--------------------------------------------
outPut:
list 的类型:class [I
list.get(0) == ints:true
从这个运行结果我们可以充分证明list里面的元素就是int数组。弄清楚这点了,那么修改方法也就一目了然了:将int 改变为Integer。
    public static void main(String[] args) {
            Integer[] ints = {1,2,3,4,5};
            List list = Arrays.asList(ints);
            System.out.println("list'size:"   list.size());
            System.out.println("list.get(0) 的类型:"   list.get(0).getClass());
            System.out.println("list.get(0) == ints[0]:"   list.get(0).equals(ints[0]));
        }
    ----------------------------------------
    outPut:
    list'size:5
    list.get(0) 的类型:class java.lang.Integer
    list.get(0) == ints[0]:true

asList产生的列表不可操作

对于上面的实例我们再做一个小小的修改:

    public static void main(String[] args) {
            Integer[] ints = {1,2,3,4,5};
            List list = Arrays.asList(ints);
            list.add(6);
        }

该实例就是讲ints通过asList转换为list 类别,然后再通过add方法加一个元素,这个实例简单的不能再简单了,但是运行结果呢?打出我们所料:

    Exception in thread "main" java.lang.UnsupportedOperationException
        at java.util.AbstractList.add(Unknown Source)
        at java.util.AbstractList.add(Unknown Source)
        at com.chenssy.test.arrayList.AsListTest.main(AsListTest.java:10)

运行结果尽然抛出UnsupportedOperationException异常,该异常表示list不支持add方法。这就让我们郁闷了,list怎么可能不支持add方法呢?难道jdk脑袋堵塞了?我们再看asList的源码:

    public static <T> List<T> asList(T... a) {
            return new ArrayList<>(a);
        }
asList接受参数后,直接new 一个ArrayList,到这里看应该是没有错误的啊?别急,再往下看:
    private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable{
            private static final long serialVersionUID = -2764017481108945198L;
            private final E[] a;
            ArrayList(E[] array) {
                if (array==null)
                    throw new NullPointerException();
                a = array;
            }
            //.................
        }
这是ArrayList的源码,从这里我们可以看出,此ArrayList不是java.util.ArrayList,他是Arrays的内部类。

该内部类提供了size、toArray、get、set、indexOf、contains方法,而像add、remove等改变list结果的方法从AbstractList父类继承过来,同时这些方法也比较奇葩,它直接抛出UnsupportedOperationException异常:

    public boolean add(E e) {
            add(size(), e);
            return true;
        }
        public E set(int index, E element) {
            throw new UnsupportedOperationException();
        }
        public void add(int index, E element) {
            throw new UnsupportedOperationException();
        }
        public E remove(int index) {
            throw new UnsupportedOperationException();
        }

通过这些代码可以看出asList返回的列表只不过是一个披着list的外衣,它并没有list的基本特性(变长)。该list是一个长度不可变的列表,传入参数的数组有多长,其返回的列表就只能是多长。所以::不要试图改变asList返回的列表,否则你会自食苦果。

subList的缺陷

我们经常使用subString方法来对String对象进行分割处理,同时我们也可以使用subList、subMap、subSet来对List、Map、Set进行分割处理,但是这个分割存在某些瑕疵。

subList返回仅仅只是一个视图

首先我们先看如下实例:

public static void main(String[] args) {List list1 = new ArrayList();list1.add(1);list1.add(2);

        //通过构造函数新建一个包含list1的列表 list2
        List<Integer> list2 = new ArrayList<Integer>(list1);
        //通过subList生成一个与list1一样的列表 list3
        List<Integer> list3 = list1.subList(0, list1.size());
        //修改list3
        list3.add(3);
        System.out.println("list1 == list2:"   list1.equals(list2));
        System.out.println("list1 == list3:"   list1.equals(list3));
    }

这个例子非常简单,无非就是通过构造函数、subList重新生成一个与list1一样的list,然后修改list3,最后比较list1 == list2?、list1 == list3?。

按照我们常规的思路应该是这样的:因为list3通过add新增了一个元素,那么它肯定与list1不等,而list2是通过list1构造出来的,所以应该相等,所以结果应该是:

    list1 == list2:true
    list1 == list3: false
首先我们先不论结果的正确与否,我们先看subList的源码:
    public List<E> subList(int fromIndex, int toIndex) {
            subListRangeCheck(fromIndex, toIndex, size);
            return new SubList(this, 0, fromIndex, toIndex);
    }

subListRangeCheck方式是判断fromIndex、toIndex是否合法,如果合法就直接返回一个subList对象,注意在产生该new该对象的时候传递了一个参数 this ,该参数非常重要,因为他代表着原始list。

/**

  • 继承AbstractList类,实现RandomAccess接口
    */
    private class SubList extends AbstractList implements RandomAccess {
    private final AbstractList parent; //列表
    private final int parentOffset;
    private final int offset;
    int size;
        //构造函数
        SubList(AbstractList<E> parent,
                int offset, int fromIndex, int toIndex) {
            this.parent = parent;
            this.parentOffset = fromIndex;
            this.offset = offset   fromIndex;
            this.size = toIndex - fromIndex;
            this.modCount = ArrayList.this.modCount;
        }
        //set方法
        public E set(int index, E e) {
            rangeCheck(index);
            checkForComodification();
            E oldValue = ArrayList.this.elementData(offset   index);
            ArrayList.this.elementData[offset   index] = e;
            return oldValue;
        }
        //get方法
        public E get(int index) {
            rangeCheck(index);
            checkForComodification();
            return ArrayList.this.elementData(offset   index);
        }
        //add方法
        public void add(int index, E e) {
            rangeCheckForAdd(index);
            checkForComodification();
            parent.add(parentOffset   index, e);
            this.modCount = parent.modCount;
            this.size  ;
        }
        //remove方法
        public E remove(int index) {
            rangeCheck(index);
            checkForComodification();
            E result = parent.remove(parentOffset   index);
            this.modCount = parent.modCount;
            this.size--;
            return result;
        }
    }

该SubLsit是ArrayList的内部类,它与ArrayList一样,都是继承AbstractList和实现RandomAccess接口。同时也提供了get、set、add、remove等list常用的方法。但是它的构造函数有点特殊,在该构造函数中有两个地方需要注意:

1、this.parent = parent;而parent就是在前面传递过来的list,也就是说this.parent就是原始list的引用。

2、this.offset = offset fromIndex;this.parentOffset = fromIndex;。同时在构造函数中它甚至将modCount(fail-fast机制)传递过来了。

我们再看get方法,在get方法中return ArrayList.this.elementData(offset index);

这段代码可以清晰表明get所返回就是原列表offset index位置的元素。同样的道理还有add方法里面的:

parent.add(parentOffset index, e);this.modCount = parent.modCount;remove方法里面的

E result = parent.remove(parentOffset index);this.modCount = parent.modCount;

诚然,到了这里我们可以判断subList返回的SubList同样也是AbstractList的子类,同时它的方法如get、set、add、remove等都是在原列表上面做操作,它并没有像subString一样生成一个新的对象。

所以subList返回的只是原列表的一个视图,它所有的操作最终都会作用在原列表上。

那么从这里的分析我们可以得出上面的结果应该恰恰与我们上面的答案相反:

list1 == list2:falselist1 == list3:true

subList生成子列表后,不要试图去操作原列表

从上面我们知道subList生成的子列表只是原列表的一个视图而已,如果我们操作子列表它产生的作用都会在原列表上面表现,但是如果我们操作原列表会产生什么情况呢?

public static void main(String[] args) {List list1 = new ArrayList();list1.add(1);list1.add(2);

        //通过subList生成一个与list1一样的列表 list3
        List<Integer> list3 = list1.subList(0, list1.size());
        //修改list1
        list1.add(3);
        System.out.println("list1'size:"   list1.size());
        System.out.println("list3'size:"   list3.size());
    }
该实例如果不产生意外,那么他们两个list的大小都应该都是3,但是偏偏事与愿违,事实上我们得到的结果是这样的:
    list1'size:3
    Exception in thread "main" java.util.ConcurrentModificationException
        at java.util.ArrayList$SubList.checkForComodification(Unknown Source)
        at java.util.ArrayList$SubList.size(Unknown Source)
        at com.chenssy.test.arrayList.SubListTest.main(SubListTest.java:17)
list1正常输出,但是list3就抛出ConcurrentModificationException异常,看过我另一篇博客的同仁肯定对这个异常非常,fail-fast?不错就是fail-fast机制,在fail-fast机制中,LZ花了很多力气来讲述这个异常,所以这里LZ就不对这个异常多讲了。我们再看size方法:
    public int size() {
                checkForComodification();
                return this.size;
            }
size方法首先会通过checkForComodification验证,然后再返回this.size。
    private void checkForComodification() {
                if (ArrayList.this.modCount != this.modCount)
                    throw new ConcurrentModificationException();
            }
该方法表明当原列表的modCount与this.modCount不相等时就会抛出ConcurrentModificationException。

同时我们知道modCount 在new的过程中 "继承"了原列表modCount,只有在修改该列表(子列表)时才会修改该值(先表现在原列表后作用于子列表)。

而在该实例中我们是操作原列表,原列表的modCount当然不会反应在子列表的modCount上啦,所以才会抛出该异常。

对于子列表视图,它是动态生成的,生成之后就不要操作原列表了,否则必然都导致视图的不稳定而抛出异常。最好的办法就是将原列表设置为只读状态,要操作就操作子列表:

//通过subList生成一个与list1一样的列表 list3

    List<Integer> list3 = list1.subList(0, list1.size());

//对list1设置为只读状态

    list1 = Collections.unmodifiableList(list1);

推荐使用subList处理局部列表

在开发过程中我们一定会遇到这样一个问题:获取一堆数据后,需要删除某段数据。例如,有一个列表存在1000条记录,我们需要删除100-200位置处的数据,可能我们会这样处理:

    for(int i = 0 ; i < list1.size() ; i  ){
       if(i >= 100 && i <= 200){
           list1.remove(i);
           /*
            * 当然这段代码存在问题,list remove之后后面的元素会填充上来,
             * 所以需要对i进行简单的处理,当然这个不是这里讨论的问题。
             */
       }
    }
这个应该是我们大部分人的处理方式吧,其实还有更好的方法,利用subList。在前面LZ已经讲过,子列表的操作都会反映在原列表上。所以下面一行代码全部搞定:
    list1.subList(100, 200).clear();
简单而不失华丽!!!!!

保持compareTo和equals同步

在Java中我们常使用Comparable接口来实现排序,其中compareTo是实现该接口方法。我们知道compareTo返回0表示两个对象相等,返回正数表示大于,返回负数表示小于。同时我们也知道equals也可以判断两个对象是否相等,那么他们两者之间是否存在关联关系呢?

    public class Student implements Comparable<Student>{
        private String id;
        private String name;
        private int age;
        public Student(String id,String name,int age){
            this.id = id;
            this.name = name;
            this.age = age;
        }
        public boolean equals(Object obj){
            if(obj == null){
                return false;
            }
            if(this == obj){
                return true;
            }
            if(obj.getClass() != this.getClass()){
                return false;
            }
            Student student = (Student)obj;
            if(!student.getName().equals(getName())){
                return false;
            }
            return true;
        }
        public int compareTo(Student student) {
            return this.age - student.age;
        }
        /** 省略getter、setter方法 */
    }
Student类实现Comparable接口和实现equals方法,其中compareTo是根据age来比对的,equals是根据name来比对的。
    public static void main(String[] args){
            List<Student> list = new ArrayList<>();
            list.add(new Student("1", "chenssy1", 24));
            list.add(new Student("2", "chenssy1", 26));
            Collections.sort(list);   //排序
            Student student = new Student("2", "chenssy1", 26);
            //检索student在list中的位置
            int index1 = list.indexOf(student);
            int index2 = Collections.binarySearch(list, student);
            System.out.println("index1 = "   index1);
            System.out.println("index2 = "   index2);
        }

按照常规思路来说应该两者index是一致的,因为他们检索的是同一个对象,但是非常遗憾,其运行结果:

index1 = 0index2 = 1

为什么会产生这样不同的结果呢?这是因为indexOf和binarySearch的实现机制不同。

indexOf是基于equals来实现的只要equals返回TRUE就认为已经找到了相同的元素。

而binarySearch是基于compareTo方法的,当compareTo返回0 时就认为已经找到了该元素。

在我们实现的Student类中我们覆写了compareTo和equals方法,但是我们的compareTo、equals的比较依据不同,一个是基于age、一个是基于name。

比较依据不同那么得到的结果很有可能会不同。所以知道了原因,我们就好修改了:将两者之间的比较依据保持一致即可。

对于compareTo和equals两个方法我们可以总结为:compareTo是判断元素在排序中的位置是否相等,equals是判断元素是否相等,既然一个决定排序位置,一个决定相等,所以我们非常有必要确保当排序位置相同时,其equals也应该相等。

使其相等的方式就是两者应该依附于相同的条件。当compareto相等时equals也应该相等,而compareto不相等时equals不应该相等,并且compareto依据某些属性来决定排序。

参考文章

https://www.cnblogs.com/galibujianbusana/p/6600226.html

http://blog.itpub.net/69906029/viewspace-2641300/

https://www.cnblogs.com/itxiaok/p/10356553.html

微信公众号

个人公众号:程序员黄小斜

​黄小斜是 985 硕士,阿里巴巴Java工程师,在自学编程、技术求职、Java学习等方面有丰富经验和独到见解,希望帮助到更多想要从事互联网行业的程序员们。​作者专注于 JAVA 后端技术栈,热衷于分享程序员干货、学习经验、求职心得,以及自学编程和Java技术栈的相关干货。​黄小斜是一个斜杠青年,坚持学习和写作,相信终身学习的力量,希望和更多的程序员交朋友,一起进步和成长!

原创电子书:关注微信公众号【程序员黄小斜】后回复【原创电子书】即可领取我原创的电子书《菜鸟程序员修炼手册:从技术小白到阿里巴巴Java工程师》这份电子书总结了我2年的Java学习之路,包括学习方法、技术总结、求职经验和面试技巧等内容,已经帮助很多的程序员拿到了心仪的offer!

程序员3T技术学习资源: 一些程序员学习技术的资源大礼包,关注公众号后,后台回复关键字 “资料” 即可免费无套路获取,包括Java、python、C 、大数据、机器学习、前端、移动端等方向的技术资料。

技术公众号:Java技术江湖

如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的微信公众号【Java技术江湖】

这是一位阿里 Java 工程师的技术小站。作者黄小斜,专注 Java 相关技术:SSM、SpringBoot、MySQL、分布式、中间件、集群、Linux、网络、多线程,偶尔讲点Docker、ELK,同时也分享技术干货和学习经验,致力于Java全栈开发!

Java工程师必备学习资源:关注公众号后回复”Java“即可领取 Java基础、进阶、项目和架构师等免费学习资料,更有数据库、分布式、微服务等热门技术学习视频,内容丰富,兼顾原理和实践,另外也将赠送作者原创的Java学习指南、Java程序员面试指南等干货资源

我的公众号

​

用Zotero+B站Kimi,我如何把一篇顶会论文快速做成组会PPT(含完整提示词) 本文介绍如何利用Zotero和Kimi快速将顶会论文转化为组会PPT,提升学术汇报效率。通过Zotero的文献管理插件和Kimi的AI提炼技术,实现从论文阅读到PPT生成的全流程自动化,帮助研究者在90分钟内完成高质量汇报准备。 阅读详情

相关推荐

Cadence原理图设计10个高效技巧:从封装绑定到BOM表导出全流程

本文为硬件工程师提供了一套Cadence OrCAD Capture CIS原理图设计的高效实战指南。文章深度解析了从封装库的精准管理与CIS数据库绑定、封装批量处理技巧,到设计缓存清理、跨页信号追踪,再到关键的DRC检查、网表导出以及智能BOM报表生成的全流程核心技巧,旨在帮助工程师提升设计效率与可靠性,规避常见错误。

weixin_29178069的博客 201

JAVA 中List等通用类对equals的重写

一、背景 在java中的equals和==比较规则: " == "在基本数据类型:比较值内容 引用类型时:比较地址 equals 重写:比较值内容 , equals不重写:比较地址 那么Java中的一些常用类型,对equals是否进行重写,如果进行了重写,则实现了怎么的逻辑? 二、equals函数重写 2.1 String String作为引用类型,==直...

王文萱的博客 1200

Flowable高级篇 -6.8.0 最全表结构说明-主要表结构字段注释

flowable 28张表 详细

billzhen的博客 1万+

Java8 Stream集合的筛选、归约、分组、聚合详解+案例

先贴上几个案例,水平高超的同学可以挑战一下:用传统的迭代处理也不是很难,但代码就显得冗余了,跟Stream相比高下立判。Java 8 是一个非常成功的版本,这个版本新增的,配合同版本出现的 ,给我们操作集合(Collection)提供了极大的便利。那么什么是?可以由数组或集合创建,对流的操作分为两种:另外,有几个特性:可以通过集合数组创建。1、通过 方法用集合创建流 2、使用方法用数组创建流 3、使用的静态方法: 输出结果:和的简单区分: 是顺序流,由主线程按顺序对流执行操作,而是并行流,内部以多线程并

qq_43842093的博客 2273

perftest实战:从零到一,精准评估RDMA网络性能

想象一下,两台服务器传输数据就像在同一个主板上交换内存,这种零拷贝、低延迟的特性,正是高性能计算、分布式存储等场景梦寐以求的。对于云环境用户,主流云厂商现在都提供RDMA实例(比如AWS的EFA、阿里云的eRDMA)。但要注意,不同机型支持的RDMA协议版本可能不同,这会直接影响测试结果的上限值。记得记录每次测试的BW average值,绘制成曲线图后,你可能会发现类似"阶梯状"的性能变化,这通常与网卡的DMA引擎设计有关。在云环境中,由于虚拟化层的影响,延迟会比裸金属高20-30%。

weixin_28700411的博客 211

PX4/ArduPilot源码克隆提速秘籍:国内镜像与子模块优化全攻略

本文详细介绍了PX4/ArduPilot源码克隆的提速秘籍,包括使用国内镜像源和子模块优化技巧。通过阿木实验室、华为云等国内镜像,克隆速度可提升20倍,同时提供子模块替换和并行编译等实用方法,大幅提升开发效率。

weixin_29251567的博客 523

jdk8集合类汇总介绍

JDK1.8数据存储容器实现类介绍 编辑       这篇主要介绍jdk1.8的一些容器实现类(集合+映射(map))的作用和线程安全与否以及实现线程安全的方式,因为jdk提供的集合类挺多的,所以篇幅有些长,大家可以跳常用的几个看如ArrayList、HashMap、ConcurrentHashMap等。 特殊词汇说明: 1)cas操作:Compare and Swap或者Co

码农的世界你不懂 9858

MATLAB R2022b 安装教程

1. 下载安装先放资源链接,百度网盘太贵了不想续了,换了夸克网盘。1. 下载安装。

qq_51654158的博客 2万+

Java集合详解8:Java集合类细节精讲

今天我们来探索一下Java集合类中的一些技术细节。主要是对一些比较容易被遗漏和误解的知识点做一些讲解和补充。可能不全面,还请谅解。 本文参考:http://cmsblogs.com/?cat=5 具体代码在我的GitHub中可以找到 https://github.com/h2pl/MyTech 文章首发于我的个人博客: https://h2pl.github.io/2018/05/13/...

Java技术江湖 6411

【FnOS飞牛云 | NAS存储】旧电脑不吃灰,手把手教你搭建完全属于自己的私人网盘

打造属于自己的NAS私人网盘,亲测有效,值得一试

木子Linux的博客世界 9413

GFPS技术原理(一)-简介

GFPS(谷歌快速配对服务)利用BLE技术简化设备配对流程,相比传统方式大幅提升用户体验。首次配对时,设备广播包含型号ID和安全密钥的BLE信号,手机检测后弹出配对通知,用户点击即可完成安全连接并将设备与Google账户绑定。该技术支持跨设备同步、电量显示、找回设备等功能,后续配对更加便捷。GFPS通过BLE实现快速发现、一键配对和安全连接,结合Google后端服务提供元数据支持,已成为出海设备的认证标准。其核心优势在于简化操作步骤并增强功能性,特别适合非技术用户群体。

十多年蓝牙协议软件开发,擅长蓝牙协议分析 2061

深入学习java8集合(一):ArrayList的实现原理

1. ArrayList简介   ArrayList是List接口的可变数组的实现。实现了所有可选列表操作,并允许包括 null 在内的所有元素。除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。      每个ArrayList实例都有一个容量,该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向ArrayList中不断添加元素,其容量也...

练好本领,报国杀敌 1467

【WLAN】【基础知识】WIFI那些事儿之信道和频谱(涵盖2.4G、5G、6G,全网最全)

- 从事WIFI相关开发或测试工作,经常要和信道、频宽、频率等打交道,本文仅将平时涉及的2.4G、5G、6G的信道、频宽、频率等做一总结。 - 一图胜万言,本文汇总了工作中经常用到的频谱图,保证全网最全!!! - 本博客地址,https://blog.youkuaiyun.com/qxhgd,欢迎各位关注,转发请注明出处。

一介草民,只为生活。 1万+

论细节决定成败

说明近期,工作中、工作外、个人、他人均遇到了不少问题,而这些问题的成因均因未注意细节而造成,使我再一次想起那句名言:细节决定成败。于是我觉得很有必要做一个记录,用以自警和他警。事件一:一个数据库预留字段造成的上线失败这个事其实是比较严重的一个事,因为涉及到了生产,并严重影响甲方公司对我方的评价。 整个经过大概是这样: 甲方公司给我们公司提出了一个需求,这个需求是六月份开始建立工单的,而实际开发应

程序员涂小哥的技术博客 2218

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

给准备跳槽的程序员Java面试秘籍!细节决定成败!

给准备跳槽的程序员Java面试秘籍!细节决定成败! 最近看到很多小伙伴都在因为面试烦恼,小编就将自己的一些面试建议分享出来,希望能帮助到大家。 简历 首先简历要写好,因为简历直接决定你是否能够获得一个面试机会。而且一份好的简历呢,也能给面试官留下一个非常好的第一印象。 那一份最优质的简历要注意哪些呢? 1.1个人信息 除了个人姓名、籍贯、现住地址等外,还需加上毕业学校、所学专业等重要的信息,无关的信息,例如身高、体重之类的,则少写或不写。 还需要注意的是你的电子邮箱,建议你用Gmail,不要用QQ邮箱,这

weixin_47955802的博客 2771

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

细节决定成败(一)

ScheduledThreadPool 执行周期性任务,假设某次出现了异常,周期任务还会继续吗? 查看ScheduledExecutorService接口里的scheduleWithFixedDelay方法的jdk文档,有描述如下: If any execution of the task encounters an exception, subsequent executions are suppressed. 如果在任务的执行中遇到异常,后续执行被取消。 ScheduledThreadPoolExecu

qq_41257365的博客 1022

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

细节决定成败,java面试必问底层

小编利用空余时间整理了一份《MySQL性能调优手册》,初衷也很简单,就是希望能够帮助到大家,减轻大家的负担和节省时间。关于这个,给大家看一份学习大纲(PDF)文件,每一个分支里面会有详细的介绍。这里都是以图片形式展示介绍,如要下载原文件以及更多的性能调优笔记(MySQL+Tomcat+JVM)!一份学习大纲(PDF)文件,每一个分支里面会有详细的介绍。[外链图片转存中…(img-EqXgV7Wa-1711094237559)]

weixin_42179304的博客 824

Java判等问题:细节决定成败

Java判等问题:细节决定成败 判等问题,在我们代码中就是一句话的事情,但是这一行代码如果处理不好,不仅会出现致命的bug,下面我们就以Java中 equals、compareTo 和 Java 的数值缓存、字符串驻留等问题展开讨论 1. 注意 equals 和 == 的区别 在业务代码中,我们通常使用 equals 或 == 进行判等操作。equals是方法而 ==是操作符 1.对基本类型,比如 int 、long、进行判断,只能使用 == ,比较对是直接值,因为基本类型对值就是其数值 2.对引用

浪人与酒 690

细节决定成败,华为java面试面经

我还通过一些渠道整理了一些大厂真实面试主要有:蚂蚁金服、拼多多、阿里云、百度、唯品会、携程、丰巢科技、乐信、软通动力、OPPO、银盛支付、中国平安等初,中级,高级Java面试题集合,附带超详细答案,希望能帮助到大家。还有专门针对JVM、SPringBoot、SpringCloud、数据库、Linux、缓存、消息中间件、源码等相关面试题。还有专门针对JVM、SPringBoot、SpringCloud、数据库、Linux、缓存、消息中间件、源码等相关面试题。

bq520111的博客 818

【Java】多线程并发基础——细节决定成败

多线程:解决多部分代码同时执行的需求。

学习只为当小狗狗,学习只为当小狗狗,学习只为当小狗狗,学习只为当小狗狗,学习只为当小狗狗,学习只为当小狗狗,学习只为当小狗狗 2174

判断“存在“你还在用count吗,细节决定成败

文章目录1. 为什么要用count(*)2. 优化方案 1. 为什么要用count(*) 在我们实际开发中,业务 往往是 如:(判断该手机是否已经存在),我们为了提高程序的运行效率,这个时候都会使用 select count(*) from sys_user where mobile = '18217692100' //而我们Java代码 Integer count = mapper.selectCount(String mobile); //写业务代码 if(count != 0){ …… el

浪人与酒 1379

细节决定成败

细节决定成败

无名 396

面试八股之集合篇8.1——面试真实场景——Java常见的集合类

🔥本篇概览:详细讲解了Java常见的集合类。

做一个善良的人,做好自己的事,如果愿意,再发一份光。 654

商务礼仪培训,细节决定成败

所谓细节决定成败,商务礼仪,是每一位求职者进入职场的必修课。1国储天府物理&amp;码教授商学院商务礼仪培训合作码教授职业发展学院,针对商务礼仪职场需求,专门开发此课程,解决求职者以及企业人员的商务礼仪困境。码教授集团市场总监白小燕,同时作为集团创意总监、金牌讲师,亲自面授商务礼仪精髓。四川国储天府物流有限公司,遵循“青年兴则国家兴,青年强则国家强。青年一代有理想、有本领、有担当,国家有前途,民族...

li_173的博客 782

用心做软件—细节决定成败

软件是什么?也许在编程者的眼中这是自己智慧的结晶,是技术运用的成果。但是在用户的眼中呢,用户会在乎你到底用了多少高级的技术、用了什么前卫的技术吗?我想大部分用户是不会管的,无论你是C#做的,Java做的,C++还是C做的,你的系统是Windows还是Linux,android还是塞班。用户的眼中你的软件只是一件产品,那么既然是产品,就要有价值,要能为用户带来方便,能为用户解决问题。当今的互联网上,

杨友山 2096

面试应该怎样应对!细节决定成败

自我介绍 一般人的自我介绍都太过简单,基本上说的都是简历上有的东西。其实企业最希望知道的是是否能胜任这项工作,和一些与这项工作有关的实习或者工作经验,包括一些个人技能,最深入研究的知识领域,也可以是个性中最积极的部分,主要的成就等。要突出积极地个性和做事的能力,说的合情合理才会让面试官信服。千万不要说的天花乱坠,从小学的事说到大学追的妞,面试官没时间听你在这里扯淡。企业也非常重视一个人的礼貌修养,在每次回答完毕后,都要说一句谢谢。 薪资要求 要是你对薪资的要求太低会让面试官们觉得你能力不够,对薪资的要求太.

weixin_49099594的博客 612
上一篇: Java集合详解7:一文搞清楚HashSet,TreeSet与LinkedHashSet的异同
下一篇: Java并发指南1:并发基础与Java多线程
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值