Brief
The previous chapter briefly describes what a collection is and what kinds of collections there are.
In this chapter we mainly introduce one of the implementation of collection, List.
What is List
In the previous chapter, we have learned that list is divided into 3 categories, ArrayList, linkedlist and vectors.
In order to further clarify the structure of the list, I drew a picture here, to review
Abstarctcollection in the previous Java collection-What is a collection has a simple introduction, it is the collection interface part of the implementation
1.List Interface
First look at the official definition of the list
This description solves the difference between the two questions that many companies often ask about the list and what the set is.
It is clear from the above that the list is an ordered set, unlike set, where the list allows the value of the item to be stored null, also allows storage items that store equal values, and E1.equal (E2).
The list is inherited from the collection interface and extends some of the methods that are only part of the list, except for collection common methods.
From the above figure can be found, the list than collection mainly a few add (...) ) method and remove (...) The overloads of the method, as well as a few index (...), get (...) Method.
And Abstractlist also just realize the list interface part of the method, and Abstractcollection is a train of thought, here is not specifically introduced, interested students can study under their own.
2.ArraryList
ArrayList is a list of array implementations, and because the data is stored in an array, it is also characterized by arrays of queries, but the insertion and deletion of the middle part is slow. Let's look at a few key pieces of code.
First is the ArrayList class relations and the member variables
ArrayList inherits Serializable and declares Serialversionuid, which means that ArrayList is a serializable object that can be passed in bundle by the public class arraylist<e> Extends abstractlist<e> implements List<e>, Randomaccess, cloneable, java.io.Serializable {privat
E static final long serialversionuid = 8683452581122892189L;
/** * Default initial capacity.
* private static final int default_capacity = 10;
/** * Shared Empty array instance used for empty instances.
* * private static final object[] Empty_elementdata = {};
/** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. any * empty ArrayList with elementdata = = Empty_elementdata would be expanded to * default_capacity when the Firs
t element is added.
//////////////From here, the ArrayList is implemented by array, and the default size of the default array is the private transient object[] elementdata; /** * The size of the ArrayList (the number of ElemeNTS it contains).
* * @serial/private int size;
And then the constructor
ArrayList has 2 constructors, one is the default parameterless, one is the passed-in array size
/////In the Javaeffect book explicitly mentions that if you know or estimate the number of data items you want, you need to pass in the initialcapacity
// Because if you use an parameterless constructor, you first assign the Empty_elementdata to Elementdata
//And then call the Arrays.copyof () method, extending the array size, based on the number of inserts in the current array size comparison.
/cause performance Waste
/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialcapacity The initial capacity of the list
* @throws illegalargumentexception if the SP Ecified Initial capacity
* is negative/public
ArrayList (int initialcapacity) {
super ();
if (initialcapacity < 0)
throw new IllegalArgumentException ("illegal Capacity:" +
initialcapacity);
This.elementdata = new object[initialcapacity];
}
/**
* Constructs a empty list with an initial capacity of ten.
* * Public
ArrayList () {
super ();
This.elementdata = Empty_elementdata;
}
Then the Add () action
First of all, it is only the specified index to perform the add operation, or the add operation at the tail, will first confirm whether the current array space enough to insert data//and from//int oldcapacity = elementdata.length;
int newcapacity = oldcapacity + (oldcapacity >> 1);
if (newcapacity-mincapacity < 0)//newcapacity = mincapacity; See, ArrayList default each time is to increase the size of 50% and mincapacity compared, if still not enough, the//size expansion to mincapacity//Then, if the end of the team to insert, but also simple, the array to move backward one, and then assign the value/
If it is inserted in the middle, need to use the System.arraycopy, the index start all the data backward to move one//and then insert/** * Appends the specified element to the "end of" this list. * * @param e element to is appended to this list * @return <tt>true</tt> (as specified by {@li NK Collection#add}) */public boolean Add (E e) {ensurecapacityinternal (size + 1);
Increments modcount!!
elementdata[size++] = e;
return true; }/** * Inserts the specified element at the specified position on this * list. Shifts the element currently at this position (if any) and * No subsequent elements to the right (adds one to TheiR indices).
* * @param index which the specified element is inserted * @param element element to be inserted * @throws indexoutofboundsexception {@inheritDoc} */public void Add (int index, E element) {Rangech
Eckforadd (index); Ensurecapacityinternal (size + 1);
Increments modcount!!
System.arraycopy (Elementdata, index, Elementdata, index + 1, size-index);
Elementdata[index] = element;
size++;
private void ensurecapacityinternal (int mincapacity) {if (Elementdata = = Empty_elementdata) {
mincapacity = Math.max (default_capacity, mincapacity);
} ensureexplicitcapacity (mincapacity);
} private void ensureexplicitcapacity (int mincapacity) {modcount++;
Overflow-conscious code if (mincapacity-elementdata.length > 0) grow (mincapacity); } private void Grow (int mincapacity) {//overflow-conscious code int oldcapacity = ELEMENTDATA.L
Ength;
int newcapacity = oldcapacity + (oldcapacity >> 1);
if (newcapacity-mincapacity < 0) newcapacity = mincapacity;
if (newcapacity-max_array_size > 0) newcapacity = hugecapacity (mincapacity);
Mincapacity is usually the close to size, so this is a win:elementdata = arrays.copyof (Elementdata, newcapacity); }
Then the Remove action
Personally feel the entire remove operation of the code has been written very redundant, unlike Oracle these great God style//First look at the Remove (int index)//first boundary confirmation, whether the incoming index exceeds the current size of the array, if it throws an exception//if in the array range, To move the data after the index whole forward one, the last value empty//If the Remove (object o), incoming is an object, will do a indexof operation, go to the current array to find//judge whether exists, the code here is very redundant, is to copy the IndexOf code once, completely can call the IndexOf method//based on whether the return value is-to determine whether the value exists, if there is to call the Fastremove method//fastremove (int index) and remove (int Index) method except the boundary check is exactly the same//can be called after the Remove call Rangecheck (index) after the call to Fastremove.//This is not very clear the intent of the designer/** * Removes the element at th
e specified position in the this list.
* Shifts any subsequent elements to the "left" (subtracts one from their * indices).
* * @param index The "the" is removed * @return the element that is removed from the list
* @throws indexoutofboundsexception {@inheritDoc} */public E remove (int index) {rangecheck (index);
modcount++;
E OldValue = elementdata (index);
int nummoved = size-index-1; if (nummoved > 0) system.arraycopy (elementdatA, index+1, Elementdata, Index, nummoved); Elementdata[--size] = null;
Clear to let GC does its work return oldValue; }/** * Removes the the specified element from this list, * if it is present. If The list does not contain the element, it is * unchanged. More formally, removes the "element with" lowest index * <tt>i</tt> such that * <tt> (o==null ? get (i) ==null : o.equals (get (i)) </tt> * (if such a element exists).
Returns <tt>true</tt> If this list * contained the specified element (or equivalently, if this list
* Changed as a result of the call). * * @param o element to is removed from-list, if present * @return <tt>true</tt> if this list C Ontained the specified element */public boolean remove (Object o) {if (o = = null) {for intindex = 0; index < size;
index++) if (elementdata[index] = = null) {fastremove (index);
return true; } else {for (int index = 0; index < size; index++) if (o.equals) (Elementdata[inde
X])) {fastremove (index);
return true;
return false;
}/* Private Remove method, skips bounds checking and does not * return the value removed.
* * private void Fastremove (int index) {modcount++;
int nummoved = size-index-1; if (nummoved > 0) system.arraycopy (elementdata, index+1, Elementdata, index, n
ummoved); Elementdata[--size] = null; Clear to let GC does its work}
IndexOf
This is exactly the same as the top remove, and it's not discussed. 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 element Data[i]) return
i;
}
return-1;
}
3.Vector
Vector is ArrayList's thread-safe version, its methods are synchronized locks, and the other implementation logic is the same.
If the thread safety requirements are not high, you can choose ArrayList, after all, synchronized is also very performance-consuming
4.LinkedList
So name thinking is linked list, and our university in the data structure of the linked list is a matter, linkedlist or a two-way linked list.
LinkedList inherits from Abstractsequentiallist, and ArrayList a routine. The internal maintenance of 3 member variables, one is the current list of the head node, one is the tail node, as well as the chain table length. And then we're looking at node this data structure.
and C-language implementation of the same way, because it is a two-way linked list, so recorded Next and Prev, but the C language in the pointer replaced by the object.
And then we're simply looking at the list limit query, insert and delete operations
The first is add (e e) operation
Students who have studied data structures look at this part of the code is particularly easy//first look at the void Linklast (e e), the tail insert//is the newnode of the front node to perform the current tail node, newnode after the node to perform NULL, because it is in the tail Then point the rear node of the current tail node to NewNode, because now the tail node is not the last one//and then look at the middle insert//is also a routine. Suppose you now insert a newnode//in number 3rd to find the 2nd node through the current 3rd node prev, and then modify next for node 2nd, point to Nownode//Then Nownode prev to Node 2nd, next to Node 3rd//Last 3 The prev of the node is changed to Nownode,next//This completes a middle insert/** * Inserts the specified element at the specified position
This list. * Shifts the element currently at this position (if any) and all * Subsequent elements to the right (adds one to Thei
R indices).
* * @param index which the specified element is inserted * @param element element to be inserted * @throws indexoutofboundsexception {@inheritDoc} */public void Add (int index, E element) {Checkpo
Sitionindex (index);
if (index = = size) linklast (element);
else Linkbefore (element, node (index)); }
/**
* Appends the specified element to the "end of" this list.
* * <p>this method are equivalent to {@link #addLast}.
* @param e element to is appended to this list * @return {@code true} (as specified by {@link Collection#add})
* * Public boolean Add (E e) {linklast (e);
return true;
}/** * Links e as last element.
* * void Linklast (e e) {final node<e> L = last;
Final node<e> NewNode = new node<> (l, E, NULL);
last = NewNode;
if (L = = null), NewNode;
else L.next = NewNode;
size++;
modcount++;
}/** * Inserts element e before Non-null Node succ.
*/void Linkbefore (e E, node<e> succ) {//assert succ!= null;
Final node<e> pred = Succ.prev;
Final node<e> NewNode = new Node<> (pred, E, SUCC);
Succ.prev = NewNode; if (pred = null)
i = NewNode;
else Pred.next = NewNode;
size++;
modcount++; }
Then the void Linklast (e e) Action
IndexOf operation is very simple, is to traverse the entire list from scratch, if not on the reverse-1, there will be returned the current subscript/** * Returns The index of the first occurrence of the specified eleme
NT * In this list, or-1 if this list does not contain the element. * More formally, returns the lowest index {@code i} such that * <tt> (o==null ?
get (i) ==null : o.equals (get (i)) </tt>, * or-1 If there is no such index.
* * @param o element to search for * @return The index of the ' the ' occurrence of the specified element in * This list, or-1 if this list does not contain the element */public int indexOf (Object o) {in
T index = 0;
if (o = = null) {for (node<e> x = i x!= null; x = X.next) {if (X.item = = null)
return index;
index++; } else {for (node<e> x = i x!= null; x = X.next) {if (O.equals (X.item)
) return index;
index++;
}} return-1; }
Although IndexOf is very simple, I have written an example here to help you understand
List List = new ArrayList ();
List.add ("Zero");
List.add (null);
List.add ("two");
List.add (null);
List.add ("three");
LOG.I ("Test", "Index:" + list.indexof (null));
Can you tell the answer accurately without looking at the answer?
Answer:i/test:index:1
From this example you can see the characteristics of the three-point list
1. is to find in order
2. Allow storage entries to be empty
3. Allow the values of multiple storage items to be equal
Last look at the Remove action
If you directly adjust the no parameter remove (), the default Delete header node//delete header node is very simple, that is, the value of the head node is emptied, next empties//then NextNode only as the head node, then empties next prev//last size minus 1 If you delete an intermediate node, call remove (int index)//first determine whether the node of index corresponds to the head node, that is, whether index is 0//If it is not an intermediate node, the prev of X points to the next public E remove of X
() {return Removefirst ();
Public E-Remove (int index) {checkelementindex (index);
Return unlink (node (index));
Public E Removefirst () {final node<e> f = i;
if (f = = null) throw new Nosuchelementexception ();
Return Unlinkfirst (f);
Private E Unlinklast (node<e> l) {//assert L = = last && l!= null;
Final E element = L.item;
Final node<e> prev = L.prev;
L.item = null; L.prev = null;
Help GC last = prev;
if (prev = null) is a = null;
else Prev.next = null;
size--;
modcount++;
return element; }
/**
*Unlinks non-null node x.
*/E unlink (node<e> x) {//assert x!= null;
Final E element = X.item;
Final node<e> next = X.next;
Final node<e> prev = X.prev;
if (prev = = null) {i = next;
else {prev.next = next;
X.prev = null;
} if (next = null) {last = prev;
else {next.prev = prev;
X.next = null;
} X.item = null;
size--;
modcount++;
return element;
}/** * Unlinks non-null-A-node F.
* Private E Unlinkfirst (node<e> f) {//assert F = = a && f!= null;
Final E element = F.item;
Final node<e> next = F.next;
F.item = null; F.next = null;
Help GC-i = next;
if (next = null) last = NULL;
else Next.prev = null;
size--; modcount++;
return element; }
Summary
Through the above analysis of ArrayList and LinkedList, we can understand the 3 characteristics of list
1. is to find in order
2. Allow storage entries to be empty
3. Allow the values of multiple storage items to be equal
You know it, you know it.
Then compare the LinkedList and ArrayList implementations differently, and use a different list in different scenarios.
ArrayList is implemented by array, easy to find, return the corresponding value of the array subscript, suitable for multiple lookup scenes
LinkedList is implemented by linked list, easy to insert and delete, suitable for multiple data replacement scenes
In the next chapter, we can see how the set is implemented, and what features the set has