1 java集合的接口框架
集合的接口框架
Java集合分為Collections和Map兩大種。
2 Collection集合
定義了Collection集合分支的基礎方法,有查詢方法,修改集合方法,批量操作方法和比較與hash方法,這些都是集合的基礎方法。
package java.util;
import java.util.function.Predicate;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public interface Collection<E> extends Iterable<E> {
// Query Operations
int size();
boolean isEmpty();
boolean contains(Object o);
Iterator<E> iterator();
Object[] toArray();
<T> T[] toArray(T[] a);
// Modification Operations
boolean add(E e);
boolean remove(Object o);
// Bulk Operations
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean removeAll(Collection<?> c);
default boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
boolean removed = false;
final Iterator<E> each = iterator();
while (each.hasNext()) {
if (filter.test(each.next())) {
each.remove();
removed = true;
}
}
return removed;
}
boolean retainAll(Collection<?> c);
void clear();
// Comparison and hashing
boolean equals(Object o);
int hashCode();
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, 0);
}
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
default Stream<E> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
}
Collection分為這幾類
1.List: 有序集合,值允許有重復
2.Set:無需集合,值不允許有重復
3.Queue:保持先進先出順序
2.1 List集合: ArrayList&LinkedList
ArrayList的基礎數據結構是數組,數組因為可以通過下標訪問成為一個隨機訪問第n個數效率很高的數據結構,隨機訪問查詢的時間復雜度是O(1),查詢某個定值的時間復雜度是O(n),刪除/增加新的元素到某個位置時,需要一個一個的移動數組中的元素直至適合的位置,所以時間復雜度是O(n)
LinkedList的基礎數據結構是鏈表,在java中鏈表的每一個節點具有指向前結點的prev引用和指向后結點next引用,即雙向鏈表。正是因為這樣的設計,在鏈表中插入元素時,只需要改變插入位置前后結點的結點指向和添加新元素的結點指向即可,時間復雜度是O(1),在訪問元素的時候,無論是隨機方位第幾位的元素還是查詢某個定值時,鏈表的時間復雜度均為O(n)
所以,根據實際場景,如果查詢操作比較多的話,建議采用ArrayList等數組實現的List,效率會高。
在java中,LinkedList也提供根據index獲得元素的方法
/**
* Returns the element at the specified position in this list.
*
* @param index index of the element to return
* @return the element at the specified position in this list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
每次查找一個元素都要從列表的頭部重新開始搜索,LinkedList對象不做任何緩存位置信息的操作,所以循環遍歷獲取LinkedList對象的數據值,效率不高。
for (int i = 0; i < linkedList.size(); i++){
do something with linkedList.get(i);
}
2.2 Set集合: HashSet&TreeSet
HashSet:不會對放入集中的數據進行排序,基于Hash存儲與查找數據。在java中,散列表用鏈表數組實現,每個列表被稱為桶,想要查找表中的對象,需要先計算他的hashCode,然后與桶的總數取余,所得到的結果就是保存這個元素的桶的索引。一般裝填因子是0.75,保持一個好的裝填因子與桶的數目,可以減少hash沖突,使每次查找盡量一次定位找到,采用has方式查找元素的時間復雜度是O(1)
TreeSet:對放入集中的數據進行排序,他是一個有序的集合,可以以任意順序將元素插入到集合中,在對集合進行遍歷時,每個值將自動地按照排序后的順序呈現,他采用的數據結構是紅黑樹。每次將一個元素添加到樹中,都被放置在正確的排序位置上,因此,迭代器總是以排好序的順序訪問每個元素。
將一個元素添加到樹中要比添加到散列表中慢,但是與將元素添加到數組或者鏈表的正確位置上還是快很多的。如果樹中包含n個元素,查找新元素的正確位置平均需要log2 n次比較。
如果所要收集的數據,不需要排序的話,那么并不需要付出排序的開銷即可以考慮HashSet
TreeSet如何知道希望元素怎么排列呢?
2.3 對象的比較: Comparable&Comparator
Comparable:是排序接口,若一個類實現了Comparatable接口,就意味著“該類支持排序”,既然實現Comparable接口的類支持排序,假設現在存在“實現Comparable接口的對象的List數組”,則該List列表或者數組可以通過Collection.sort(或Arrays.sort)進行排序。實現Comparable接口的類的對象可以用作有序映射(e.g.TreeMap)中的“鍵值”或“有序集合(e.g.TreeSet)中的元素”,而不需要指定比較器。
package java.lang;
import java.util.*;
public interface Comparable<T> {
public int compareTo(T o);
}
Comparator:比較器接口,如果需要控制某個類的次序,而該類本身不支持排序(沒有實現Comparable接口),那么我們可以建立一個“該類的比較器”來進行排序,這個“比較器”只需要實現Comparator接口即可,我們可以通過“實現Comparator類來建立一個比較器”,然后通過該比較器對類進行排序。
package java.util;
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
}
Comparable相當于“內部比較器”,而Comparator相當于“外部比較器”。
Comparable&Comparator使用:
public class Item implements Comparable<Item> {
private String description;
private int partNumber;
public Item(String description, int partNumber) {
this.description = description;
this.partNumber = partNumber;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public int getPartNumber() {
return partNumber;
}
public void setPartNumber(int partNumber) {
this.partNumber = partNumber;
}
@Override
public String toString() {
return "Item{" +
"description='" + description + '\'' +
", partNumber=" + partNumber +
'}';
}
@Override
public boolean equals(Object otherObject) {
if (this == otherObject){
return true;
}
if (otherObject == null){
return false;
}
if (getClass() != otherObject.getClass()){
return false;
}
Item other = (Item)otherObject;
return Objects.equals(description, other.description) && partNumber == other.partNumber;
}
@Override
public int hashCode() {
return Objects.hash(description, partNumber);
}
@Override
public int compareTo(Item other) {
return Integer.compare(partNumber, other.partNumber);
}
}
public class ComparatorTest {
@Test
public void treeSetTest(){
SortedSet<Item> parts = new TreeSet<Item>();
parts.add(new Item("Toaster", 1234));
parts.add(new Item("Widget", 4562));
parts.add(new Item("Modem", 9912));
SortedSet<Item> sortByDescription = new TreeSet<Item>(new Comparator<Item>() {
public int compare(Item a, Item b) {
String descA = a.getDescription();
String descB = b.getDescription();
return descA.compareTo(descB);
}
});
sortByDescription.addAll(parts);
System.out.println(sortByDescription);
}
}
2.4 Queue: PriorityQueue
優先級隊列,元素可以按照任意的順序插入,但總是按照排序的順序進行檢索,內部實現的數據結構是堆。堆是一個可以自我調整的二叉樹,對樹執行添加和刪除的時候,可以讓最小的元素移動到根,而不用花費時間對元素進行排序。使用的典型實例是任務調度場景。
3 Map集合
鍵值對,鍵必須是唯一的,不能對同一個鍵存放兩個值,Map的基礎方法
public interface Map<K,V> {
// Query Operations
int size();
boolean isEmpty();
boolean containsKey(Object key);
boolean containsValue(Object value);
V get(Object key);
// Modification Operations
V put(K key, V value);
V remove(Object key);
// Bulk Operations
void putAll(Map<? extends K, ? extends V> m);
void clear();
// Views
Set<K> keySet();
Collection<V> values();
Set<Map.Entry<K, V>> entrySet();
interface Entry<K,V> {
K getKey();
V getValue();
V setValue(V value);
boolean equals(Object o);
int hashCode();
public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() {
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> c1.getKey().compareTo(c2.getKey());
}
public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K,V>> comparingByValue() {
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> c1.getValue().compareTo(c2.getValue());
}
public static <K, V> Comparator<Map.Entry<K, V>> comparingByKey(Comparator<? super K> cmp) {
Objects.requireNonNull(cmp);
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> cmp.compare(c1.getKey(), c2.getKey());
}
public static <K, V> Comparator<Map.Entry<K, V>> comparingByValue(Comparator<? super V> cmp) {
Objects.requireNonNull(cmp);
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> cmp.compare(c1.getValue(), c2.getValue());
}
}
// Comparison and hashing
boolean equals(Object o);
int hashCode();
// Defaultable methods
default V getOrDefault(Object key, V defaultValue) {
V v;
return (((v = get(key)) != null) || containsKey(key))
? v
: defaultValue;
}
default void forEach(BiConsumer<? super K, ? super V> action) {
Objects.requireNonNull(action);
for (Map.Entry<K, V> entry : entrySet()) {
K k;
V v;
try {
k = entry.getKey();
v = entry.getValue();
} catch(IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
action.accept(k, v);
}
}
default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
Objects.requireNonNull(function);
for (Map.Entry<K, V> entry : entrySet()) {
K k;
V v;
try {
k = entry.getKey();
v = entry.getValue();
} catch(IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
// ise thrown from function is not a cme.
v = function.apply(k, v);
try {
entry.setValue(v);
} catch(IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
}
}
default V putIfAbsent(K key, V value) {
V v = get(key);
if (v == null) {
v = put(key, value);
}
return v;
}
default boolean remove(Object key, Object value) {
Object curValue = get(key);
if (!Objects.equals(curValue, value) ||
(curValue == null && !containsKey(key))) {
return false;
}
remove(key);
return true;
}
default boolean replace(K key, V oldValue, V newValue) {
Object curValue = get(key);
if (!Objects.equals(curValue, oldValue) ||
(curValue == null && !containsKey(key))) {
return false;
}
put(key, newValue);
return true;
}
default V replace(K key, V value) {
V curValue;
if (((curValue = get(key)) != null) || containsKey(key)) {
curValue = put(key, value);
}
return curValue;
}
default V computeIfAbsent(K key,
Function<? super K, ? extends V> mappingFunction) {
Objects.requireNonNull(mappingFunction);
V v;
if ((v = get(key)) == null) {
V newValue;
if ((newValue = mappingFunction.apply(key)) != null) {
put(key, newValue);
return newValue;
}
}
return v;
}
default V computeIfPresent(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
V oldValue;
if ((oldValue = get(key)) != null) {
V newValue = remappingFunction.apply(key, oldValue);
if (newValue != null) {
put(key, newValue);
return newValue;
} else {
remove(key);
return null;
}
} else {
return null;
}
}
default V compute(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
V oldValue = get(key);
V newValue = remappingFunction.apply(key, oldValue);
if (newValue == null) {
// delete mapping
if (oldValue != null || containsKey(key)) {
// something to remove
remove(key);
return null;
} else {
// nothing to do. Leave things as they were.
return null;
}
} else {
// add or replace old mapping
put(key, newValue);
return newValue;
}
}
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;
}
}
a.Map的三個視圖方法
Set<K> keySet();
Collection<V> values();
Set<Map.Entry<K, V>> entrySet();
分別是獲得鍵集、值集合鍵值對集