第一章 Java集合框架 Java集合框架(一)-ArrayList
-----------------------------------------------------------------------------
Java集合框架(一)-ArrayList
大佬理解->Java集合之LinkedList
1、ArrayList的特點
存放的元素有序 |
---|
元素不唯一(可以重複) |
隨機訪問快 |
插入刪除元素慢 |
非執行緒安全 |
2、底層實現
底層初始化,使用一個Object型別的空物件陣列,初始長度為0;
原始碼
//Object型別物件陣列引用
transient Object[] elementData;
//預設空的Object陣列
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//例項化時,將Object型別物件陣列引用 指向 預設空的Object陣列
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
首次新增元素,自動進行擴容,預設擴充容量是10(陣列的長度,也就是集合存放元素的個數);
原始碼
//如果是第一次新增元素
public boolean add(E e) {
//private int size; //size = 0;
//呼叫ensureCapacityInternal(int minCapacity)方法
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
//minCapacity = 1;
private void ensureCapacityInternal(int minCapacity) {
//呼叫calculateCapacity(Object[] elementData, int minCapacity)方法
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//判斷是不是預設空的Object陣列
//如果是進入選擇一個數組容量
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//private static final int DEFAULT_CAPACITY = 10;
//minCapacity = 1;
//所以第一次新增元素時,自動進行擴容,預設擴充容量是10
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
3、擴容
- 當前一次擴容的陣列容量不足時(放滿10個元素,再想新增一個元素,容量不足),開始進行動態擴容;
- 每次擴容,是之前一次擴容後的陣列容量的1.5倍(即:每次都在前一次陣列容量的基礎上,增加一半-右移1位);
- 最大容量Integer.MAX_VALUE - 8,即2^31-8;
//擴容方法
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length; //舊陣列的容量
int newCapacity = oldCapacity + (oldCapacity >> 1); //新陣列的容量 = 老陣列的容量+老陣列的一半(右移一位)
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0) //如果新陣列的容量大於最大值,將陣列的容量設定為Integer.MAX_VALUE - 8
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
//private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
4、ArrayList初始化
基於多型建立ArrayList集合物件
List<Object> list = new ArrayList<>(); // 推薦
Collection collection = new ArrayList();
ArrayList arrayList = new ArrayList();
List<Integer> intList = new ArrayList<>(); //可以使用泛型,指定存放資料的型別
5、常用方法
方法 | 說明 |
---|---|
add(Object obj) | 新增元素 |
add(int index, E element) | 指定下標新增元素 |
remove(int index) | 移除指定 下標 |
remove(Object o) | 移除指定 元素 |
get(int index)) | 獲取元素 |
size() | 集合元素個數 |
contains(Object o) | 是否包含某元素 |
isEmpty() | 集合是否為空 |
5.1 add(Object obj)
//新增元素方法:add(Object obj),每次新增元素都是自動新增到陣列的末尾,元素下標值從0開始,跟陣列一致;
//可以新增重複值;
//可以新增null值;
5.2 add(int index, E element)
//指定下標新增元素和刪除元素,執行效率比較低;
5.3 remove(int index)
// 先檢查該索引是否合法,不合法會丟擲索引越界異常;
//再把要刪除的元素拿出來作為返回值,期間會計算要刪除的元素後面還有多少個元素,再呼叫System.arraycopy(elementData, index+1, elementData, index, numMoved);移動陣列元素,這個方法是一個本地方法,將一個數組從指定位置複製到另外一個數組的指定位置。
//然後讓最後一個元素變成null,將陣列大小減一,不置為null會存在引用,無法被GC回收,造成記憶體洩漏。
原始碼分析
//刪除指定索引位置元素
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);
//讓最後一個元素變成null
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
5.4 remove(Object o)
//刪除指定元素和上一個方法類似,主要是這個需要先找到指定元素的索引位置,找不到直接返回false,找到再呼叫內部的刪除方法fastRemove(int index)來刪除;
原始碼分析
//刪除指定元素
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
//內部的刪除方法
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
注意
:使用remove(Object o)移除元素的時候,如果存在多個元素值一樣,只會移除一個;
特殊案例1,使用for迴圈遍歷刪除元素:
@Test
public void testRemoveObject(){
List nums = new ArrayList();
nums.add(1);
nums.add(1);
nums.add(1);
for (int i = 0; i < nums.size(); i++) {
if(nums.get(i).equals(1)){
nums.remove(i);
System.out.println("刪除第"+i+"個1");
//第一個1被刪除
//第二個1,由於第一個1刪除後,往前移動了,所以第二次遍歷的是第三個1
}
}
System.out.println("刪除後的集合:"+nums);
/*
刪除第0個1
刪除第1個1
刪除後的集合:[1] 刪除還有遺漏
*/
}
特殊案例2使用 遍歷刪除元素:
@Test
public void testRemoveObject2(){
List nums = new ArrayList();
nums.add(1);
nums.add(1);
nums.add(1);
Iterator iterator = nums.iterator();
int i = 0;
while (iterator.hasNext()){
if(iterator.next().equals(new Integer(1))){
iterator.remove();
System.out.println("刪除第"+i+"個1"); i++;
}
}
System.out.println("刪除後的集合:"+nums);
/*
刪除第0個1
刪除第1個1
刪除第2個1
刪除後的集合:[] 刪除成功
*/
}
5.5get(int index))
// 獲取元素方法:get(下標值),只能通過下標取值;
//當訪問下標值超出了集合元素的最大下標值,報下標越界異常:java.lang.IndexOutOfBoundsException
// 可用的下標值的範圍:最小值是0,最大值是集合元素的個數 - 1
5.6 size()
// 獲取集合中元素個數方法:size();
5.7 contains(Object o)
// 判斷list集合中,是否包含某個元素方法:contains(查詢元素值),返回true,代表存在,返回false,代表不存在;
5.8 isEmpty()
// 判斷list集合是否為空方法:isEmpty(),返回true代表沒有元素,空的,返回false,代表有元素,不是空的
// 底層就是通過集合中元素個數size == 0 判斷,所以也可以使用size() == 0判斷集合非空
原始碼
public boolean isEmpty() {
return size == 0;
}
5.9 clear()
//清空list集合方法:clear(),清除集合中的所有元素
原始碼
ublic void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++) //一次將陣列賦值為null;
elementData[i] = null;
size = 0; //設定陣列長度為0;
}
5.10 toArray()
// list集合一步轉換為陣列方法:toArray(),返回的是Object型別陣列
6、陣列轉換成集合
Arrays.asList(目標陣列)
String[] strArrays = {"奧迪", "賓士", "寶馬"};
List<String> strList1 = Arrays.asList(strArrays);
System.out.println(strList1); //[奧迪, 賓士, 寶馬]
7、遍歷
List<String> strList = new ArrayList<>();
strList.add("Audi");
strList.add("Benz");
strList.add("Bmw");
strList.add("Audi");
//for迴圈
for (int i = 0; i < strList.size(); i++) {
System.out.println("汽車品牌:" + strList.get(i));
}
//迭代器
//Iterator迭代器,只能通過集合獲取,不可以重複使用,迭代結束,迭代器就失效,如果想再次使用,需要重新獲取
Iterator<String> iterator = strList.iterator();
// 迭代器遍歷,使用while,不知道其中元素個數
while(iterator.hasNext()){
System.out.println("汽車品牌:" + iterator.next());
}
執行結果:
汽車品牌:Audi
汽車品牌:Benz
汽車品牌:Bmw
汽車品牌:Audi
8、Vector(執行緒安全)
- Vector,底層資料結構是和ArrayList一致的,都是物件陣列,但是它的操作是執行緒安全的,每個方法都帶有synchronized同步;
- 預設初始容量是10,可以自定義,但是不能小於0,預設每次擴容是前一次容量的一倍,擴容的數量也是可以指定的,如果指定,每次都是在前一次基礎上擴容指定的數量
-----------------------------------------------------------------------------------------------
1、LinkedList的特點
存放的元素有序 |
---|
元素不唯一(可以重複) |
隨機訪問慢 |
插入刪除元素快 |
非執行緒安全 |
2、底層實現
底層實現是連結串列結構(雙向連結串列),插入和刪除元素的效率高(遍歷元素和隨機訪問元素的效率低);
底層使用Node雙向連結串列實現的
private static class Node<E> {
E item; //元素值
Node<E> next; //下一個元素引用
Node<E> prev; //上一個元素引用
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
3、LinkedList初始化
// LinkedList包含首尾操作的特有方法,在list介面中沒有定義,不推薦多型建立集合物件
// List<String> strList = new LinkedList<>();
// strList.addFirst();
LinkedList<String> linkedList = new LinkedList<>();
4、常用方法
方法 | 說明 |
---|---|
add(E e) | 新增元素(元素新增在連結串列末尾) |
addFirst(E e) | 在連結串列首新增元素 |
addLast(E e) | 在連結串列末尾新增元素 |
getFirst() | 獲取第一個元素 |
getLast() | 獲取最後一個元素 |
removeFirst() | 移除第一個元素 |
removeLast() | 移除最後一個元素 |
size() | 獲取元素的總數 |
get(int index) | 根據元素下標獲取元素值 |
4.1 add(E e)
新增元素的普通方法:add(元素),將元素自動新增到連結串列的末尾
4.2 addFirst(E e)
addFirst(E e)新增到連結串列首部
4.3 addLast(E e)
addLast(E e)新增到連結串列首部
4.4 getFirst()
getFirst() 獲取第一個元素
4.5 getLast()
getLast() 獲取最後一個元素
4.7 removeFirst()
removeFirst() 刪除第一個元素
4.8 removeLast()
removeLast() 刪除最後一個元素
4.9 size()
size() 獲取集合中元素個數方法
4.10 get(int index)
1)獲取的下標值必須是在有效的範圍內:從0到元素個數-1之間,否則報錯:IndexOutOfBoundsException;
2)如果下標值在可以有效的範圍內,自動進行二分法移動指標,先判斷目標下標值在元素個數一半的前半段還是後半段;
如果在前半段,自動從第一個節點開始,依次向後移動指標,直到找到下標位置,返回對應元素;
如果在後半段,自動從最後一個節點,依次向前移動指標,直到找到指定下標位置,返回對應元素;
所以:當下標位置越接近元素個數一半值(越靠近中間位置),效率是最低的,可以看出:遍歷和隨機訪問的效率低;
原始碼
public E get(int index) {
checkElementIndex(index); //判斷下標是否大於集合元素總數
return node(index).item; //返回結點值(元素)
}
//一個一個的往後找到指定的節點並返回
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
5、ArrayList 和 LinkedList的區別
相同點
都是list的實現類,存放的元素都是有序,不唯一的,都是執行緒不安全的
不同點
1)底層實現不同:ArrayList底層實現是Object物件陣列,而LinkedList底層是雙向連結串列結構;
2)擴容機制不同:ArrayList預設是空的Object物件陣列(也可以指定大於0的初識容量),首次自動擴容為10個長度,後續每次擴容原來容量的1.5倍,LinkedList底層是連結串列,沒有容量,沒有擴容,存放元素沒限制;
3)使用場景不同:ArrayList適用於快速的遍歷和隨機訪問元素,而LinkedList適用於快速的新增刪除元素;
-----------------------------------------------------------------------------------------------
1、HashSet特點
存放的元素是無序的(不保證新增元素的順序) |
---|
元素唯一(不可以重複) |
可以存null,但是隻能存放1個 |
雖然set集合不保證新增元素的順序,但是集合中存放的元素順序其實是固定的,根據元素的hash值確定的順序 |
2、HashSet原理分析
HashSet底層,是藉助HashMap實現的;
3、HashSet初始化
Set<String> strSet = new HashSet<>();
4、HashSet常用方法
方法 | 說明 |
---|---|
size() | 結合元素個數 |
contains(Object o) | 集合是否包含某個元素 |
4.1 size()
// 獲取set集合元素個數方法:size()
4.2 contains(Object o)
// 判斷set集合中是否包含某個元素方法:contains(元素)
4.3 list的其它常用方法,set中也有,不再介紹
5、HashSet遍歷
5.1 迭代器遍歷
Set<String> carSet = new HashSet<>();
carSet.add("Bmw325");
carSet.add("BenzC200");
carSet.add("AudiA4");
// 方式一:迭代器遍歷
Iterator<String> iterator = carSet.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
5.2 增強for迴圈
// 方式二:增強for迴圈
for (String car : carSet) {
System.out.println(car);
}
執行結果:
BenzC200
AudiA4
Bmw325
6、HashSet集合是如何確定元素唯一的
6.1 HashSet新增一個元素的過程
-
呼叫物件的hashCode()方法獲取物件的雜湊值;
-
根據物件的雜湊值計算物件的儲存位置;
-
判斷該位置是否有元素,如果沒有元素則將元素儲存到該位置;如果有元素則遍歷該位置的所有元素,和新存入的元素比較雜湊值是否相同,如果都不相同則將元素儲存到該位置;如果有相同的,則呼叫equals()方法比較物件內容是否相等;
-
如果返回false則將元素儲存到該位置,如果返回true則說明元素重複,不儲存;
6.2 流程圖
HashSet集合儲存元素:要保證元素唯一性,需要重寫hashCode()和equals()方法。
-----------------------------------------------------------------------------------------------
1、HashMap特點
存放的元素都是鍵值對(key-value),key是唯一的,value是可以重複的 |
---|
存放的元素也不保證新增的順序,即是無序的 |
存放的元素的鍵可以為null,但是隻能有一個key為null,可以有多個value為null(前提是存放的是HasHap物件) |
如果新新增的元素的鍵(key)在集合中已經存在,自動將新新增的值覆蓋到原有的值 |
2、底層實現
HashMap的底層使用的是Node物件陣列;
HashMap原始碼
transient Node<K,V>[] table; //Node物件陣列
//Node類
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
......
}
3、擴容
- HashMap的底層使用的是Node物件陣列,初始容量(未自定義)是16,根據負載因子跟陣列容量,計算出擴容臨界值,每當存放元素達到了臨界值就可以擴容,而不是等到陣列長度不夠;
- 每次擴容,都是原有陣列容量的2倍,必須要保證是2的整數次冪(底層演算法實現),最大容量是2的30次方;
初始容量和預設擴容因子
/**
* Constructs an empty <tt>HashMap</tt> with the default initial capacity
* (16) and the default load factor (0.75).
*/
//初始容量為16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
//預設擴容因子為0.75
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//最大容量是2的30次方
static final int MAXIMUM_CAPACITY = 1 << 30;
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
4、初始化
Map<String,String> carMap = new HashMap<>(); //推薦使用
5、常用方法
put(key, value) | 新增鍵值對 |
---|---|
get(Object key) | 通過key獲取value |
size() | 獲取集合鍵值對數量 |
keySet() | 獲取所有的鍵集合(返回值為set集合) |
values() | 獲取所有值集合 |
containsKey(Object key) | 判斷某個鍵是否存在 |
containsValue(Object value) | 判斷某個值是否存在某個值 |
remove(Object key) | 根據鍵值刪除鍵值對 |
clear() | 清空集合 |
5.1 put(key, value);
新增鍵值對方法;
可以新增 null 的key 或者value,鍵只能由一個null,值可以由多個null;
5.2 get(Object key)
獲取鍵值對的方法:get(key),只能根據key獲取value,如果key不存在,不會報錯,返回null;
5.3 size()
獲取集合中存放鍵值對數量;
5.4 keySet()
獲取所有的鍵集合;
Map<String,String> carMap = new HashMap<>();
carMap.put("Audi","奧迪");
carMap.put("Benz","賓士");
carMap.put("Bmw","寶馬");
Set<String> keySet = carMap.keySet();
System.out.println("獲取所有的鍵集合:"+keySet);//[Benz, Audi, Bmw]
5.5 values()
獲取所有值集合方法;
Collection<String> values = carMap.values();
System.out.println(values);//[賓士, 奧迪, 寶馬]
5.6 containsKey(Object key)
判斷集合中是否包含某個鍵值對,存在返回true;
5.7 containsValue(Object value)
判斷集合中是否包含某個值,不可以作為鍵值對的唯一標識,值可重複;
5.8 remove(Object key)
刪除鍵值對方法;
5.9 clear()
清空map集合;
6、遍歷
6.1 方式一:迭代器(不可以通過map集合直接獲取,因為它只能通過Collection獲取)
System.out.println("方式一");
Iterator<String> iterator = carKeySet.iterator();
while (iterator.hasNext()){
//獲取key
String carKey = iterator.next();
//根據key 獲取值
String carValue = carMap.get(carKey);
System.out.print(carKey + "---" + carValue +" ");
}
6.2 方式二:增強for,原理和上一個類似,也根據鍵的集合,獲取值
System.out.println("\n"+"方式二");
for (String carKey : carMap.keySet()) {
System.out.print(carKey+"---"+carMap.get(carKey)+" ");
}
6.3 方式三:增強for,操作的是Map.Entry物件,推薦寫法,效率較高
System.out.println("\n"+"方式三");
for (Map.Entry<String,String> entry : carMap.entrySet()){
System.out.print(entry.getKey()+"---"+entry.getValue()+" ");
}
執行結果
Benz---賓士 Audi---奧迪 Bmw---寶馬
7、TreeMap
自帶排序功能的集合map,TreeMap,自動按照key的字典序排序;
System.out.println("自帶排序功能的集合map,TreeMap,自動按照key的字典序排序");
Map<String,String> paramsMap = new TreeMap<>();
paramsMap.put("body","TreeMap");
paramsMap.put("userId","U0001");
paramsMap.put("sign","sign");
paramsMap.put("appId","KH96");
System.out.println(paramsMap);
自帶排序功能的集合map,TreeMap,自動按照key的字典序排序
{appId=KH96, body=TreeMap, sign=sign, userId=U0001}
8、HashTable
Hashtable,是map集合的實現類,但是跟HashMap的去表是,執行緒安全的;
Hashtable的put方法原始碼
//put方法是同步安全的
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
預設初始容量是11,擴容因子也是0.75;
Hashtable初始化原始碼
/**
* Constructs a new, empty hashtable with a default initial capacity (11)
* and load factor (0.75).
*/
//預設初始容量是11
//擴容因子也是0.75
public Hashtable() {
this(11, 0.75f);
}
每次擴容是之前容量的2倍+1;
Hashtable擴容原始碼
protected void rehash() {
int oldCapacity = table.length;
Entry<?,?>[] oldMap = table;
// 新陣列的容量=舊陣列長度*2+1
int newCapacity = (oldCapacity << 1) + 1;
// 保證新陣列的大小永遠小於等於MAX_ARRAY_SIZE
// MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8
if (newCapacity - MAX_ARRAY_SIZE > 0) {
if (oldCapacity == MAX_ARRAY_SIZE)
return;
newCapacity = MAX_ARRAY_SIZE;
}
// 建立新陣列
Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];
modCount++;
// 計算新的臨界值
threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
table = newMap;
// 將舊陣列中的元素遷移到新陣列中
for (int i = oldCapacity ; i-- > 0 ;) {
for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
Entry<K,V> e = old;
old = old.next;
//計算新陣列下標
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
// 頭插法的方式遷移舊陣列的元素
e.next = (Entry<K,V>)newMap[index];
newMap[index] = e;
}
}
}
-----------------------------------------------------------------------------------------------
1、Collections
sort(List list) | 自然升序排序 |
---|---|
reverse(List<?> list) | 集合反轉 |
binarySearch(List<? extends Comparable<? super T>> list, T key) | 二分查詢(要求集合有序) |
addAll(Collection<? extends E> c) | 從集合中新增批量元素 |
max(Collection<? extends T> coll) | 集合最大元素 |
min(Collection<? extends T> coll) | 集合最小元素 |
1.1 sort(List list)
自然升序;
1.2 reverse(List<?> list)
集合反轉;
1.3 binarySearch(List<? extends Comparable<? super T>> list, T key)
二分查詢(要求集合有序);
1.4 addAll(Collection<? extends E> c)
從集合中新增批量元素;
1.5 max(Collection<? extends T> coll)
集合中最大元素;
1.6 min(Collection<? extends T> coll)
集合中最小元素;
1.7 同步控制
Collections工具類中提供了多個synchronizedXxx方法,該方法返回指定集合物件對應的同步物件,從而解決多執行緒併發訪問集合時執行緒的安全問題。HashSet、ArrayList、HashMap都是執行緒不安全的,如果需要考慮同步,則使用這些方法。這些方法主要有:synchronizedSet、synchronizedSortedSet、synchronizedList、synchronizedMap、synchronizedSortedMap。
2、泛型
泛型就相當於是型別模板,指定什麼樣的型別,對應的值就是什麼型別,通常泛型給你引數T,E,K,V等,推薦使用T(Type);
自定義泛型舉例
Studnet類
public class Student<T1,T2,T3>{
private T1 name;
private T2 age;
private T3 sex;
public Student() {
}
public Student(T1 name, T2 age, T3 sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
//省略get,set方法
@Override
public String toString() {
return "Student{" +
"name=" + name +
", age=" + age +
", sex=" + sex +
'}';
}
}
測試
public class TestStudnet {
public static void main(String[] args) {
//在例項化的時候可以對型別進行約束
Student<String, Integer, Character> student = new Student<>();
student.setName("二狗");
student.setAge(18);
student.setSex('男');
System.out.println(student);
}
}
執行結果
Student{name=二狗, age=18, sex=男}
-----------------------------------------------------------------------------------------------
大佬理解->Java集合之LinkedList
1、ArrayList的特點
存放的元素有序 |
---|
元素不唯一(可以重複) |
隨機訪問快 |
插入刪除元素慢 |
非執行緒安全 |
2、底層實現
底層初始化,使用一個Object型別的空物件陣列,初始長度為0;
原始碼
//Object型別物件陣列引用
transient Object[] elementData;
//預設空的Object陣列
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//例項化時,將Object型別物件陣列引用 指向 預設空的Object陣列
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
首次新增元素,自動進行擴容,預設擴充容量是10(陣列的長度,也就是集合存放元素的個數);
原始碼
//如果是第一次新增元素
public boolean add(E e) {
//private int size; //size = 0;
//呼叫ensureCapacityInternal(int minCapacity)方法
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
//minCapacity = 1;
private void ensureCapacityInternal(int minCapacity) {
//呼叫calculateCapacity(Object[] elementData, int minCapacity)方法
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//判斷是不是預設空的Object陣列
//如果是進入選擇一個數組容量
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//private static final int DEFAULT_CAPACITY = 10;
//minCapacity = 1;
//所以第一次新增元素時,自動進行擴容,預設擴充容量是10
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
3、擴容
- 當前一次擴容的陣列容量不足時(放滿10個元素,再想新增一個元素,容量不足),開始進行動態擴容;
- 每次擴容,是之前一次擴容後的陣列容量的1.5倍(即:每次都在前一次陣列容量的基礎上,增加一半-右移1位);
- 最大容量Integer.MAX_VALUE - 8,即2^31-8;
//擴容方法
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length; //舊陣列的容量
int newCapacity = oldCapacity + (oldCapacity >> 1); //新陣列的容量 = 老陣列的容量+老陣列的一半(右移一位)
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0) //如果新陣列的容量大於最大值,將陣列的容量設定為Integer.MAX_VALUE - 8
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
//private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
4、ArrayList初始化
基於多型建立ArrayList集合物件
List<Object> list = new ArrayList<>(); // 推薦
Collection collection = new ArrayList();
ArrayList arrayList = new ArrayList();
List<Integer> intList = new ArrayList<>(); //可以使用泛型,指定存放資料的型別
5、常用方法
方法 | 說明 |
---|---|
add(Object obj) | 新增元素 |
add(int index, E element) | 指定下標新增元素 |
remove(int index) | 移除指定 下標 |
remove(Object o) | 移除指定 元素 |
get(int index)) | 獲取元素 |
size() | 集合元素個數 |
contains(Object o) | 是否包含某元素 |
isEmpty() | 集合是否為空 |
5.1 add(Object obj)
//新增元素方法:add(Object obj),每次新增元素都是自動新增到陣列的末尾,元素下標值從0開始,跟陣列一致;
//可以新增重複值;
//可以新增null值;
5.2 add(int index, E element)
//指定下標新增元素和刪除元素,執行效率比較低;
5.3 remove(int index)
// 先檢查該索引是否合法,不合法會丟擲索引越界異常;
//再把要刪除的元素拿出來作為返回值,期間會計算要刪除的元素後面還有多少個元素,再呼叫System.arraycopy(elementData, index+1, elementData, index, numMoved);移動陣列元素,這個方法是一個本地方法,將一個數組從指定位置複製到另外一個數組的指定位置。
//然後讓最後一個元素變成null,將陣列大小減一,不置為null會存在引用,無法被GC回收,造成記憶體洩漏。
原始碼分析
//刪除指定索引位置元素
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);
//讓最後一個元素變成null
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
5.4 remove(Object o)
//刪除指定元素和上一個方法類似,主要是這個需要先找到指定元素的索引位置,找不到直接返回false,找到再呼叫內部的刪除方法fastRemove(int index)來刪除;
原始碼分析
//刪除指定元素
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
//內部的刪除方法
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
注意
:使用remove(Object o)移除元素的時候,如果存在多個元素值一樣,只會移除一個;
特殊案例1,使用for迴圈遍歷刪除元素:
@Test
public void testRemoveObject(){
List nums = new ArrayList();
nums.add(1);
nums.add(1);
nums.add(1);
for (int i = 0; i < nums.size(); i++) {
if(nums.get(i).equals(1)){
nums.remove(i);
System.out.println("刪除第"+i+"個1");
//第一個1被刪除
//第二個1,由於第一個1刪除後,往前移動了,所以第二次遍歷的是第三個1
}
}
System.out.println("刪除後的集合:"+nums);
/*
刪除第0個1
刪除第1個1
刪除後的集合:[1] 刪除還有遺漏
*/
}
特殊案例2使用 遍歷刪除元素:
@Test
public void testRemoveObject2(){
List nums = new ArrayList();
nums.add(1);
nums.add(1);
nums.add(1);
Iterator iterator = nums.iterator();
int i = 0;
while (iterator.hasNext()){
if(iterator.next().equals(new Integer(1))){
iterator.remove();
System.out.println("刪除第"+i+"個1"); i++;
}
}
System.out.println("刪除後的集合:"+nums);
/*
刪除第0個1
刪除第1個1
刪除第2個1
刪除後的集合:[] 刪除成功
*/
}
5.5get(int index))
// 獲取元素方法:get(下標值),只能通過下標取值;
//當訪問下標值超出了集合元素的最大下標值,報下標越界異常:java.lang.IndexOutOfBoundsException
// 可用的下標值的範圍:最小值是0,最大值是集合元素的個數 - 1
5.6 size()
// 獲取集合中元素個數方法:size();
5.7 contains(Object o)
// 判斷list集合中,是否包含某個元素方法:contains(查詢元素值),返回true,代表存在,返回false,代表不存在;
5.8 isEmpty()
// 判斷list集合是否為空方法:isEmpty(),返回true代表沒有元素,空的,返回false,代表有元素,不是空的
// 底層就是通過集合中元素個數size == 0 判斷,所以也可以使用size() == 0判斷集合非空
原始碼
public boolean isEmpty() {
return size == 0;
}
5.9 clear()
//清空list集合方法:clear(),清除集合中的所有元素
原始碼
ublic void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++) //一次將陣列賦值為null;
elementData[i] = null;
size = 0; //設定陣列長度為0;
}
5.10 toArray()
// list集合一步轉換為陣列方法:toArray(),返回的是Object型別陣列
6、陣列轉換成集合
Arrays.asList(目標陣列)
String[] strArrays = {"奧迪", "賓士", "寶馬"};
List<String> strList1 = Arrays.asList(strArrays);
System.out.println(strList1); //[奧迪, 賓士, 寶馬]
7、遍歷
List<String> strList = new ArrayList<>();
strList.add("Audi");
strList.add("Benz");
strList.add("Bmw");
strList.add("Audi");
//for迴圈
for (int i = 0; i < strList.size(); i++) {
System.out.println("汽車品牌:" + strList.get(i));
}
//迭代器
//Iterator迭代器,只能通過集合獲取,不可以重複使用,迭代結束,迭代器就失效,如果想再次使用,需要重新獲取
Iterator<String> iterator = strList.iterator();
// 迭代器遍歷,使用while,不知道其中元素個數
while(iterator.hasNext()){
System.out.println("汽車品牌:" + iterator.next());
}
執行結果:
汽車品牌:Audi
汽車品牌:Benz
汽車品牌:Bmw
汽車品牌:Audi
8、Vector(執行緒安全)
- Vector,底層資料結構是和ArrayList一致的,都是物件陣列,但是它的操作是執行緒安全的,每個方法都帶有synchronized同步;
- 預設初始容量是10,可以自定義,但是不能小於0,預設每次擴容是前一次容量的一倍,擴容的數量也是可以指定的,如果指定,每次都是在前一次基礎上擴容指定的數量
-----------------------------------------------------------------------------------------------
1、LinkedList的特點
存放的元素有序 |
---|
元素不唯一(可以重複) |
隨機訪問慢 |
插入刪除元素快 |
非執行緒安全 |
2、底層實現
底層實現是連結串列結構(雙向連結串列),插入和刪除元素的效率高(遍歷元素和隨機訪問元素的效率低);
底層使用Node雙向連結串列實現的
private static class Node<E> {
E item; //元素值
Node<E> next; //下一個元素引用
Node<E> prev; //上一個元素引用
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
3、LinkedList初始化
// LinkedList包含首尾操作的特有方法,在list介面中沒有定義,不推薦多型建立集合物件
// List<String> strList = new LinkedList<>();
// strList.addFirst();
LinkedList<String> linkedList = new LinkedList<>();
4、常用方法
方法 | 說明 |
---|---|
add(E e) | 新增元素(元素新增在連結串列末尾) |
addFirst(E e) | 在連結串列首新增元素 |
addLast(E e) | 在連結串列末尾新增元素 |
getFirst() | 獲取第一個元素 |
getLast() | 獲取最後一個元素 |
removeFirst() | 移除第一個元素 |
removeLast() | 移除最後一個元素 |
size() | 獲取元素的總數 |
get(int index) | 根據元素下標獲取元素值 |
4.1 add(E e)
新增元素的普通方法:add(元素),將元素自動新增到連結串列的末尾
4.2 addFirst(E e)
addFirst(E e)新增到連結串列首部
4.3 addLast(E e)
addLast(E e)新增到連結串列首部
4.4 getFirst()
getFirst() 獲取第一個元素
4.5 getLast()
getLast() 獲取最後一個元素
4.7 removeFirst()
removeFirst() 刪除第一個元素
4.8 removeLast()
removeLast() 刪除最後一個元素
4.9 size()
size() 獲取集合中元素個數方法
4.10 get(int index)
1)獲取的下標值必須是在有效的範圍內:從0到元素個數-1之間,否則報錯:IndexOutOfBoundsException;
2)如果下標值在可以有效的範圍內,自動進行二分法移動指標,先判斷目標下標值在元素個數一半的前半段還是後半段;
如果在前半段,自動從第一個節點開始,依次向後移動指標,直到找到下標位置,返回對應元素;
如果在後半段,自動從最後一個節點,依次向前移動指標,直到找到指定下標位置,返回對應元素;
所以:當下標位置越接近元素個數一半值(越靠近中間位置),效率是最低的,可以看出:遍歷和隨機訪問的效率低;
原始碼
public E get(int index) {
checkElementIndex(index); //判斷下標是否大於集合元素總數
return node(index).item; //返回結點值(元素)
}
//一個一個的往後找到指定的節點並返回
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
5、ArrayList 和 LinkedList的區別
相同點
都是list的實現類,存放的元素都是有序,不唯一的,都是執行緒不安全的
不同點
1)底層實現不同:ArrayList底層實現是Object物件陣列,而LinkedList底層是雙向連結串列結構;
2)擴容機制不同:ArrayList預設是空的Object物件陣列(也可以指定大於0的初識容量),首次自動擴容為10個長度,後續每次擴容原來容量的1.5倍,LinkedList底層是連結串列,沒有容量,沒有擴容,存放元素沒限制;
3)使用場景不同:ArrayList適用於快速的遍歷和隨機訪問元素,而LinkedList適用於快速的新增刪除元素;
-----------------------------------------------------------------------------------------------
1、HashSet特點
存放的元素是無序的(不保證新增元素的順序) |
---|
元素唯一(不可以重複) |
可以存null,但是隻能存放1個 |
雖然set集合不保證新增元素的順序,但是集合中存放的元素順序其實是固定的,根據元素的hash值確定的順序 |
2、HashSet原理分析
HashSet底層,是藉助HashMap實現的;
3、HashSet初始化
Set<String> strSet = new HashSet<>();
4、HashSet常用方法
方法 | 說明 |
---|---|
size() | 結合元素個數 |
contains(Object o) | 集合是否包含某個元素 |
4.1 size()
// 獲取set集合元素個數方法:size()
4.2 contains(Object o)
// 判斷set集合中是否包含某個元素方法:contains(元素)
4.3 list的其它常用方法,set中也有,不再介紹
5、HashSet遍歷
5.1 迭代器遍歷
Set<String> carSet = new HashSet<>();
carSet.add("Bmw325");
carSet.add("BenzC200");
carSet.add("AudiA4");
// 方式一:迭代器遍歷
Iterator<String> iterator = carSet.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
5.2 增強for迴圈
// 方式二:增強for迴圈
for (String car : carSet) {
System.out.println(car);
}
執行結果:
BenzC200
AudiA4
Bmw325
6、HashSet集合是如何確定元素唯一的
6.1 HashSet新增一個元素的過程
-
呼叫物件的hashCode()方法獲取物件的雜湊值;
-
根據物件的雜湊值計算物件的儲存位置;
-
判斷該位置是否有元素,如果沒有元素則將元素儲存到該位置;如果有元素則遍歷該位置的所有元素,和新存入的元素比較雜湊值是否相同,如果都不相同則將元素儲存到該位置;如果有相同的,則呼叫equals()方法比較物件內容是否相等;
-
如果返回false則將元素儲存到該位置,如果返回true則說明元素重複,不儲存;
6.2 流程圖
HashSet集合儲存元素:要保證元素唯一性,需要重寫hashCode()和equals()方法。
-----------------------------------------------------------------------------------------------
1、HashMap特點
存放的元素都是鍵值對(key-value),key是唯一的,value是可以重複的 |
---|
存放的元素也不保證新增的順序,即是無序的 |
存放的元素的鍵可以為null,但是隻能有一個key為null,可以有多個value為null(前提是存放的是HasHap物件) |
如果新新增的元素的鍵(key)在集合中已經存在,自動將新新增的值覆蓋到原有的值 |
2、底層實現
HashMap的底層使用的是Node物件陣列;
HashMap原始碼
transient Node<K,V>[] table; //Node物件陣列
//Node類
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
......
}
3、擴容
- HashMap的底層使用的是Node物件陣列,初始容量(未自定義)是16,根據負載因子跟陣列容量,計算出擴容臨界值,每當存放元素達到了臨界值就可以擴容,而不是等到陣列長度不夠;
- 每次擴容,都是原有陣列容量的2倍,必須要保證是2的整數次冪(底層演算法實現),最大容量是2的30次方;
初始容量和預設擴容因子
/**
* Constructs an empty <tt>HashMap</tt> with the default initial capacity
* (16) and the default load factor (0.75).
*/
//初始容量為16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
//預設擴容因子為0.75
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//最大容量是2的30次方
static final int MAXIMUM_CAPACITY = 1 << 30;
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
4、初始化
Map<String,String> carMap = new HashMap<>(); //推薦使用
5、常用方法
put(key, value) | 新增鍵值對 |
---|---|
get(Object key) | 通過key獲取value |
size() | 獲取集合鍵值對數量 |
keySet() | 獲取所有的鍵集合(返回值為set集合) |
values() | 獲取所有值集合 |
containsKey(Object key) | 判斷某個鍵是否存在 |
containsValue(Object value) | 判斷某個值是否存在某個值 |
remove(Object key) | 根據鍵值刪除鍵值對 |
clear() | 清空集合 |
5.1 put(key, value);
新增鍵值對方法;
可以新增 null 的key 或者value,鍵只能由一個null,值可以由多個null;
5.2 get(Object key)
獲取鍵值對的方法:get(key),只能根據key獲取value,如果key不存在,不會報錯,返回null;
5.3 size()
獲取集合中存放鍵值對數量;
5.4 keySet()
獲取所有的鍵集合;
Map<String,String> carMap = new HashMap<>();
carMap.put("Audi","奧迪");
carMap.put("Benz","賓士");
carMap.put("Bmw","寶馬");
Set<String> keySet = carMap.keySet();
System.out.println("獲取所有的鍵集合:"+keySet);//[Benz, Audi, Bmw]
5.5 values()
獲取所有值集合方法;
Collection<String> values = carMap.values();
System.out.println(values);//[賓士, 奧迪, 寶馬]
5.6 containsKey(Object key)
判斷集合中是否包含某個鍵值對,存在返回true;
5.7 containsValue(Object value)
判斷集合中是否包含某個值,不可以作為鍵值對的唯一標識,值可重複;
5.8 remove(Object key)
刪除鍵值對方法;
5.9 clear()
清空map集合;
6、遍歷
6.1 方式一:迭代器(不可以通過map集合直接獲取,因為它只能通過Collection獲取)
System.out.println("方式一");
Iterator<String> iterator = carKeySet.iterator();
while (iterator.hasNext()){
//獲取key
String carKey = iterator.next();
//根據key 獲取值
String carValue = carMap.get(carKey);
System.out.print(carKey + "---" + carValue +" ");
}
6.2 方式二:增強for,原理和上一個類似,也根據鍵的集合,獲取值
System.out.println("\n"+"方式二");
for (String carKey : carMap.keySet()) {
System.out.print(carKey+"---"+carMap.get(carKey)+" ");
}
6.3 方式三:增強for,操作的是Map.Entry物件,推薦寫法,效率較高
System.out.println("\n"+"方式三");
for (Map.Entry<String,String> entry : carMap.entrySet()){
System.out.print(entry.getKey()+"---"+entry.getValue()+" ");
}
執行結果
Benz---賓士 Audi---奧迪 Bmw---寶馬
7、TreeMap
自帶排序功能的集合map,TreeMap,自動按照key的字典序排序;
System.out.println("自帶排序功能的集合map,TreeMap,自動按照key的字典序排序");
Map<String,String> paramsMap = new TreeMap<>();
paramsMap.put("body","TreeMap");
paramsMap.put("userId","U0001");
paramsMap.put("sign","sign");
paramsMap.put("appId","KH96");
System.out.println(paramsMap);
自帶排序功能的集合map,TreeMap,自動按照key的字典序排序
{appId=KH96, body=TreeMap, sign=sign, userId=U0001}
8、HashTable
Hashtable,是map集合的實現類,但是跟HashMap的去表是,執行緒安全的;
Hashtable的put方法原始碼
//put方法是同步安全的
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
預設初始容量是11,擴容因子也是0.75;
Hashtable初始化原始碼
/**
* Constructs a new, empty hashtable with a default initial capacity (11)
* and load factor (0.75).
*/
//預設初始容量是11
//擴容因子也是0.75
public Hashtable() {
this(11, 0.75f);
}
每次擴容是之前容量的2倍+1;
Hashtable擴容原始碼
protected void rehash() {
int oldCapacity = table.length;
Entry<?,?>[] oldMap = table;
// 新陣列的容量=舊陣列長度*2+1
int newCapacity = (oldCapacity << 1) + 1;
// 保證新陣列的大小永遠小於等於MAX_ARRAY_SIZE
// MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8
if (newCapacity - MAX_ARRAY_SIZE > 0) {
if (oldCapacity == MAX_ARRAY_SIZE)
return;
newCapacity = MAX_ARRAY_SIZE;
}
// 建立新陣列
Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];
modCount++;
// 計算新的臨界值
threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
table = newMap;
// 將舊陣列中的元素遷移到新陣列中
for (int i = oldCapacity ; i-- > 0 ;) {
for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
Entry<K,V> e = old;
old = old.next;
//計算新陣列下標
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
// 頭插法的方式遷移舊陣列的元素
e.next = (Entry<K,V>)newMap[index];
newMap[index] = e;
}
}
}
-----------------------------------------------------------------------------------------------
1、Collections
sort(List list) | 自然升序排序 |
---|---|
reverse(List<?> list) | 集合反轉 |
binarySearch(List<? extends Comparable<? super T>> list, T key) | 二分查詢(要求集合有序) |
addAll(Collection<? extends E> c) | 從集合中新增批量元素 |
max(Collection<? extends T> coll) | 集合最大元素 |
min(Collection<? extends T> coll) | 集合最小元素 |
1.1 sort(List list)
自然升序;
1.2 reverse(List<?> list)
集合反轉;
1.3 binarySearch(List<? extends Comparable<? super T>> list, T key)
二分查詢(要求集合有序);
1.4 addAll(Collection<? extends E> c)
從集合中新增批量元素;
1.5 max(Collection<? extends T> coll)
集合中最大元素;
1.6 min(Collection<? extends T> coll)
集合中最小元素;
1.7 同步控制
Collections工具類中提供了多個synchronizedXxx方法,該方法返回指定集合物件對應的同步物件,從而解決多執行緒併發訪問集合時執行緒的安全問題。HashSet、ArrayList、HashMap都是執行緒不安全的,如果需要考慮同步,則使用這些方法。這些方法主要有:synchronizedSet、synchronizedSortedSet、synchronizedList、synchronizedMap、synchronizedSortedMap。
2、泛型
泛型就相當於是型別模板,指定什麼樣的型別,對應的值就是什麼型別,通常泛型給你引數T,E,K,V等,推薦使用T(Type);
自定義泛型舉例
Studnet類
public class Student<T1,T2,T3>{
private T1 name;
private T2 age;
private T3 sex;
public Student() {
}
public Student(T1 name, T2 age, T3 sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
//省略get,set方法
@Override
public String toString() {
return "Student{" +
"name=" + name +
", age=" + age +
", sex=" + sex +
'}';
}
}
測試
public class TestStudnet {
public static void main(String[] args) {
//在例項化的時候可以對型別進行約束
Student<String, Integer, Character> student = new Student<>();
student.setName("二狗");
student.setAge(18);
student.setSex('男');
System.out.println(student);
}
}
執行結果
Student{name=二狗, age=18, sex=男}
-----------------------------------------------------------------------------------------------