编写高质量代码:改善Java程序的151个建议(第5章:数组和集合___建议67~82)

有些东西,当你即将失去了的时候,你才懂得它的弥足珍贵,也许亡羊补牢,为时未晚!html

建议67:不一样的列表选择不一样的遍历算法算法

建议68:频繁插入和删除时使用LinkedList数组

建议69:列表相等只关心元素数据缓存

建议70:子列表只是原列表的一个视图安全

建议71:推荐使用subList处理局部列表多线程

建议72:生成子列表后不要再操做原列表性能

建议73:使用Comparator进行排序测试

建议74:不推荐使用binarySearch对列表进行检索大数据

建议75:集合中的元素必须作到compareTo和equals同步加密

建议76:集合运算的并集、交集、差集

建议77:使用shuffle打乱列表

建议78:减小hashmap中元素的数量

建议79:集合中哈希码不要重复

建议80:多线程使用Vector或HashTable

建议81:非稳定排序推荐使用List

建议82:由点及面,集合你们族总结

建议67:不一样的列表选择不一样的遍历算法

测试来看简单for循环比foreach能快那么一丢丢。

建议68:频繁插入和删除时使用LinkedList

ArrayList在进行插入元素时:

public void add(int index, E element) {
    //检查下标是否越界
    rangeCheckForAdd(index);
    //若须要扩容,则增大底层数组的长度
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    //给index下标以后的元素(包括当前元素)的下标加1,空出index位置
    System.arraycopy(elementData, index, elementData, index + 1,size - index);
    //赋值index位置元素
    elementData[index] = element;
    //列表长度加1
    size++;
}

注意看arrayCopy方法,只要插入一个元素,其后的元素就会向后移动一位,虽然arrayCopy是一个本地方法,效率很是高,但频繁的插入,每次后面的元素都要拷贝一遍,效率变的就低了。而使用LinkedList就显得更好了,LinkedList是一个双向列表,它的插入只是修改了相邻元素的next和previous引用。

原理不说了。

修改元素:LinkedList不如ArrayList,由于LinkedList是按顺序存储的,所以定位元素必然是一个遍历过程,效率大打折扣。而ArrayList的修改动做是数组元素的直接替换,简单高效。

LinkedList:删除和插入效率高;ArrayList:修改元素效率高。

建议69:列表相等只关心元素数据

判断集合是否相等只须关注元素是否相等便可,不用看容器

建议70:子列表只是原列表的一个视图

List接口提供了subList方法,其做用是返回一个列表的子列表,这与String类subSting有点相似。

注意:subList产生的列表只是一个视图,全部的修改动做直接做用于原列表。

建议71:推荐使用subList处理局部列表

咱们来看这样一个简单的需求:一个列表有100个元素,如今要删除索引位置为20~30的元素。这很简单,一个遍历很快就能够完成,代码以下:

public class Client71 {
    public static void main(String[] args) {
        // 初始化一个固定长度,不可变列表
        List<Integer> initData = Collections.nCopies(100, 0);
        // 转换为可变列表
        List<Integer> list = new ArrayList<Integer>(initData);
        // 遍历,删除符合条件的元素
        for (int i = 0; i < list.size(); i++) {
            if (i >= 20 && i < 30) {
                list.remove(i);
            }
        }
    }
}

这段代码很符合个人风格!

下面用subList解决这个问题:

public static void main(String[] args) {
        // 初始化一个固定长度,不可变列表
        List<Integer> initData = Collections.nCopies(100, 0);
        // 转换为可变列表
        List<Integer> list = new ArrayList<Integer>(initData);
        //删除指定范围内的元素
        list.subList(20, 30).clear();
    }

建议72:生成子列表后不要再操做原列表

注意:subList生成子列表后,保持原列表的只读状态。

建议73:使用Comparator进行排序

一、默认排序

Collections.sort(list)

二、按某字段排序

Collections.sort(list,new PositionComparator())

知道这些就好了,原文写的太晦涩了,我感受没啥用!

想了解更多的使用Comparator进行排序

建议74:不推荐使用binarySearch对列表进行检索

不推荐,干脆就不要写了,好吗?

对列表进行检索就使用indexOf就挺好的!

public class Client74 {
    public static void main(String[] args) {
        List<String> cities = new ArrayList<String> ();
        cities.add("上海");
        cities.add("广州");
        cities.add("广州");
        cities.add("北京");
        cities.add("天津");
        //indexOf取得索引值
        int index1= cities.indexOf("广州");
        //binarySearch找到索引值
        int index2= Collections.binarySearch(cities, "广州");
        System.out.println("索引值(indexOf):"+index1);
        System.out.println("索引值(binarySearch):"+index2);
    }
}

binarySearch采用的是二分法搜索的Java版实现。从中间开始搜索,结果确定是2了。

使用binarySearch的二分法查找比indexOf的遍历算法性能上高不少,特别是在大数据集且目标值又接近尾部时,binarySearch方法与indexOf方法相比,性能上会提高几十倍,所以从性能的角度考虑时能够选择binarySearch。

建议75:集合中的元素必须作到compareTo和equals同步

一看到标题,短期有些发懵,简单来讲,

indexOf依赖equals方法查找,binarySearch则依赖compareTo方法查找;

equals是判断元素是否相等,compareTo是判断元素在排序中的位置是否相同。

注意:实现了compareTo方法就应该覆写equals方法,确保二者同步。

建议76:集合运算的并集、交集、差集

一、并集 addAll

二、交集 retainAll

三、差集 removeAll

四、无重复的并集

并集是集合A加集合B,那若是集合A和集合B有交集,就须要确保并集的结果中只有一份交集,此为无重复的并集,此操做也比较简单,代码以下:

//删除在list1中出现的元素
list2.removeAll(list1);
//把剩余的list2元素加到list1中
list1.addAll(list2);

之因此介绍并集、交集、差集,那是由于在实际开发中,不多有使用JDK提供的方法实现集合这些操做,基本上都是采用了标准的嵌套for循环:要并集就是加法,要交集就是contains判断是否存在,要差集就使用了!contains(不包含)。

之因此会写这么low的东西是想告诫本身多用JDK提供的方法,不要总想着本身去实现,不少东西Java老贼都有现成的!

建议77:使用shuffle打乱列表

简而言之,言而总之,shuffle就是用来打乱list列表顺序的,应用的场景好比

一、在抽奖程序中:好比年会的抽奖程序,先使用shuffle把员工顺序打乱,每一个员工的中奖概率相等,而后就能够抽出第一名、第二名。

二、用于安全传输方法:好比发送端发送一组数据,先随机打乱顺序,而后加密发送,接收端解密,而后进行排序!

建议78:减小hashmap中元素的数量

一、hashmap和ArrayList的长度都是动态增长的,两者机制有些不一样

二、先说hashmap,它在底层是以数组的方式保存元素的,其中每个键值对就是一个元素,也就是说hashmap把键值对封装成一个entry对象,而后再将entry对象放到数组中,也就是说hashmap比ArrayList多一层封装,多出一倍的对象。

在插入键值对时会作长度校验,若是大于或者等于阈值,则数组长度会增大一倍。

hashMap的size大于数组的0.75倍时,就开始扩容,一次扩容两倍。

三、ArrayList的扩容策略,它是在小于数组长度的时候才会扩容1.5倍。

综合来讲,HashMap比ArrayList多了一层Entry的底层封装对象,多占用了内存,而且它的扩容策略是2倍长度的递增,同时还会根据阈值判断规则进行判断,所以相对于ArrayList来讲,一样的数据,它就会优先内存溢出。

注:Entry是Map中用来保存一个键值对的,而Map实际上就是多个Entry的集合。

建议79:集合中哈希码不要重复

一、Java中hashcode的理解

JVM每new一个Object,都会在Hash哈希表中产生一个hashcode。假设不一样的对象产生了相同的hashcode,hash key相同致使冲突,那么就在这个hash key的地方产生了一个链表,将一样hashcode的对象放到单链表中,串到一块儿。

重写equals时必定要重写hashcode方法

两个相等对象的equals方法必定为true, 但两个hashcode相等的对象不必定是相等的对象。

hashcode相等仅仅保证两个对象在hash表里的同一个hash链上,继而经过equals方法才能肯定是否是同一个对象。

总而言之,hashmap中hashcode应避免冲突。

建议80:多线程使用Vector或HashTable

Vector是ArrayList的多线程版本,HashTable是HashMap的多线程版本。

建议81:非稳定排序推荐使用List

什么叫非稳定排序?

public static void main(String[] args) {
        SortedSet<Person> set = new TreeSet<Person>();
        // 身高180CM
        set.add(new Person(180));
        // 身高175CM
        set.add(new Person(175));
        set.first().setHeight(185);
        for (Person p : set) {
            System.out.println("身高:" + p.getHeight());
        }
    }

奇了怪了,为何呢?这个就是非稳定排序。

SortedSet接口(TreeSet实现了此接口)只是定义了在给集合加入元素时将其进行排序,并不能保证元素修改后的排序结果,所以TreeSet适用于不变量的集合数据排序。

解决方法:

一、Set集合重排序:从新生成一个Set对象,再排序。

set.first().setHeight(185);
//set重排序
set=new TreeSet<Person>(new ArrayList<Person>(set));

感受若是这么写就失去了代码了乐趣了,太愚蠢了,pass。

二、使用List解决

使用Collections.sort()方法对List排序。

list中容许有重复的元素,避免重复能够先转成HashSet,去重后再转回来。

建议82:由点及面,集合你们族总结

Java中的集合类实在是太丰富了,有经常使用的ArrayListHashMap,也有不经常使用的StackQueue,有线程安全的VectorHashTable,也有线程不安全的LinkedListTreeMap,有阻塞式的ArrayBlockingQueue,也有非阻塞式的PriorityQueue等,整个集合你们族很是庞大,能够划分如下几类:

一、List:

实现List接口的集合主要有:ArrayList、LinkedList、Vector、Stack,其中ArrayList是一个动态数组,LinkedList是一个双向链表,Vector是一个线程安全的动态数组,Stack是一个对象栈,遵循先进后出的原则。  

stack简介:

Stack来自于Vector,那么显然stack的底层实现是数组。

stack的方法:

① push(xx); //入栈

② pop() ; //栈顶元素出栈

③ empty() ; //断定栈是否为空

④ peek(); //获取栈顶元素

⑤ search(xx); //判端元素num是否在栈中,若是在返回1,不在返回-1。

二、Set:

Set是不包含重复元素的集合,其主要实现类有:EnumSet、HashSet、TreeSet,其中EnumSet是枚举类型专用Set,全部元素都是枚举类型;HashSet是以哈希吗决定其元素位置的Set,其原理和hashmap类似,它提供快速的插入和查找方法;TreeSet是一个自动排序的Set,它实现了SortedSet接口。

三、Map:

HashMap、HashTable、Properties、EnumMap、TreeMap等。

其中properties是hashtable的子类,它的主要用途是从property文件中加载数据,并提供方便的操做,EnumMap则要求其key必须是一个枚举类型。

map中还有一个WeakHashMap类须要简单说明一下,它是一个采用弱键方式的map类,它的特色是:WeakHashMap对象的存在并不会阻止垃圾回收器对键值对的回收,也就是说使用WeakHashMap不用担忧内存溢出的问题,GC会自动删除不用的键值对,但存在一个严重的问题:GC是静悄悄的回收的(什么时候回收,God,Knows!)咱们的程序没法知晓该动做,存在着重大的隐患。

WeakHashMap存在重大隐患,那这个东西何时使用呢?

在《Effective Java》一书中第六条,消除陈旧对象时,提到了weakHashMap,使用短期内就过时的缓存时最好使用weakHashMap。

四、Queue:

浅谈Java队列Queue

五、数组:

数组与集合的最大区别是数组可以容纳基本类型,而集合只能容纳引用类型,全部的集合底层存储的都是数组。

 

编写高质量代码:改善Java程序的151个建议@目录

相关文章
相关标签/搜索