java基礎(4)注解,集合,

注解

什么是注解(Annotation)?注解是放在Java源碼的類、方法、字段、參數前的一種特殊“注釋”

// this is a component:
@Resource("hello")
public class Hello {@Injectint n;@PostConstructpublic void hello(@Param String name) {System.out.println(name);}@Overridepublic String toString() {return "Hello";}
}

注釋會被編譯器直接忽略,注解則可以被編譯器打包進入class文件,因此,注解是一種用作標注的“元數據”。

注解的作用

從JVM的角度看,注解本身對代碼邏輯沒有任何影響,如何使用注解完全由工具決定。
Java的注解可以分為三類:

  • 由編譯器使用的注解,
例如:@Override:讓編譯器檢查該方法是否正確地實現了覆寫;
@SuppressWarnings:告訴編譯器忽略此處代碼產生的警告

這類注解不會被編譯進class文件中。

  • 第二類是由工具處理.class文件使用的注解,比如有些工具會在加載class的時候,對class做動態修改,實現一些特殊的功能。一般只被底層庫使用,不必自己處理。
  • 第三類是在程序運行期能夠讀取的注解,它們在加載后一直存在于JVM中,這也是最常用的注解。例如,一個配置了@PostConstruct的方法會在調用構造方法后自動被調用(這是Java代碼讀取該注解實現的功能,JVM并不會識別該注解)

定義一個注解時,還可以定義配置參數。配置參數可以包括:
所有基本類型; String; 枚舉類型; 基本類型、String、Class以及枚舉的數組。

public class Hello {@Check(min=0, max=100, value=55)public int n;@Check(value=99)public int p;@Check(99) // @Check(value=99)public int x;@Checkpublic int y;
}

@Check就是一個注解。第一個@Check(min=0, max=100, value=55)明確定義了三個參數,第二個@Check(value=99)只定義了一個value參數,它實際上和@Check(99)是完全一樣的。最后一個@Check表示所有參數都使用默認值。

定義注解

java 用@interface定義注解,然后添加參數、默認值:

public @interface Report {int type() default 0;String level() default "info";String value() default "";
}

接著有一些可以用于注解的注解,成為元注解。如

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Report {int type() default 0;String level() default "info";String value() default "";
}

@Target定義該注解能注解什么東西
類或接口:ElementType.TYPE;
字段:ElementType.FIELD;
方法:ElementType.METHOD;
構造方法:ElementType.CONSTRUCTOR;
方法參數:ElementType.PARAMETER。

@Retention定義該注解的生命周期,如上就是定義在runtime的時候運行,因為我們自己寫的注解一般在runtime的時候執行。
僅編譯期:RetentionPolicy.SOURCE;
僅class文件:RetentionPolicy.CLASS;
運行期:RetentionPolicy.RUNTIME。
其次還有@Repeatable定義該注解是否可以重復使用在同一個地方,@Inherited定義是否可以繼承父類的注解。

處理注解

Java的注解本身對代碼邏輯沒有任何影響。根據@Retention的配置:

  • SOURCE類型的注解在編譯期就被丟掉了;
  • CLASS類型的注解僅保存在class文件中,它們不會被加載進JVM;
  • RUNTIME類型的注解會被加載進JVM,并且在運行期可以被程序讀取。
使用注解
// 定義一個注解
@Retention(RetentionPolicy.RUNTIME) // runtime時運行,會打入jvm
@Target(ElementType.FIELD) // 作用于字段
@interface Range {int min() default 0;int max() default 255;
}class Person1 {@Range(min = 1, max = 20)public String name;@Range(max = 10)public String city;
}

定義了注解,本身對程序邏輯沒有任何影響。我們必須自己編寫代碼來使用注解。

void check(Person person) throws IllegalArgumentException, ReflectiveOperationException {// 遍歷所有Field:for (Field field : person.getClass().getFields()) {// 獲取Field定義的@Range:Range range = field.getAnnotation(Range.class);// 如果@Range存在:if (range != null) {// 獲取Field的值:Object value = field.get(person);// 如果值是String:if (value instanceof String s) {// 判斷值是否滿足@Range的min/max:if (s.length() < range.min() || s.length() > range.max()) {throw new IllegalArgumentException("Invalid field: " + field.getName());}}}}
}

集合

什么是集合(Collection)?集合就是“由若干個確定的元素所構成的整體”。
在Java中,如果一個Java對象可以在內部持有若干其他Java對象(注意是對象,而不是基礎類型,比如Integer可以,但是int不行),并對外提供訪問接口,我們把這種Java對象稱為集合。很顯然,Java的數組可以看作是一種集合:

String[] ss = new String[10]; // 可以持有10個String對象
ss[0] = "Hello"; // 可以放入String對象
String first = ss[0]; // 可以獲取String對象

數組的限制,長度固定,數據只能通過索引獲取。
我們需要各種不同類型的集合類來處理不同的數據,例如:

  • 可變大小的順序鏈表;
  • 保證無重復元素的集合;
Collection

Java標準庫自帶的java.util包提供了集合類:Collection,它是除Map外所有其他集合類的根接口
ava的java.util包主要提供了以下三種類型的集合:

  • List 一種有序列表的集合,例如,按索引排列的Student的List,如ArrayList, LinkedList
  • Set 一種保證沒有重復元素的集合,例如,所有無重復名稱的Student的Set
  • Map 一種通過鍵值(key-value)查找的映射表集合,例如,根據Student的name查找對應Student的Map

Java集合設計的特點

  • 實現了 接口和 實現類 相分離,如有序表的接口是List,具體的實現類有ArrayList,LinkedList等
  • 支持泛型 List list = new ArrayList<>(); // 只能放入String類型
  • 訪問集合都是通過迭代器(Iterator)的形式訪問的,它最明顯的好處在于無需知道集合內部元素是按什么方式存儲的。(類似于js的實現Symbol.iterator,實現對象的迭代,外部無需知道他是怎么存儲的。)
List

List是最基礎的一種集合:它是一種有序列,數組和List非常相似。但是數組進行增刪操作會非常麻煩。反觀List
當我們需要增刪操作的時候,可以使用ArrayList,

如 一個ArrayList擁有5個元素,實際數組大小為6(即有一個空位)

  • 當添加一個元素并指定索引如2到ArrayList時,ArrayList自動移動需要移動的元素,把2及后面的元素移動。
  • 然后,往內部指定索引的數組位置添加一個元素,然后把size加1
  • 此時繼續添加元素的時候,數組已滿,沒有空閑位置的時候,ArrayList先創建一個更大的新數組,然后把舊數組的所有元素復制到新數組,緊接著用新數組取代舊數組。

可見,ArrayList把添加和刪除的操作封裝起來,讓我們操作List類似于操作數組,卻不用關心內部元素如何移動。
List < T >主要的接口方法
在末尾添加一個元素:boolean add(E e)
在指定索引添加一個元素:boolean add(int index, E e)
刪除指定索引的元素:E remove(int index)
刪除某個元素:boolean remove(Object e)
獲取指定索引的元素:E get(int index)
獲取鏈表大小(包含元素的個數):int size()

上述是通過數組實現的List,實際上,使用鏈表也能實現。
在LinkedList中,它的內部每個元素都指向下一個元素:

        ┌───┬───┐   ┌───┬───┐   ┌───┬───┐   ┌───┬───┐
HEAD ──>│ A │ ●─┼──>│ B │ ●─┼──>│ C │ ●─┼──>│ D │   │└───┴───┘   └───┴───┘   └───┴───┘   └───┴───┘

我們來比較一下ArrayList和LinkedList:

123ArrayListLinkedList
獲取指定元素速度很快需要從頭開始查找元素
添加元素到末尾速度很快速度很快
在指定位置添加/刪除需要移動元素不需要移動元素
內存占用較大

使用

	   List<String> list = new ArrayList<>();list.add("apple");list.add("pear");List<String> list1 = new LinkedList<>();list1.add("test1");list1.add("test2");// Java 9 List<Integer> list2 = List.of(1, 2, 5);System.out.println(list);System.out.println(list1);

遍歷List,使用for循環

 for (int i = 0; i < list.size(); i++) {String s = list.get(i);System.out.println(s);}

這種方法并不推薦
一是代碼復雜,二是因為get(int)方法只有ArrayList的實現是高效的,換成LinkedList后,索引越大,訪問速度越慢。
要始終堅持使用迭代器Iterator來訪問List。Iterator本身也是一個對象,但它是由List的實例調用iterator()方法的時候創建的。

 for (Iterator<String> it = list.iterator();it.hasNext();) {String s = it.next();System.out.println(s);}

調用iterator獲取一個iterator對象,跟js的迭代器很類似。it.hasNext()判斷是否還有下一個元素,it.next()返回下一個元素
以上是一種固定寫法,第二個參數需要搭配it.hasNext()返回一個boolen判斷是否繼續循環。
Java的for each循環本身就可以幫我們使用Iterator遍歷

for (String it1 : list1) {System.out.println(it1);}

可以直接遍歷獲取值。
實際上,只要實現了Iterable接口的集合類都可以直接用for each循環來遍歷,Java編譯器本身并不知道如何遍歷集合對象,但它會自動把for each循環變成Iterator的調用原因就在于Iterable接口定義了一個Iterator<E> iterator()方法,強迫集合類必須返回一個Iterator實例。
有點像js中,只要實現了Symbol.iterator方法的對象,就可以使用for of進行遍歷。因為Symbol.iteraotr方法返回了一個迭代器對象。

List和Array互相轉換
  • 把List變為Array有三種方法,第一種是調用toArray()方法直接返回一個Object[]數組,不常用,因為類型可能會丟失
  • 第二種方式是給toArray(T[])傳入一個類型相同的Array,List內部自動把元素復制到傳入的Array中。這里的T和List< E>不一樣,所以toArray可以傳其他類型的數組,但不兼容會報錯。
  List<String> list1 = new LinkedList<>();list1.add("test1");list1.add("test2");String[] test2 = list1.toArray(new String[2]);Integer[] test3 = list1.toArray(new Integer[2]); //會報錯

傳入的數組如果不夠大,那么List內部會創建一個新的數組,大小一樣。
如果傳入的數組過大,那么多余的元素全部填充null。

Array to List
// JAVA 9 
Integer[] array = { 1, 2, 3 };
List<Integer> list = List.of(array);// JAVA 9 之前List<String> list3 = Arrays.asList(test2);

注意,這種返回的List不一定是ArrayList或者LinkedList,因為返回的List是只讀的,在操作會報錯。’

編寫equals方法

List提供了contains方法,來判斷當前list是否包含哪些內容。提供了indexOf(obj)方法來判斷obj在當前list的位置

 List<String> list3 = new ArrayList<>();list3.add("S");list3.add("C");System.out.println(list3.contains(new String("S"))); // trueSystem.out.println(list3.indexOf("S")); //0

這里contains我們傳入了一個全新的String對象,但是結果還是為true,這是因為contains的實現是

public class ArrayList {Object[] elementData;public boolean contains(Object o) {for (int i = 0; i < elementData.length; i++) {if (o.equals(elementData[i])) {return true;}}return false;}
}

拿傳入的對象,調用其equals方法,所以,要向正確的使用contains和indexOf,傳入的對象就必須正確的實現equals方法。而String和Integer等對象,java已經幫我們實現了equals方法。

如何編寫equals方法
  • 先確定實例“相等”的邏輯,即哪些字段相等,就認為實例相等;
  • 用instanceof判斷傳入的待比較的Object是不是當前類型,如果是,繼續比較,否則,返回false;
  • 對引用類型用Objects.equals()比較,對基本類型直接用==比較。
class Person {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}public boolean equals(Object o) {if (o instanceof Person) {Person p = (Person) o;boolean nameEquals = false;if (this.name == null && p.name == null) {nameEquals = true;}if (this.name != null) {nameEquals = this.name.equals(p.name);}return nameEquals && this.age == p.age;}return false;}// 使用Objects.equals()比較兩個引用類型是否相等的目的是省去了判斷null的麻煩。兩個引用類型都是null時它們也是相等的。public boolean equals1(Object o) {if (o instanceof Person) {Person p = (Person) o;return Objects.equals(this.name, p.name) && this.age == p.age;}return false;}
}
Map

有個需求,在存放Students 集合 List中查找一個名為小明的,查看他的成績。
這時候我們需要遍歷List,然后判斷到name微小明的,就取他的成績。這樣效率很低。
而Map這種key value的形式剛好可以滿足。

  Map<String, Person> map = new HashMap<>();map.put("lin", new Person("lin", 20));Person Target = map.get("lin");

和List類似,Map也是一個接口,最常用的實現類是HashMap。如果只是想查詢某個key是否存在,可以調用boolean containsKey(K key)方法。
其他與js的Map類似。
遍歷

  Person Target = map.get("lin");for (String key : map.keySet()) {System.out.println(key);}for (Map.Entry<String, Person> entry : map.entrySet()) {String key = entry.getKey();Person value = entry.getValue();System.out.println(key);System.out.println(value);}

map.keySet()獲取key,map.entrySet()獲取key-value

  • Map不保證順序
編寫equals 和 hashCode

Map之所以能很快通過key獲取value,是因為內部通過空間換時間的處理。
Map內部用很大的數組存放數據,然后通過特殊的計算,比如f函數 f(key) = index,傳入同一個key,index總是相同的,然后通過index存放value,取數據的時候也是通過f(key) = index計算出index,然后通過數組[index]去獲取數據。但這樣有個問題,如果傳入的兩個key是不同的對象呢。

 Map<String, String> map2 = new HashMap<>();map2.put("2", "123");System.out.println(map2.get(new String("2"))); // 123

傳入不同的String還是能拿到正確的值,因為跟List一樣,key值得比對也是通過equals方法。
其次,通過key獲取index是通過hashCode方法獲取的,

正確使用Map必須保證:
  • 作為key的對象必須正確覆寫equals()方法,相等的兩個key實例調用equals()必須返回true;
  • 作為key的對象還必須正確覆寫hashCode()方法,且hashCode()方法要嚴格遵循以下規范:
  • 如果兩個對象相等,則兩個對象的hashCode()必須相等;
  • 如果兩個對象不相等,則兩個對象的hashCode()盡量不要相等
public class Person {String firstName;String lastName;int age;@Overrideint hashCode() {int h = 0;h = 31 * h + firstName.hashCode();h = 31 * h + lastName.hashCode();h = 31 * h + age;return h;}
}
// 借助Objects.hash
int hashCode() {return Objects.hash(firstName, lastName, age);
}

equals()用到的用于比較的每一個字段,都必須在hashCode()中用于計算;
equals()中沒有使用到的字段,絕不可放在hashCode()中計算。

這里注意,兩個對象的hashCode盡量不要想等,也就是有可能相等。

我們把不同的key具有相同的hashCode()的情況稱之為哈希沖突。在沖突的時候,一種最簡單的解決辦法是用List存儲hashCode()相同的key-value。顯然,如果沖突的概率越大,這個List就越長,Map的get()方法效率就越低,這就是為什么要盡量滿足條件二。
在這里插入圖片描述

hashCode()方法編寫得越好,HashMap工作的效率就越高。
計算hash code,相同hash code用一個List存放。

EnumMap

HsahMap通過空間換時間,計算index從數組直接拿數據。
如果key是Enum的話,可以用EnumMap ,在內部以一個非常緊湊的數組存儲value,并且根據enum類型的key直接定位到內部數組的索引,并不需要計算hashCode(),不但效率最高,而且沒有額外的空間浪費。

public class Main {public static void main(String[] args) {Map<DayOfWeek, String> map = new EnumMap<>(DayOfWeek.class);map.put(DayOfWeek.MONDAY, "星期一");map.put(DayOfWeek.TUESDAY, "星期二");map.put(DayOfWeek.WEDNESDAY, "星期三");map.put(DayOfWeek.THURSDAY, "星期四");map.put(DayOfWeek.FRIDAY, "星期五");map.put(DayOfWeek.SATURDAY, "星期六");map.put(DayOfWeek.SUNDAY, "星期日");System.out.println(map);System.out.println(map.get(DayOfWeek.MONDAY));}
}
TreeMap

HashMap無序,還有一種Map,它在內部會對Key進行排序,這種Map就是SortedMap。注意到SortedMap是接口,它的實現類是TreeMap。

       ┌───┐│Map│└───┘▲┌────┴─────┐│          │
┌───────┐ ┌─────────┐
│HashMap│ │SortedMap│
└───────┘ └─────────┘▲│┌─────────┐│ TreeMap │└─────────┘

SortedMap保證遍歷時以Key的順序來進行排序。例如,放入的Key是"apple"、“pear”、“orange”,遍歷的順序一定是"apple"、“orange”、“pear”,因為String默認按字母排序

 public static void main(String[] args) {Map<String, Integer> map = new TreeMap<>();map.put("orange", 1);map.put("apple", 2);map.put("pear", 3);for (String key : map.keySet()) {System.out.println(key);}// apple, orange, pear}

TreeMap要求實現Comparable方法
沒有的話需要在使用TreeMap的時候指定排序方法,

public class Main {public static void main(String[] args) {Map<Person, Integer> map = new TreeMap<>(new Comparator<Person>() {public int compare(Person p1, Person p2) {return p1.name.compareTo(p2.name);}});map.put(new Person("Tom"), 1);map.put(new Person("Bob"), 2);map.put(new Person("Lily"), 3);for (Person key : map.keySet()) {System.out.println(key);}// {Person: Bob}, {Person: Lily}, {Person: Tom}System.out.println(map.get(new Person("Bob"))); // 2}
}

Comparator接口要求實現一個比較方法,它負責比較傳入的兩個元素a和b,如果a<b,則返回負數,通常是-1,如果a==b,則返回0,如果a>b,則返回正數,通常是1。TreeMap內部根據比較結果對Key進行排序

  • SortedMap在遍歷時嚴格按照Key的順序遍歷,最常用的實現類是TreeMap;
  • 作為SortedMap的Key必須實現Comparable接口,或者傳入Comparator;
  • 要嚴格按照compare()規范實現比較邏輯,否則,TreeMap將不能正常工作。
Set

不重復的集合,且不需要value,類似于js的Set

   Set<String> set = new HashSet<>();System.out.println(set.add("abc")); // trueSystem.out.println(set.add("xyz")); // trueSystem.out.println(set.add("xyz")); // false,添加失敗,因為元素已存在System.out.println(set.contains("xyz")); // true,元素存在System.out.println(set.contains("XYZ")); // false,元素不存在System.out.println(set.remove("hello")); // false,刪除失敗,因為元素不存在System.out.println(set.size()); // 2,一共兩個元素

set.add, set.contains, set.remove, set.resize
Set實際上相當于只存儲key、不存儲value的Map。我們經常用Set用于去除重復元素。
因為放入Set的元素和Map的key類似,都要正確實現equals()和hashCode()方法,否則該元素無法正確地放入Set

HashSet其實是封裝HashMap

public class HashSet<E> implements Set<E> {// 持有一個HashMap:private HashMap<E, Object> map = new HashMap<>();// 放入HashMap的value:private static final Object PRESENT = new Object();public boolean add(E e) {return map.put(e, PRESENT) == null;}public boolean contains(Object o) {return map.containsKey(o);}public boolean remove(Object o) {return map.remove(o) == PRESENT;}
}

在這里插入圖片描述

HasHSet是無序的,而SortedSet是有序的,但他只是個接口,實現的類是TreeSet

public class Main {public static void main(String[] args) {Set<String> set = new TreeSet<>();set.add("apple");set.add("banana");set.add("pear");set.add("orange");for (String s : set) {System.out.println(s);}}
}

使用TreeSet和使用TreeMap的要求一樣,添加的元素必須正確實現Comparable接口,如果沒有實現Comparable接口,那么創建TreeSet時必須傳入一個Comparator對象。

Queue

隊列,先進先出
它和List的區別在于,List可以在任意位置添加和刪除元素,而Queue只有兩個操作:

把元素添加到隊列末尾;
從隊列頭部取出元素。
在這里插入圖片描述
這些操作有兩個方法,一個會拋出錯誤,一個返回false/null。

// 這是一個List:
List<String> list = new LinkedList<>();
// 這是一個Queue:
Queue<String> queue = new LinkedList<>();

LinkedList(鏈表)類實現了Queue接口和List接口

PriorityQueue

上述的Queue是先進先出,但是如果有優先級的處理,就像react中優先級高的渲染,就出現了優先級隊列PriorityQueue,
PriorityQueue和Queue的區別在于,它的出隊順序與元素的優先級有關,對PriorityQueue調用remove()或poll()方法,返回的總是優先級最高的元素。(react-sceduler的實現類似)

public class Main {public static void main(String[] args) {Queue<String> q = new PriorityQueue<>();// 添加3個元素到隊列:q.offer("apple");q.offer("pear");q.offer("banana");// 按照元素順序System.out.println(q.poll()); // appleSystem.out.println(q.poll()); // bananaSystem.out.println(q.poll()); // pearSystem.out.println(q.poll()); // null,因為隊列為空}
}

放入PriorityQueue的元素,必須實現Comparable接口,PriorityQueue會根據元素的排序順序決定出隊的優先級。
也可以在創建PriorityQeue的時候提供一個comparator對象來判斷兩個元素的順序,跟TreeMap類似。

public class Main {public static void main(String[] args) {Queue<User> q = new PriorityQueue<>(new UserComparator());// 添加3個元素到隊列:q.offer(new User("Bob", "A1"));q.offer(new User("Alice", "A2"));q.offer(new User("Boss", "V1"));System.out.println(q.poll()); // Boss/V1System.out.println(q.poll()); // Bob/A1System.out.println(q.poll()); // Alice/A2System.out.println(q.poll()); // null,因為隊列為空}
}class UserComparator implements Comparator<User> {public int compare(User u1, User u2) {if (u1.number.charAt(0) == u2.number.charAt(0)) {// 如果兩人的號都是A開頭或者都是V開頭,比較號的大小:return u1.number.compareTo(u2.number);}if (u1.number.charAt(0) == 'V') {// u1的號碼是V開頭,優先級高:return -1;} else {return 1;}}
}
Deque雙端隊列

,允許兩頭都進,兩頭都出,這種隊列叫雙端隊列

Java集合提供了接口Deque來實現一個雙端隊列,它的功能是:

既可以添加到隊尾,也可以添加到隊首;
既可以從隊首獲取,又可以從隊尾獲取。

在這里插入圖片描述
Deque是一個接口,它的實現類有ArrayDequeLinkedList

Stack 先進后出

在這里插入圖片描述
將十進制轉為16進制

public static void main(String[] args) {String hex = toHex(12500);System.out.println(hex);if (hex.equalsIgnoreCase("30D4")) {System.out.println("測試通過");} else {System.out.println("測試失敗");}}static String toHex(int n) {Deque<String> dq = new LinkedList<>();int num = n % 16;int num1 = n / 16;while (num1 != 0) {dq.addFirst(Integer.toHexString(num));num = num1 % 16;num1 = num1 / 16;}dq.addFirst(Integer.toHexString(num));String result = "";String s = dq.pollFirst();System.out.println(s);while (s != null) {result += s;s = dq.pollFirst();}return result;}

循環求余數壓入棧中,再循環取出。

Iterator

Java的集合類都可以使用for each循環,List、Set和Queue會迭代每個元素,Map會迭代每個key。因為他們都實現了Iterator對象
Java編譯器并不知道如何遍歷List。上述代碼能夠編譯通過,只是因為編譯器把for each循環通過Iterator改寫為了普通的for循環:

for (Iterator<String> it = list.iterator(); it.hasNext(); ) {String s = it.next();System.out.println(s);
}
for (String s : list) {System.out.println(s);
}

類似于js的Symbol.Iterator迭代器,只要實現了Symbol.iterator,就可以用for of循環。
實現一個集合,需要實現他的Iterator方法,并且返回一個Iterator對象

class ReverseList<T> implements Iterable<T> {private List<T> list = new ArrayList<>();public void add(T t) {list.add(t);}@Overridepublic Iterator<T> iterator() {return new ReverseIterator(list.size());}class ReverseIterator implements Iterator<T> {int index;ReverseIterator(int index) {this.index = index;}@Overridepublic boolean hasNext() {return index > 0;}@Overridepublic T next() {index--;return ReverseList.this.list.get(index);}}
}

可以看到迭代器主要實現hasNext方法和next方法。js的Symbol.Iterator則是實現{done: ture, value: ‘’}來判斷是否已經遍歷結束。

Collections

Collections是JDK提供的工具類,同樣位于java.util包中。它提供了一系列靜態方法,能更方便地操作各種集合。

 // 空集合List<String> list2 = Collections.emptyList(); // 返回不可變List// 單元素集合List<String> list3 = Collections.singletonList("apple"); // 返回不可變ListList<String> list4 = Arrays.asList("1230", "456", "132"); // 返回不可變List// 排序Collections.sort(list4); // 排序回修改List元素的位置,所以需要傳入可變ListSystem.out.println(list4);Collections.shuffle(list4); // 打亂List的順序System.out.println(list4);// 變為不可變集合 假設List4是可變ListList<String> list5 = Collections.unmodifiableList(list4);// list5.add("orange"); // UnsupportedOperationException!list4.add("2222");list4 = null; // 轉變后立馬扔掉老的引用System.out.println(list5); // 老的List修改影響不可變List
小結
List
  • Java集合使用統一的Iterator遍歷
  • 有序集合 List -> ArrayList(通過數組實現,) LinkedList(通過鏈表實現),可以用for each遍歷。存放List的對象需要實現equals方法,便于調用List的contains和indexOf方法。
Map
  • key-value 映射表 -> Map , Map集合無序,可以通過for each遍歷keySet(),也可以通過for each遍歷entrySet(),直接獲取key-value。最常用的Map就是HashMap

  • HashMap通過數組的形式存放,調用key.hashCode()獲取索引,通過key.equals()判斷是否相等,所以HashMap存放到對象需要實現hashCode和equals方法,而且equals方法返回true,則hashCode返回的索引一定相等,如果equals返回false,那么hashCode返回的值盡兩不要相等。

    • 但是萬一相等了呢,HashMap的數組存放的是List<Entry<string, Object>>,當不同的key的HashCode返回的索引相等的時候,會拿到一個List,再通過key.equlas去獲取到對應的值。
  • 如果Map的key是enum類型,推薦使用EnumMap,既保證速度,也不浪費空間。使用EnumMap的時候,根據面向抽象編程的原則,應持有Map接口。

  • HashMap是無序的,但是Map下面還有一個SortedMap接口,TreeMap類實現了這個接口,他是有序的。

    • TreeMap怎么排序呢,通過存放的每個對象實現一個comparable接口,或者在定義TreeMap的時候,指定一個自定義排序算法
    •   Map<Person, Integer> map = new TreeMap<>(new Comparator<Person>() {public int compare(Person p1, Person p2) {return p1.name.compareTo(p2.name);}});
      
    • Comparator接口要求實現一個比較方法,它負責比較傳入的兩個元素a和b,如果a<b,則返回負數,通常是-1,如果a==b,則返回0,如果a>b,則返回正數,通常是1。TreeMap內部根據比較結果對Key進行排序。
Set
  • 只需要存儲不重復的key,并不需要存儲映射的value,那么就可以使用Set。
  • 最常用的Set是HashSet,他是HashMap的封裝
  • 此外HashSet是無序的,但同時,SrotedSet是有序的,他是一個接口,TreeSet實現了這個接口
  • 跟TreeMap一樣,TreeSet也要求一個compator接口,來判斷兩個對象的順序。
Queue 通過LinkedList類實現該接口
  • 隊列先進先出,通過add()/offer()方法將元素添加到隊尾
  • 通過remove/poll獲取隊首元素并挪去。
  • 通過element()/peek()獲取隊首元素但不去掉。
  • 前面的add/remove/eleemtn執行出錯會報錯,而offer/poll/peek只返回對應的false/null
  • PriorityQueue優先級隊列,優先級隊列需要實現Comparable接口,priorityQueue根據元素的優先順序決定誰先出隊。
  • PriorityQueue默認按元素比較的順序排序(必須實現Comparable接口),也可以通過Comparator自定義排序算法(元素就不必實現Comparable接口)。
Deque 雙頭隊列 也是LinkedList實現
  • Deque其實也是繼承于Queue的
  • addLast/offerLast addFirst/offerFirst 從隊頭/隊尾插入
  • removeFirst/pollFirst removeLast/pollLast 從對頭/隊尾取出并刪除
  • getFirst/peekFirst getLast/peekLast 從對頭/隊尾取出但不刪除
Stack 棧,先進后廚
  • 利用Deque實現棧即可
  • 只調用對應的offerFirst/peekFirst/getFirst三個方法。
  • Stack作用很大,比如計算機調用方法通過執行棧維護等等
Iterator

Iterator是一種抽象的數據訪問模型。使用Iterator模式進行迭代的好處有:

  • 對任何集合都采用同一種訪問模型;
  • 調用者對集合內部結構一無所知;
  • 集合類返回的Iterator對象知道如何迭代。
  • Java提供了標準的迭代器模型,即集合類實現java.util.Iterable接口,返回java.util.Iterator實例。
Collections

Collections類提供了一組工具方法來方便使用集合類:

  • Collections.emptyList()創建空集合 返回不可變集合
  • Collections.singletonList(“test”)創建單個集合 返回不可變集合
  • Collections.unmodifiableList(可變集合),將可變集合變成不可變集合
  • Collections.sort()排序集合, Collections.shuffle() 將集合的順序重新打亂(洗牌)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/712659.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/712659.shtml
英文地址,請注明出處:http://en.pswp.cn/news/712659.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

經典文獻閱讀之--CamMap(基于SLAM地圖對不共視相機進行外參標定)

0. 簡介 由于多相機之間通常存在有限或無重疊的視場&#xff0c;因此在估計外參相機參數時面臨著一定的挑戰&#xff0c;為了解決這個問題&#xff0c;本文提出了CamMap&#xff1a;一種新穎的6自由度外參標定流程。根據三個操作規則&#xff0c;使一個多相機系統單獨捕捉一些…

【Linux進程】進程狀態(運行阻塞掛起)

目錄 前言 1. 進程狀態 2. 運行狀態 3. 阻塞狀態 4. 掛起狀態 5. Linux中具體的狀態 總結 前言 在Linux操作系統中&#xff0c;進程狀態非常重要&#xff0c;它可以幫助我們了解進程在系統中的運行情況&#xff0c;從而更好地管理和優化系統資源&#xff0c;在Linux系統中&am…

【Python筆記-設計模式】迭代器模式

一、說明 迭代器模式是一種行為設計模式&#xff0c;讓你能在不暴露集合底層表現形式&#xff08;列表、棧和樹等&#xff09;的情況下遍歷集合中所有的元素。 (一) 解決問題 遍歷聚合對象中的元素&#xff0c;而不需要暴露該對象的內部表示 (二) 使用場景 需要對聚合對象…

SpringBoot實現短鏈跳轉

目錄 1.背景介紹 2.短鏈跳轉的意義 3.SpringBoot中的代碼實現 1.建議短鏈-長鏈的數據庫表&#xff1a;t_url_map: 2.映射實體 3.Dao層實現 4.Service層實現 5.Controller層實現 3.結果測試 4.問題 1.背景介紹 短鏈跳轉是一種通過將長鏈接轉換為短鏈接的方式&…

南方電網的能源棋局上,蔚來換電扮演什么角色?

2 月 26 日&#xff0c;南網儲能科技與蔚來能源簽署協議&#xff0c;將充換電站、儲能站、可調負載等聚合資源連接到虛擬電廠平臺&#xff0c;推動換電站作為分布式儲能在虛擬電廠項目上的應用。 蔚來換電站是國內首個智慧微電網型分布式換電設施&#xff0c;可透過換電訂單預…

軟考-系統集成項目管理中級-信息系統建設與設計

本章重點考點 1.信息系統的生命周期 信息系統建設的內容主要包括設備采購、系統集成、軟件開發和運維服務等。信息系統的生命周期可以分為四個階段:立項、開發、運維和消亡。 2.信息系統開發方法 信息系統常用的開發方法有結構化方法、原型法、面向對象方法等 1)結構化方法 …

AI智能分析網關V4:抽煙/打電話/玩手機行為AI算法及場景應用

抽煙、打電話、玩手機是人們在日常生活中常見的行為&#xff0c;但這些行為在某些場合下可能會帶來安全風險。因此&#xff0c;對于這些行為的檢測技術及應用就變得尤為重要。今天來給大家介紹一下TSINGSEE青犀AI智能分析網關V4抽煙/打電話/玩手機檢測算法及其應用場景。 將監控…

java項目打包運行報異常:xxxxx-1.0-SNAPSHOT.jar中沒有主清單屬性

pom.xml中加入這段話即可 <build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>2.4.4</version><executions><execution><…

安泰ATA-7050高壓放大器在微流控細胞分選中的應用

微流控細胞分選是一種用于分離和鑒定生物樣本中特定類型細胞的技術&#xff0c;其原理基于將生物細胞通過微通道進行操縱和區分。微流控細胞分選的原理主要基于流體力學、電氣學、光學和熱力學等多學科的交叉應用。通過設計具有特定尺寸和性質的微通道網絡&#xff0c;可實現對…

RV1126芯片概述

RV1126芯片概述 前言1 主要特性2 詳細參數 前言 1 主要特性 四核 ARM Cortex-A7 and RISC-V MCU250ms快速開機2.0Tops NPU14M ISP with 3幀 HDR支持3個攝像頭同時輸入4K H.264/H.265 視頻編碼和解碼 2 詳細參數

萬人在線直播:構建高效穩定的音視頻架構

萬人在線大型直播音視頻架構解析 隨著網絡技術的發展,大型直播已成為人們生活中不可或缺的一部分。萬人在線直播音視頻架構是實現高清、流暢直播的關鍵。本文將深入探討這一架構的核心組成部分及其運作機制。 直播客戶端作為架構的基石,負責音視頻數據的采集、編碼、推流、…

永磁同步電機無感FOC(龍伯格觀測器)算法技術總結-仿真篇

文章目錄 1、觀測器的引入2、β軸向下的電機觀測器數學模型3、β軸向下的轉子點角度及速度觀測4、Simulink仿真模型搭建4.1模型總覽4.2 Luenberger觀測器模塊4.2.1 I_alpha觀測4.2.2 I_beta觀測4.2.3 e_alpha、e_beta觀測4.2.4 鎖相環 4.3 速度設定4.4 速度觀測結果4.5 電角度觀…

express+mysql+vue,從零搭建一個商城管理系統6--數據校驗和登錄

提示&#xff1a;學習express&#xff0c;搭建管理系統 文章目錄 前言一、修改models/user.js二、修改routes下的user.js三、Api新建user/login接口四、刪除數據庫原有數據&#xff0c;添加新驗證規則的用戶四、用戶登錄總結 前言 需求&#xff1a;主要學習express&#xff0c;…

SQL數學函數--pow(),abs() 函數 全面且詳細

一、冪運算函數: pow 語法: pow(double a, double p) 返回值: double 說明:返回a的p次冪 舉例&#xff1a; hive> select pow(2,4) ; 16.0 ???????二、絕對值函數: abs 語法: abs(double a) abs(int a) 返回值: double int 說明:返回數值a的絕對值 …

MacBook將iPad和iPhone備份到移動硬盤

#創作靈感# 一個是ICloud不夠用&#xff0c;想備份到本地&#xff1b;然而本地存儲不夠用&#xff0c;增加容量巨貴&#xff0c;舍不得這個錢&#xff0c;所以就想著能不能備份到移動硬盤。剛好有個移動固態&#xff0c;所以就試了一下&#xff0c;還真可以。 #正文# 說一下邏…

《PyTorch深度學習實踐》第八講加載數據集

一、 1、DataSet 是抽象類&#xff0c;不能實例化對象&#xff0c;主要是用于構造我們的數據集 2、DataLoader 需要獲取DataSet提供的索引[i]和len;用來幫助我們加載數據&#xff0c;比如說做shuffle(提高數據集的隨機性)&#xff0c;batch_size,能拿出Mini-Batch進行訓練。它…

Windows10環境下MongoDB安裝配置

1. 下載對應MongoDB安裝包 進入官網&#xff1a;MongoDB官網 如果不連接外網則在官網下載較慢&#xff0c;這里給出下載好的安裝包&#xff0c;版本為4.2.25&#xff1a;百度網盤 選擇你需要的版本&#xff0c;推薦選擇Package的格式為zip&#xff08;解壓即可&#xff09; Pa…

[VNCTF2024]-PWN:preinit解析(逆向花指令,繞過strcmp,函數修改,機器碼)

查看保護&#xff1a; 查看ida&#xff1a; 這邊其實看反匯編沒啥大作用&#xff0c;需要自己動調。 但是前面的繞過strcmp還是要看一下的。 解題&#xff1a; 這里是用linux自帶的產生隨機數的文件urandom來產生一個隨機密碼&#xff0c;然后讓我們輸入密碼&#xff0c;用st…

k8s 存儲卷詳解與動靜部署詳解

目錄 一、Volume 卷 1.1 卷類型 emptyDir &#xff1a; hostPath&#xff1a; persistentVolumeClaim (PVC)&#xff1a; configMap 和 secret&#xff1a; 二、 emptyDir存儲卷 2.1 特點 2.2 用途&#xff1a; 2.3 示例 三、 hostPath存儲卷 3.1 特點 3.2 用途 …

前端mock數據 —— 使用Apifox mock頁面所需數據

前端mock數據 —— 使用Apifox 一、使用教程二、本地請求Apifox所mock的接口 一、使用教程 在首頁進行新建項目&#xff1a; 新建項目名稱&#xff1a; 新建接口&#xff1a; 創建json&#xff1a; 請求方法&#xff1a; GET。URL&#xff1a; api/basis。響應類型&#xff1…