JavaSE——List集合的三個子類、泛型
List的三個子類
List的三個子類的特點
-
ArrayList:
底層資料結構是陣列,查詢快,增刪慢。
執行緒不安全,效率高。 -
Vector:
底層資料結構是陣列,查詢快,增刪慢。
執行緒安全,效率低。 -
LinkedList:
底層資料結構是連結串列,查詢慢,增刪快。
執行緒不安全,效率高。使用時要針對他們的特點來選取最合適的一種。
ArrayList概述
可調整大小的陣列實現List介面。 實現所有可選列表操作,並允許所有元素,包括null 。 除了實現List 介面之外,該類還提供了一些方法來操縱內部使用的儲存列表的陣列的大小。
Arraylist中的特有方法
-
int indexOf (Object o)
返回此列表中指定元素的第一個出現的索引,或 - 如果此列表不包含元素,則 - 1。 -
int lastIndexOf (Object o)
返回此列表中指定元素的最後一個發生的索引,或 - 如果此列表不包含元素,則 - 1。 -
E remove(int index)
移除此列表中指定位置上的元素 -
boolean remove(Object o)
移除此列表中首次出現的指定元素。 -
E set(int index, E element)
用指定的元素替代此列表中指定位置上的元素。
import java.util. ArrayList;
import java.util.List;
public class Demo {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add(100);
arrayList.add(200);
arrayList.add(300);
arrayList.add(100);
arrayList.add(200);
arrayList.add(300 );
arrayList.add(100);
arrayList.add(200);
arrayList.add(300);
int index = arrayList.indexOf(100);
System.out.println(index);//0
int lastIndexOf = arrayList.lastIndexOf(100);
System.out.println(lastIndexOf);//6
//通過首尾索引,擷取一部分,集合中的元素,放到一個新的集合當中,含頭不含尾
List subList = arrayList.subList(0, 6);
System.out.println(subList);
//[100, 200, 300, 100, 200, 300]
System.out.println(arrayList);
//[100, 200, 300, 100, 200, 300, 100, 200, 300]
}
}
ArrayList 的幾種遍歷方法
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.function.Consumer;
public class Demo1 {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
Student s1 = new Student("鋼鐵俠", 19);
Student s2 = new Student("蜘蛛俠", 20);
Student s3 = new Student("黑寡婦", 21);
Student s4 = new Student("綠巨人", 22);
Student s5 = new Student("蝙蝠俠", 23);
arrayList.add(s1);
arrayList.add(s2);
arrayList.add(s3);
arrayList.add(s4);
arrayList.add(s5);
// 遍歷1
Iterator iterator = arrayList.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next().toString());
}
System.out.println("---------------------");
// 遍歷2
ListIterator listIterator = arrayList.listIterator();
while (listIterator.hasNext()) {
System.out.println(listIterator.next().toString());
}
System.out.println("---------------------");
// 遍歷3
for (int i = 0; i < arrayList.size(); i++) {
System.out.println(arrayList.get(i).toString());
}
System.out.println("---------------------");
// 遍歷4
arrayList.forEach(new Consumer() {
@Override
public void accept(Object o) {
System.out.println(o.toString());
}
});
System.out.println("---------------------");
}
}
class Student {
private String name;
private int 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;
}
public Student() {
}
public Student(String name, int age) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Vector概述
Vector類實現了可擴充套件的物件陣列。 像陣列一樣,它包含可以使用整數索引訪問的元件。 但是, Vector的大小可以根據需要增長或縮小,以適應在建立Vector之後新增和刪除專案。
Vector的特有功能演示
- void add(int index, E element)
在此Vector中的指定位置插入指定的元素 - void addElement(E obj)
將指定的元件新增到此向量的末尾,將其大小增加1 - void insertElementAt(E obj, int index)
在指定的index插入指定物件作為該向量中的一個 index 。 - E set(int index, E element)
用指定的元素替換此Vector中指定位置的元素。
import java.util.Enumeration;
import java.util.Vector;
public class VectorDemo {
public static void main(String[] args) {
Vector vector = new Vector();
vector.add(100);
//這裡要特別注意不要越界,要麼往後新增,要麼往前面新增
vector.insertElementAt(200, 1);
vector.add(2, 300);
vector.addElement(400);
System.out.println(vector);//[100, 200, 300, 400]
//根據索引替換元素,返回原來的元素
Object set = vector.set(0, 1000);
System.out.println(set);//100
System.out.println(vector);//[1000, 200, 300]
/*獲取功能*/
System.out.println(vector.firstElement());
System.out.println(vector.lastElement());
System.out.println(vector.get(0));
Object o = vector.elementAt(1);
System.out.println(o);
/*Vextor裡面特有的迭代方式*/
Enumeration elements = vector.elements();
while (elements.hasMoreElements()) {
System.out.println(elements.nextElement());
}
//這裡的hasMoreElements和nextElement與前面講過的hasNext和next用法相似
}
}
LinkedList概述
雙鏈表實現了List和Deque介面。 實現所有可選列表操作,並允許所有元素(包括null )。
所有的操作都能像雙向列表一樣預期。 索引到列表中的操作將從開始或結束遍歷列表,以更接近指定的索引為準。
請注意,此實現不同步。 如果多個執行緒同時訪問連結列表,並且至少有一個執行緒在結構上修改列表,則必須在外部進行同步。
LinkedList的基本使用
- public void addFirst (E e) 及 addLast(E e)
- public E getFirst () 及 getLast()
- public E removeFirst () 及 public E removeLast()
import java.util.LinkedList;
public class LinkedListDemo {
public static void main(String[] args) {
//LinkedList底層資料結構是連結串列,查詢慢,增刪快,執行緒安全效率高
LinkedList linkedList = new LinkedList();
/*新增*/
linkedList.addLast("張三");
linkedList.addLast("李四");
linkedList.addLast("王五");
linkedList.addLast("趙六");
linkedList.addLast("鬼腳七");
linkedList.addFirst("陳二");
/*獲取*/
Object first = linkedList.getFirst();
System.out.println(first);
Object last = linkedList.getLast();
System.out.println(last);
Object e2 = linkedList.get(1);
System.out.println(e2);
/*刪除*/
boolean b = linkedList.remove("張三");//返回布林
Object o = linkedList.remove(0);//返回被刪除的元素
Object o1 = linkedList.removeFirst();//返回被刪除的元素
Object o2 = linkedList.removeLast();//返回被刪除的元素
}
}
eg: 去除ArrayList中重複字串元素方式
import java.util.ArrayList;
import java.util.Iterator;
public class Test {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
arrayList.add(4);
arrayList.add(5);
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
arrayList.add(4);
arrayList.add(5);
System.out.println(arrayList);//[1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
// 去重,思路就是新建一個表,新增元素的條件是這張新表中沒有這個元素
ArrayList newArrayList = new ArrayList();
// 遍歷
Iterator iterator = arrayList.iterator();
while (iterator.hasNext()) {
Object o = iterator.next();
while (!newArrayList.contains(o)) {
newArrayList.add(o);
}
}
System.out.println(newArrayList);//[1, 2, 3, 4, 5]
}
}
泛型
泛型的概述
泛型是一種把型別明確的工作推遲到建立物件或者呼叫方法的時候採取明確的特殊的型別。它的實質是引數化型別,即把型別當做引數一樣傳遞。
泛型的格式
public class 類名<資料型別 , …> {}
這裡的資料型別只能是引用資料型別。
泛型的優勢
把執行時期的問題提前到了編譯期間
避免了強制型別轉換
優化了程式設計,解決了黃色警告
泛型只在編譯器有效,在執行期就會被擦除
eg:
ArrayList list = new ArrayList();
list.add("abc");
list.add("xyz");
//手誤插入int 100
list.add(100);
for (int i = 0; i < list.size(); i++) {
String item = (String) list.get(i);
System.out.println(item);
}
//執行後程序會崩潰
//java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
ArrayList可以存放任意型別,例子中誤入了一個整型,使用時都以String的方式使用,因此程式崩潰了。為了解決類似這樣的問題(在編譯階段就可以解決),泛型應運而生。我們將第一行宣告初始化list的程式碼更改一下,編譯器會在編譯階段就能夠幫我們發現類似這樣的問題:
ArrayList<String> list = new ArrayList<>();
list.add(100);//會報紅線警告
//只能傳入String型別的引數
如果不使用泛型,我們每次使用某些型別特有的方法是都要向下轉型,特別麻煩:
ArrayList arrayList = new ArrayList();
arrayList.add("abc");
Object o = arrayList.get(0);
String s = (String)o;
System.out.println(s.length());
加上泛型後,就不用再向下轉型:
ArrayList<String> stringArrayList = new ArrayList();
stringArrayList.add("abc");
String s1 = stringArrayList.get(0);
System.out.println(s1.length());
eg: ArrayList儲存自定義物件並遍歷泛型版
//新建一個學生類
public class Student {
private String name;
private int age;
public Student() {
}
public Student(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 "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
//用ArrayList儲存學生物件並遍歷
public class GenericDemo {
public static void main(String[] args) {
ArrayList<Student> students = new ArrayList<>();
students.add(new Student("子貢",30));
students.add(new Student("顏回",28));
for (Student student : students) {
System.out.println(student.toString());
}
}
}
/*Student{name='子貢', age=30}
* Student{name='顏回', age=28}
*/
泛型介面
泛型介面與泛型類的定義及使用基本相同。泛型介面常被用在各種類的生產器中。
格式:
public interface 介面名<泛型型別>
public interface MyGenericInterface<T> {
public T sayHello();
}
- 實現泛型介面的子類已經傳入泛型實參,即已經指定型別
public class MyTest implements MyGenericInterface<String>{
@Override
public String returnHello() {
return "helloword";
}
}
- 實現泛型介面的子類沒有傳入泛型實參,即沒有指定型別
這時候這個子類也要定義成泛型
public class MyTest2<G> implements MyGenericInterface<G>{
@Override
public G returnHello() {
return null;
}
}
泛型萬用字元
泛型萬用字元<?>: 任意型別,如果沒有明確,就是Object以及任意的Java類
泛型萬用字元一般是使用?代替具體的型別實參而不是型別形參,可以把?看成所有型別的父類。是一種真實的型別。當操作型別時,不需要使用型別的具體功能時,只使用Object類中的功能。那麼可以用 ? 萬用字元來表未知型別。
泛型上下邊界
<? extends E>:傳入的型別實參必須是指定型別的子型別
<? super E>:傳入的型別實參必須是指定型別的父型別
public class Tester<X> {
private X x;
public Tester(X x) {
this.x = x;
}
}
public class myTest {
public static void main(String[] args) {
Tester<? extends Number> tester = new Tester(50);
Tester<? extends Number> tester1 = new Tester(60f);
Tester<? extends Number> tester2 = new Tester(60d);
Tester<? super Object> tester3 = new Tester(new Object());
}
}
泛型方法
格式:
public <泛型型別> 返回型別 方法名(泛型型別 變數名)
注意:在泛型類中使用了泛型形參的方法並不是泛型類
比如:
public class MyGenericFunction<T> {
//不是泛型方法,只是使用了泛型類的這個未知型別
public void genericPrint(T t){
System.out.println(t);
}
}
public class MyGenericFunction {
//泛型方法
public <T> void genericPrint(T t){
System.out.println(t);
}
}
public class Test {
public static void main(String[] args) {
MyGenericFunction myGenericFunction = new MyGenericFunction();
String s = "123456";
int i = 123456;
double d = 100.00;
//可以傳入各種型別的例項化物件
myGenericFunction.genericPrint(s);
myGenericFunction.genericPrint(i);
myGenericFunction.genericPrint(d);
}
}
可變引數
如果定義方法的時候不知道該定義多少個引數怎麼辦?這個時候就可以用到可變引數。
格式:
修飾符 返回值型別 方法名(資料型別… 變數名){}
public class Tester {
public static void main(String[] args) {
int sum = add(1,2,3,4,5,6);
System.out.println(sum);//21
}
//這個方法用來返回不限量的整型數的和
public static int add(int... a) {
int sum = 0;
for (int s : a) {
sum += s;
}
return sum;
}
}
Arrays工具類的asList()方法的使用
陣列工具類中有一個方法:
static List < T >
asList(T…a)
返回一個受指定陣列支援的固定大小的列表。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ArraysDemo {
public static void main(String[] args) {
List abc = Arrays.asList(10, 20, 30, 30, 30, 100);
for (Object o : abc) {
System.out.println(o);
}
System.out.println("----------------------");
//傳遞了一個數組,他把陣列中的元素取出來放到集合中去
List<Integer> integers = Arrays.asList(new Integer[]{100, 200, 300});
System.out.println(integers);
//傳遞了多個數組,他將陣列整體作為元素存到集合中去
List<Integer[]> integers1 =
Arrays.asList(
new Integer[]{100, 200,300},
new Integer[]{1003, 2005, 3005},
new Integer[]{1004, 20066, 300333});
System.out.println(integers1);
//遍歷
for (Integer[] integers2 : integers1) {
for (Integer integer : integers2) {
System.out.println(integer);
}
}
}
}
eg: 集合巢狀
我們先來新建一個學生類
public class Students {
private String name;
private int age;
public Students(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Students{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
每一個學生都是物件,現在,我們把我們班的所有學生放進一個集合,那麼隔壁班的所有學生也寫進一個集合,再把這兩個集合放進一個大的集合,就是年級。
import java.util.ArrayList;
public class Demo {
public static void main(String[] args) {
ArrayList<Students> class1 = new ArrayList<>();
ArrayList<Students> class2 = new ArrayList<>();
class1.add(new Students("張三", 19));
class1.add(new Students("李四", 20));
class2.add(new Students("王五", 21));
class2.add(new Students("趙六", 21));
ArrayList<ArrayList> grade = new ArrayList();
grade.add(class1);
grade.add(class2);
//遍歷輸出每一個同學的資訊
for (ArrayList<Students> arrayList : grade) {
for (Students students : arrayList) {
System.out.println(students.toString());
}
}
}
}
//Students{name='張三',age=19}
//Students{name='李四',age=20}
//Students{name='王五',age=21}
//Students{name='趙六',age=21}