集合複習
一.集合就像一種容器,可以動態的把多個對象的引用
放入到容器中,存儲多個數據 。
集合數組都是對多個數據進行存儲操作的結構,簡稱java容器
二、集合框架、
collection接口:單列集合,用來存儲一個一個對象
list接口:存儲有序可重複的數據
實現類:ArrayList LinkedList
set接口:存儲無序,不可重複的數據
實現類:HashSet
map接口:雙列集合,用來存儲一對一對的數據
實現類:HashMap
三、collection(爲什麼不用collection? 因爲即有序又無序,即可重複,又不可重複,找不到這樣的容器,所以纔會用List和Set)
collection的方法:(需要重寫元素所在類的equals方法)
-
add() //添加元素,可以爲簡單類型,和引用類型 ,返回boolean
Collection coll=new ArrayList(); coll.add("aa");
-
addAll(Collection c)//添加集合數據
Collection coll=new ArrayList(); Collection coll1=new ArrayList(); coll1.add("bb"); coll.addAll(coll1);
-
clear() //清空集合
Collection coll=new ArrayList(); coll.add("aa"); coll.clear();
-
isEmpty() // 判斷是否爲空,沒有元素
Collection coll=new ArrayList(); coll.add("aa"); coll.clear(); coll.isEmpty();
-
contains(Object obj)// 是否包含一個對象,判斷的是其內容,內容一致則結果爲true,內容不一致則結果爲false,這裏對於String類型,其equals方法已經重寫了,故而只需要判斷內容是否一致即可,但是對於JAVA BEAN類型,需要重寫其equals方法,否則會判斷爲false。 可看代碼後的總結
例如:
private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } //重寫equals方法,如果不寫,則結果爲false @Override public boolean equals(Object o) { System.out.println("kaishi l "); if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return age == person.age && Objects.equals(name, person.name); }
import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; public class Demo { public static void main(String[] args) { Collection coll=new ArrayList(); coll.add("aa"); coll.add("bb"); System.out.println(coll.contains("aa"));//true ,判斷是否有aa,有,則爲true coll.add(new String ("huhu")); System.out.println(coll.contains(new String("huhu")));//結果爲true,因爲string底層已經把equals重寫過了,直接判斷內容 coll.add(new Person("wwuu",123)); System.out.println(coll.contains(new Person("wwuu",123)));//因爲上面已經對Person對象進行重寫了equals方法,這裏直接判斷是否有wuwu,123.有則爲true //如果上面沒有對Person對象的equals方法進行重寫,那麼這裏爲false //總結:contains比較的是內容,等於用equals方法進行比較 //我們在判斷時,會調用obj對象所在類的equals()方法,故而obj所在類要重寫equals()方法。 } }
-
containsAll(Collection coll1)//判斷coll集合中的所有元素是否都存在於當前集合中(注意是:所有元素,但凡有一個不在,則爲false)
Collection coll=new ArrayList(); Collection coll1=new ArrayList(); coll.add("123"); coll1.add("haha"); coll.addAll(coll1); System.out.println(coll.containsAll(coll1));//true,上面用了addAll()方法,把coll1中的數據都放到了coll中,所以coll包含了coll1,爲true coll1.add("uiui"); System.out.println(coll.containsAll(coll1));//false,這裏是因爲coll1新加了一條數據,而coll中沒有這條數據,故而爲false
-
remove(Object obj) (刪除某個元素,重寫元素所在類的equal()方法)
coll.remove(new Person("wwuu",123)); System.out.println(coll);//由於上述的方法中已經對Person這個類進行了equals()方法的重寫,故而這裏是可以對new Person("wwuu",123)這個元素進行刪除的,如果不重寫equals方法則刪除不了
-
remoeAll(Collection coll)(刪除coll中所有的元素,重寫元素所在類的equal()方法)
coll.removeAll(coll1); System.out.println(coll1); System.out.println(coll);
-
retainAll(Collection coll)(求交集的意思,僅僅保留當前集合中的元素並且該元素同時也在另一個集合中,即獲取當前集合與coll的交集,重寫元素所在類的equal()方法)
Collection coll2=new ArrayList(); coll2.add(123); coll2.add(new String ("huhu")); coll2.add(new String("bb")); coll.retainAll(coll2); System.out.println(coll);
-
equals(Object o) (這裏是判斷兩個集合是否相等,那麼這裏的obj必須是集合才行,也要重寫元素所在類的equals()方法。對於有序集合,其位置也不必須相同,若爲無序集合,其位置可以不同,只需要元素相等即可。)
Collection coll3= Arrays.asList(123,"huhu","bb"); System.out.println(coll2.equals(coll3));
-
hashCode() (計算機hashCode哈希值的方法,返回值是int)
System.out.println(coll3.hashCode());
-
toArray() (集合轉數組)
Object[] objects = coll3.toArray(); for (Object o: objects) { System.out.println(o); }
拓展一下:數組轉集合(使用的時候注意 Arrays.asList(T)的參數是可變形參,根據不同需要看是寫int 還是其包裝類integer ,int會判斷成爲一個參數,integer爲兩個參數)
List list2 = Arrays.asList(new int[]{123, 455}); List list3 = Arrays.asList(new Integer[]{123, 455}); System.out.println(list2.size());//結果是1,判斷一個參數 System.out.println(list3.size());//結果是2,判斷2個參數
-
iterator()(迭代器接口:遍歷集合的時候使用)
Iterator iterator=coll2.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); }
拓展:內部定義了一個remove方法,可以刪除集合中的元素
-
foreach
foreach(Object obj: coll2){ System.out.println(obj); }
LIst----有序可重複(俗稱:動態數組)
他是一個接口,通常用List代替數組,有三個實現類ArrayList、LinkedList、Vector
三者的異同:ArrayList、LinkedList、Vector?
相同點:三個類都實現了List接口;存儲數據特點相同,有序,可重複
不同點:
ArrayList:作爲List接口的主要實現類,基本都用他,線程不安全,執行效率高,查詢快;底層使用的是Object[ ] elementData數組儲存數據
LikedList:對於頻繁的插入和刪除操作,使用LinkedList比ArrayList效率高,底層使用的是雙向鏈表儲存
Vector:List接口的古老實現類,很少用,JDK 1.0就有了,其他的都是1.2出現的,包括List,線程安全,執行效率低;底層是使用Object[ ] elementData儲存數據
ArrayList源碼分析(java.util包下):
JDK7:
JDK7在造對象的時候就給把底層的數組進行了初始化
ArrayList arraylist=new ArrayList();
JDK8:並沒有給原始數組初始化,節省內存空間,這樣的方式更好,在我們調用add方法的時候,我們的數組纔會被創建好
總結:
LinkedList源碼分析
是一個雙向鏈表,
import java.util.LinkedList;
public class LinkedListTest<E> {
int size;
transient MyNode first;
transient MyNode last;
private static class MyNode<E>{
private E element;
private MyNode prev;
private MyNode next;
public MyNode(MyNode prev, E ele, MyNode next){
this.element=ele;
this.prev=prev;
this.next=next;
}
}
//添加元素的方法,裏面有一個拼接的方法一個Node分爲三部分:【prev】【元素】【next】
public void add(E ele){
appendLast(ele);
}
//定義l節點爲last,
public void appendLast(E ele){
MyNode<E> l=last;
MyNode newNode=new MyNode(l,ele,null);
//last節點爲新節點
last=newNode;
//判斷新節點的【prev】是否爲null,是則表示爲該新節點爲【null】【ele】【null】
//表示新結點爲first
//否則,表示不是首節點,則把last的【next】指向新結點
if(l==null){
first=newNode;
}else {
l.next=newNode;
}
size++;
}
public void remove(E obj){
//刪除元素
//從頭開始遍歷,一直遍歷到結尾,只要沒到結尾就繼續遍歷
for (MyNode pos=first;pos!=null;pos=pos.next){
//判斷,當前元素pos是不是想要刪除的元素,是的就進行unlink方法,只要找到了就break
if (pos.element.equals(obj)){
unlink(pos);
break;
}
}
}
//這裏就是刪除元素的方法
private void unlink(MyNode<E> pos){
//首先定義當前pos元素【prev】【ele】【next】
E ele=pos.element;
MyNode<E> prev=pos.prev;
MyNode<E> next=pos.next;
//判斷當前元素的【prev】是不是空,如果是空,那就表示是first,爲第一個節點
//那就直接把第一個刪除,然後把pos後面的節點MyNode<E> next=pos.next 賦值給first
//並把當前元素pos的【next】變成null,就不會指向後面的結點了,就斷開連接了
if (prev==null){
first=next;
pos.next=null;
}else {
prev.next=next;
pos.prev=null;
}
//判斷當前元素是不是最後一個,如果是就刪除最後一個元素,即讓前一個元素的變成爲最後一個last結點
//否則把下一個元素的【prev】指向prev結點
if (next==null){
last=prev;
pos.prev=null;
}else {
next.prev=prev;
pos.next=null;
}
pos.element=null;
size--;
}
}
Set集合
是接口,存儲無序不重複的數據,Set接口繼承了Collection接口,同時沒有添加新的方法,所以在使用上與List接口的實現類使用方式一致。都是有增加元素的add方法和獲取元素個數的size方法
要求:
向set中添加數據,其所在類一定要重寫hashCode()和equals()方法;
重寫的hashCode()和equals()儘可能地保持一致:即相同對象必須擁有相同散列碼(相同對象要有一樣的哈希值)
具有三個實現類:
HashSet:作爲set接口的主要實現類,線程不安全:可以存儲null值
LinkedHashSet:是Hashset的子類,在Hashset基礎之上,添加了指針,遍歷其內部子類的時候,可以按照添加的順序遍歷。
TreeSet:使用紅黑樹存儲,要求放在TreeSet中的數據必須是同類型,可以按照添加對象的知道屬性進行排序
無序不可重複的理解:
1.無序性:解釋HashSet集合如何存放元素:
無序性:不等於隨機性。存儲的數據在數組中並非按照數組索引的順序進行添加。而是根據數據的hash值決定的。
當我們給HashSet中存放元素的時候,這時並不是直接把這個元素就存放到HashSet內部維護的數組中。
而是先根據當前要存放的那個元素,再結合一種算法(這個方法就是Object類中的hashCode方法),算出當前這個元素應該在數組中存儲的位置。
在存放數據的時候,如果計算出來的位置上已經有元素,這時還會去調用當前正要存放的這個元素的equals方法,把已經在計算出位置上的那個元素一起進行比較,如果equals方法返回的true,就丟棄當前正要存放的元素。如果equals方法返會的false,當前這個對象還要存儲。
2.不可重複性:HashSet集合是如何保證元素不重複
不可重複性:表示在hashset中的元素 是不可重複的
當給hashset中存放元素的時候會先調用對象的hashCode方法,計算哈希值,根據哈希值來決定當前對象在集合中的存儲位置。
在存儲的時候,如果遇到了哈希值相同的元素,這時集合的底層還會去調用當前對象的equals方法,判斷當前正要存放的對象和
位置上已經存在的對象是否是同一個對象,equals方法返回的true,就認爲相同對象,不保存,如果equals方法返回的false,當前對象已經會被保存
計算哈希值得時候減少衝突
鏈表與數組的區別:
-
對於在內存中數據的存放順序及位置:
數組中元素是按順序存放的,那麼在內存中也是按順序存放的,但是鏈表中元素存放的位置與內存中存放的順序及位置不一致;
2.對於擴容的速度:
鏈表的速度要快於數組,數據的擴容需要在內存在新申請一片內存空間,而鏈表直接擴就行
Map接口
存儲雙列數據:存儲key--value鍵值對的數據
一、實現類
HashMap:主要實現類;線程不安全,效率高,可以存儲null的key和value,key爲null可以使用方法,不會報空指針異常
LinkedHashMap---HashMap的子類;保證在遍歷map元素時,可以按照添加的順序實現遍歷。
原因:在原有的hashMap底層結構基礎上,添加一對指針,指向前一個和後一個元素。
對於頻繁的遍歷操作, 此類執行效率高於HashMap
TreeMap:可以按照添加的key-value進行排序,實現遍歷,按照key進行排序
底層使用紅黑樹進行排序
Hashtable----是古老實現類;線程安全,效率低,不能存儲null的key和value,key爲null可以使用方法,會報空指針異常
properties---Hashtable的子類,常用來處理配置文件,key和value嗾使String類型
線程安全
面試題:
1.(高頻)HashMap底層實現原理
2.HashMap和Hashtable的異同
二、MAP結構的理解:
Map中 的key:無序的、不可重複,使用set存儲所有的key,
要求:key所在的類要重寫equals()和hashCode()
Map中的value:無序的、可重複的,使用Collection存儲所有的value
要求:value所在的類要重寫equals()方法
一個鍵值對:key-value構成了一個Entry,key、value就是entry的屬性
Map中的entry:無序的,不可重複的,使用set存儲所有的entry
三、HashMap的底層原理
哈希衝突:
解決方法1:再哈希法===再次計算哈希值,直到每一個元素都有唯一的索引(位置)爲止
解決方法2:鏈地址法===使用鏈表
JDK7
import java.util.HashMap;
public class HashMapTest <K,V>{
private Entry<K,V>[]table;
private static final Integer INITCAPACITY=8;
private int size;
public HashMapTest(){
table=new Entry[INITCAPACITY];
}
private int size(){
return size;
}
private V get(Object key){
int hash=key.hashCode();
int i=hash%table.length;
for (Entry<K,V> entry=table[i];entry!=null;entry=entry.next){
if (entry.k.equals(key)){
return entry.v;
}
}
return null;
}
private V put(K key,V value){
int hash=key.hashCode();
int i=hash%table.length;
for (Entry<K,V> entry=table[i];entry!=null;entry=entry.next){
if (entry.k.equals(key)){
V oldvalue=entry.v;
entry.v=value;
return oldvalue;
}
}
addEntry(key, value, i);
return null;
}
private void addEntry(K key, V value, int i) {
Entry<K,V> entry=new Entry<K,V>(key,value,table[i]);
table[i]=entry;
size++;
}
class Entry<K,V>{
private K k;
private V v;
private Entry<K,V>next;
public Entry() {
}
public Entry(K k, V v, Entry<K, V> next) {
this.k = k;
this.v = v;
this.next = next;
}
public K getK() {
return k;
}
public V getV() {
return v;
}
}
public static void main(String[] args) {
HashMapTest<Integer ,String>hashMapTest=new HashMapTest<Integer, String>();
for(int i=0;i<10;i++){
hashMapTest.put(i,"結束"+i);
System.out.println(hashMapTest.get(i));
}
}
}