Java collections Detailed 8: Java collection classes succinctly details, the details determine success or failure

This series of articles will arrange to me on GitHub's "Java Interview Guide" warehouse, more exciting content please go to my warehouse View

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

Like it under the trouble spots Star Kazakhstan

Article first appeared in my personal blog:

www.how2playlife.com

This article is a micro-channel public number [of] Java technology rivers and lakes "into JavaWeb technology world" in which a Part of this article from the network, to the subject of this article speaks clear and thorough, and I think a lot of good integration of technology blog content, which cited a number of good blog posts, if infringement, please contact the author.

This blog series will show you how to from entry to advanced, from servlet to the frame, and then from ssm SpringBoot, step by step JavaWeb learn the basics and get started actual combat, and then to understand the technical components of the project JavaWeb often used, including log component, Maven, Junit, and much more, in order to make you a more complete understanding of the entire JavaWeb technology system, to form their own intellectual framework. In order to summarize and test your learning outcomes, this series will provide every knowledge point corresponding interview questions and suggested answers.

If you have any suggestions for this series of articles, or have any questions, you can also concerned about the number of public rivers and lakes [] Java technology to contact the author, you are welcome to participate in the creation and revision of this blog series.

End of the text presented 8000G Java architect of learning materials, a friend in need can go to the end to understand way to receive, including information on Java-based, advanced, project architects and other free learning materials and, more databases, distributed, service and other popular micro-technology learning video, rich content, both theory and practice, the other will also be presented original authors study guide Java, Java programmer interview guide and other resources dry goods)
<-! More ->

Today we explore some of the technical details of the Java collection classes. Mainly for some of the more easily be missed and misunderstood knowledge to do some explaining and additions. May be incomplete, please understand.

The initial capacity

Our collection is very widely used, it is like the sea in Java programming, be tolerant to diversity, like universal container, dressed all things, and the sea, universal container can become infinitely large (if conditions allow). When the amount of the sea, the vessel becomes very large, its initial capacity will be very very important, because digging the sea, the expansion is the need to consume a large amount of manpower and financial resources.

By the same token, the initial capacity of the Collection is also extremely important. So: For known scenario, specify the initial capacity of the 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));
}

The above two codes are inserted into the list of data 1,000,000, but there is no application list1 initial capacity, initial capacity and list2 1,000,000. That results are as follows:

list1 time:1638
list2 time:921

Operating results from the above we can see that the speed is about twice list1 list2's. LZ on the front mentioned, ArrayList expansion mechanism is resource consuming. We look at the ArrayList add method:

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 Each time a new element, it will detect whether the current capacity of the ArrayList has reached a critical point, if you reach the critical point will be 1.5 times the expansion. However, expansion and copying of the arrays generate a new ArrayList array is very resource intensive. So if we had known usage scenarios collections, we understand that the range of the collection, the best we specified initial capacity, this use of resources will be even better, especially under the premise of a large amount of data and the use of resources and improve the efficiency of It will be even more advantageous.

asList defects

In the actual development process, we often use asList speak array into a List, this method is very convenient to use, but there are several drawbacks asList method:

Avoid the use of an array of basic data types into a list

There will be a comparison of flavored defects using an array of eight basic types into a list. Look at the following program:

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

Run the program and the results are not as we expected but the Guards 5 1 What is the situation? Look at the source code:

public static <T> List<T> asList(T... a) {
        return new ArrayList<>(a);
    }

asList accept the argument is a generic variable-length parameters, we are unable to know the basic data types of hair, that is eight basic types can not be used as a parameter asList, in order as a generic parameter must use it corresponding to the type of packaging. But this this example why no wrong?

Since this example is an array of type int as its parameters, an array in Java is an object, it is possible to generified. So this example is no wrong. Since it is an example of the entire array of type int as the generic parameter, then after asList convert only a list of int. as follows:

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));
}

the OUTPUT:
list of types: class [the I
list.get (0) == INTS: to true
From this result we can run proved list of elements which is int array. Clarify this point, then modify the method also clear: to change the 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

List generated inoperable asList

For the example above we do a little modification:

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

This example is by asList say ints converted to list categories, and then add an element through the add method, this example simply can not be simple, but the result of it running? We expected to play:

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)

Run necessarily result in an UnsupportedOperationException exception, which represents the list does not support the add method. Which brings us depressed, list how it might not support the add method? Is jdk head clogged? We look asList source:

public static <T> List<T> asList(T... a) {
        return new ArrayList<>(a);
    }

asList After accepting the argument, direct a new ArrayList, to see here should be no wrong ah? Do not worry, then look down:

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;
        }
        //.................
    }

This is the source ArrayList, from here we can see, this is not the ArrayList java.util.ArrayList, he was Arrays inner class.

The internal class provides size, toArray, get, set, indexOf, contains method, and the method to change the list as the result of the add, remove, etc. AbstractList inherited from the parent class, but these methods are more wonderful, it has a direct throw an UnsupportedOperationException exception:

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();
    }

It can be seen asList returned list is merely a cloak dressed by the code list, it does not list the basic characteristics (variable length). The list is a list of immutable length, how long arguments passed an array, a list of which can only be returned long. So do not try to change the list asList :: returned, or you will eat the bitter fruit.

subList defects

We often use subString approach to segmentation processing of String objects, and we can also use subList, subMap, subSet come to List, Map, Set segmentation process, but this division there are some flaws.

subList return just a view

First we look at the following examples:

public static void main(String[] args) {
List<Integer> list1 = new ArrayList<Integer>();
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));
}

This example is very simple, nothing more than the constructor, subList regenerate with a list1 same list, and then modify list3, and finally compare list1 == list2?, List1 == list3 ?.

According to our conventional way of thinking it should be this: because by list3 add a new element, then it must vary with the list1, but list2 through list1 constructed, so it should be equal, so the result should be:

list1 == list2:true
list1 == list3: false

First, let's correct or not, regardless of the results, we look at the source code subList:

public List<E> subList(int fromIndex, int toIndex) {
        subListRangeCheck(fromIndex, toIndex, size);
        return new SubList(this, 0, fromIndex, toIndex);
}

subListRangeCheck way is to determine fromIndex, toIndex is legitimate, if a legitimate direct return subList objects, pay attention to passing one option at this time to produce the new of the object, this parameter is very important because he represents the original list.

/**

  • AbstractList inheritance class that implements the RandomAccess interface
    * /
    Private class subList the extends AbstractList <E> the implements RandomAccess {
    Private Final AbstractList <E> parent; // list
    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;
    }

    }

The SubLsit is an internal ArrayList class, it ArrayList, are inherited AbstractList and implement RandomAccess interface. It also provides a list of commonly used methods get, set, add, remove and so on. But it's a bit special constructor, the constructor has two things to note:

1, this.parent = parent; while the parent is passed over in front of the list, that is to say this.parent is a reference to the original list.

2, this.offset = offset + fromIndex; this.parentOffset = fromIndex ;. Constructor while it even modCount (fail-fast mechanism) passed over.

We look at the get method, return the get method ArrayList.this.elementData (offset + index);

This code clearly shows that get returned is the original list element offset + index positions. By the same token there inside the add method:

parent.add (parentOffset + index, E);
this.modCount = parent.modCount;
Remove method inside

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

Indeed, to where we can judge subList returned SubList also a subclass AbstractList, as well as its methods such as get, set, add, remove, etc. are done operating in the original list above, it does not like subString as to generate a new object .

So subList just returned from a view of the original list, it will eventually effect all operations on the original list.

Well, from here, we can analyze the outcome of the above should be exactly the opposite and above us the answer:

list1 == list2:false
list1 == list3:true

After subList generate sub-list, do not try to operate the original list

From the above we know subList generated sub-list is just a list of only the original view, if we are operating sub-list action it produces will show the original list above, but if we operate the original list of what would happen?

public static void main(String[] args) {
List<Integer> list1 = new ArrayList<Integer>();
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());
}

If the instance is not unexpected, then they should be the size of two list of all 3, but it happens it did not, in fact the result we get is this:

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 normal output, but list3 ConcurrentModificationException throw an exception, I read another blog affirmation of my colleagues on this very abnormal, fail-fast? Good is fail-fast mechanism, fail-fast mechanism, LZ spent a lot of effort to tell the exception, so here LZ not to speak of this exception. We look at the size method:

public int size() {
            checkForComodification();
            return this.size;
        }

size by checkForComodification verification method first, then returns this.size.

private void checkForComodification() {
            if (ArrayList.this.modCount != this.modCount)
                throw new ConcurrentModificationException();
        }

This method means that when the original list modCount and this.modCount unequal throws ConcurrentModificationException.

We know modCount "inheritance" in the new process of the original list modCount, will only modify the value when modifying the list (sub-list) (first performance in the original list of acting on the sub-list).

In this example, we are operating the original list, modCount original list of course not reflected in the modCount sub-list friends, it will be thrown.

For sub-list view, it is dynamically generated, do not operate the original list after generation, this would certainly have led to instability in the view and throw an exception. The best way is original list is set to read-only operations to operate on a sub-list:

// generate a list with the same list1 through list3 subList

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

// set to read-only status of list1

list1 = Collections.unmodifiableList(list1);

SubList recommended partial list processing

In the development process, we will encounter such a problem: after acquiring a bunch of data, you need to delete certain data. For example, the presence of 1000 records a list, we need to delete data at the 100-200 position, maybe we'll deal with this:

for(int i = 0 ; i < list1.size() ; i++){
   if(i >= 100 && i <= 200){
       list1.remove(i);
       /*
        * 当然这段代码存在问题,list remove之后后面的元素会填充上来,
         * 所以需要对i进行简单的处理,当然这个不是这里讨论的问题。
         */
   }
}

This should be the way most of us deal with it, in fact there is a better way to use subList. In front of the LZ already mentioned, the operation of sub-lists will be reflected in the original list. So the following line of code all set:

list1.subList(100, 200).clear();

Simple yet gorgeous! ! ! ! !

CompareTo and equals to maintain synchronization

In Java, we often use the Comparable interface to achieve the sort, where compareTo is the interface method implementation. We know compareTo returns 0 for two objects are equal, returns a positive number that is greater than, less than negative numbers returned. We also know that equals can also determine whether two objects are equal, then whether there is any relationship between their two?

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方法 */
}

Comparable Student class interface and implementation to achieve the equals method, wherein compareTo according to align the age, name equals according to the alignment.

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);
    }

According to conventional thinking, it should be consistent with both the index, because they retrieve the same object, but it is very regrettable, its operating results:

index1 = 0
index2 = 1

Why would such a different result? This is because different mechanisms to achieve the indexOf and binarySearch.

indexOf is implemented based on equals equals returns TRUE as long as it is believed to have found the same elements.

The binarySearch is based on the compareTo method, compareTo returns 0 when it is thought to have found the element.

In the Student class we realized we override the compareTo and equals methods, but our compareTo, equals a different basis of comparison, one based on age, is based on a name.

Depending then compare the results obtained is likely to be different. So we know the reason, we modified the like: the basis of comparison between the two is to be consistent.

For both methods compareTo and equals we can be summarized as: compareTo is to determine the position of the element in the sort of equality, equals is to determine whether the same element, since a decision ordering position, a decision are equal, so we are very necessary to ensure that when ranking position the same, which equals should be equal.

It should be equal way is both attached to the same conditions. When the values ​​of compareto equals should be equal, not equal equals when compareto should not be equal, and compareto based on certain properties to determine the order.

Reference article

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

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

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

Guess you like

Origin blog.51cto.com/14006572/2448599