新手了解java 集合基礎(chǔ)知識(shí)
一、概述
集合是一種長(zhǎng)度可變,存儲(chǔ)數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)多樣,存儲(chǔ)對(duì)象多樣的一種數(shù)據(jù)容器。Java中集合可分為:List集合、Set集合、HashMap集合,等。
Java集合體系結(jié)構(gòu):
二、collection
collection是Java中所有值存儲(chǔ)集合的頂級(jí)接口,因此它的所有直接或者間接實(shí)現(xiàn)類都有它的非私有方法,我們可以從它的方法開(kāi)始了解這個(gè)體系的功能實(shí)現(xiàn)。
boolean add(E e) 確保此 collection 包含指定的元素。 boolean addAll(Collection<? extends E> c) 將指定 collection 中的所有元素都添加到此 collection 中。 void clear() 移除此 collection 中的所有元素。 boolean contains(Object o) 如果此 collection 包含指定的元素,則返回 true。 boolean containsAll(Collection<?> c) 如果此 collection 包含指定 collection 中的所有元素,則返回 true。 boolean equals(Object o) 比較此 collection 與指定對(duì)象是否相等。 int hashCode() 返回此 collection 的哈希碼值。 boolean isEmpty() 如果此 collection 不包含元素,則返回 true。 Iterator<E> iterator() 返回在此 collection 的元素上進(jìn)行迭代的迭代器。 boolean remove(Object o) 從此 collection 中移除指定元素的單個(gè)實(shí)例,如果存在的話)。 boolean removeAll(Collection<?> c) 移除此 collection 中那些也包含在指定 collection 中的所有元素。 boolean retainAll(Collection<?> c) 僅保留此 collection 中那些也包含在指定 collection 的元素。 int size() 返回此 collection 中的元素?cái)?shù)。 Object[] toArray() 返回包含此 collection 中所有元素的數(shù)組。 <T> T[] toArray(T[] a) 返回包含此 collection 中所有元素的數(shù)組;返回?cái)?shù)組的運(yùn)行時(shí)類型與指定數(shù)組的運(yùn)行時(shí)類型相同。
1、List
List,是單列集合,存儲(chǔ)的是一組插入有序的數(shù)據(jù),并且數(shù)據(jù)可以重復(fù)。
List集合
- LinkedList
- ArrayList
1)ArrayList
示例:
public class CollectionTest { public static void main(String[] args) { List list = new ArrayList(); //添加元素,boolean add(E e) 確保此 collection 包含指定的元素 list.add("張三"); list.add(1); list.add('A'); System.out.println(list);//[張三, 1, A] //boolean addAll(Collection<? extends E> c) // 將指定 collection 中的所有元素都添加到此 collection 中 List list1 = new ArrayList(); list.add("java"); list.add("MySQL"); list.addAll(list1); System.out.println(list);//[張三, 1, A, java, MySQL] //boolean contains(Object o) // 如果此 collection 包含指定的元素,則返回 true。 System.out.println(list.contains("java"));//true //boolean remove(Object o) // 從此 collection 中移除指定元素的單個(gè)實(shí)例,如果存在的話)。 System.out.println(list.remove("java"));//true // int size() // 返回此 collection 中的元素?cái)?shù)。 System.out.println(list.size());//4 //set(int index, E element) // 用指定的元素替代此列表中指定位置上的元素。 //并返回被修改的值 System.out.println(list.set(1, "李四")); //get(int index) // 返回此列表中指定位置上的元素。 System.out.println(list.get(1)); // Iterator<E> iterator() // 返回在此 collection 的元素上進(jìn)行迭代的迭代器。 //集合的遍歷 Iterator iterator = list.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); } }
說(shuō)明:ArrayList底層是使用數(shù)組的形式創(chuàng)建集合的,因此基于數(shù)組的特性,此集合對(duì)數(shù)據(jù)的查找很快速,但是在刪除或移動(dòng)大量數(shù)據(jù)操作上會(huì)顯得緩慢。它適合用于快速查找,但不適合做刪除多的操作。
2)LinkedList
LinkedList:雙向鏈表,內(nèi)部沒(méi)有聲明數(shù)組,而是定義了Node類型的first 和last,用于記錄首末元素。同時(shí),定義內(nèi)部類Node,作為L(zhǎng)inkedList中 保存數(shù)據(jù)的基本結(jié)構(gòu)。Node除了保存數(shù)據(jù),還定義了兩個(gè)變量:
- prev變量記錄前一個(gè)元素的位置
- next變量記錄下一個(gè)元素的位置
特點(diǎn):
- 數(shù)據(jù)有序
- 底層結(jié)構(gòu)為鏈表
ArrayList比較:
- LinkedList的添加元素速度比ArrayList快;
- LinkedList的查詢速度比ArrayList慢;
- 底層數(shù)據(jù)結(jié)構(gòu)不同:LinkedList用的是鏈表結(jié)構(gòu),而ArrayList底層使用 的是數(shù)組結(jié)構(gòu);
說(shuō)明:LinkedList一般用于添加頻繁的操作,ArrayList一般用于頻繁查詢 的操作。
示例:
public class Stack { private LinkedList data = null; public Stack(){ data = new LinkedList(); } // 添加元素 public boolean push(Object element) { data.addFirst(element); return true; } // 獲取元素 public Object pop() { return data.pollFirst(); } // 判斷集合是否為空 public boolean isEmpty() { return data.isEmpty(); } // 迭代元素 public void list() { Iterator it = data.iterator(); while(it.hasNext()){ System.out.println(it.next()); } } } public class MyStack { public static void main(String[] args) { Stack stack = new Stack(); stack.push("張三"); stack.push("李四"); stack.push("王五"); stack.list(); System.out.println("-------------"); Object pop = stack.pop(); System.out.println(pop); } }
2、set
1)HashSet
HashSet 是 Set 接口的典型實(shí)現(xiàn),大多數(shù)時(shí)候使用 Set 集合時(shí)都使用 這個(gè)實(shí)現(xiàn)類。
- HashSet 按 Hash 算法來(lái)存儲(chǔ)集合中的元素,因此具有很好的存取、 查找、刪除性能。
- HashSet 具有以下特點(diǎn):不能保證元素的排列順序
- HashSet 不是線程安全的
- 集合元素可以是 null
- 不能添加重復(fù)元素
- HashSet 集合判斷兩個(gè)元素相等的標(biāo)準(zhǔn):兩個(gè)對(duì)象通過(guò) hashCode() 方法比較相等,并且兩個(gè)對(duì)象的 equals() 方法返回值也相等。
- 對(duì)于存放在Set容器中的對(duì)象,對(duì)應(yīng)的類一定要重寫equals()和 hashCode(Object obj)方法,以實(shí)現(xiàn)對(duì)象相等規(guī)則。即:“相等的對(duì)象必須具有相等的散列碼”。
示例:
public static void main(String[] args) { Set set = new HashSet(); // 添加 // boolean add(E e) :把指定的元素添加到集合中 set.add("hello"); set.add("world"); set.add("world"); set.add(null); System.out.println(set); // 注:Set集合中元素是無(wú)序,并且不能重復(fù) // boolean addAll(Collection<? extends E> c) :把指定的集合添加到集合中 Set set1 = new HashSet(); set1.add("aaa"); set1.add("linux"); ; set.addAll(set1); System.out.println(set); // boolean remove(Object o) :從集合中刪除指定元素 set.remove("hello"); System.out.println(set); // boolean removeAll(Collection<?> c) :從集合中刪除指定集合中的所有元素 set1.add("aaa"); set1.add("linux"); set.removeAll(set1); System.out.println(set); // void clear() :清空集合中所有元素 set.clear(); System.out.println(set); // int size() :獲取集合的元素個(gè)數(shù) int size = set.size(); System.out.println(size); // boolean contains(Object o) :判斷集合中是否包含指定元素,包含為true,否則為false; System.out.println(set.contains("aaa")); // boolean isEmpty() :判斷集合是否為空 System.out.println(set.isEmpty()); }
說(shuō)明:在HashSet添加元素時(shí),會(huì)首先比較兩個(gè)元素的hashCode值是不相等,如 果不相等則直接添加;如果相等再判斷兩個(gè)元素的equals的值是否相等, 如果相等則不添加,如果不相等則添加。
2)TreeSet
- TreeSet和TreeMap采用紅黑樹(shù)的存儲(chǔ)結(jié)構(gòu)
- 特點(diǎn):有序,查詢速度比List快
使用TreeSet集合是,對(duì)象必須具有可比較性。而要讓對(duì)象具有可比較性有 兩種方式:
第一種:實(shí)現(xiàn)Comparable
接口,并重寫compareTo()
方法:
第二種:寫一個(gè)比較器類,讓該類去實(shí)現(xiàn)Comparator
接口,并重寫 comare()
方法。
示例:
1.實(shí)體類
public class Student implements Comparable<Student>{ private String name; private int age; private String sex; private int height; public Student() { } public Student(String name, int age, String sex, int height) { this.name = name; this.age = age; this.sex = sex; this.height = height; } 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 String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; return age == student.age && height == student.height && Objects.equals(name, student.name) && Objects.equals(sex, student.sex); } @Override public int hashCode() { return Objects.hash(name, age, sex, height); } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", sex='" + sex + '\'' + ", height=" + height + '}'; } @Override public int compareTo(Student stu) { if (stu.getAge() > this.getAge()){ return 1; } if (stu.getAge() < this.getAge()){ return -1; } return stu.getName().compareTo(this.getName()); } }
2.測(cè)試類:
public class TreeSetTest { public static void main(String[] args) { TreeSet treeSet = new TreeSet(); Student student1 = new Student("張三", 20, "男", 165); Student student2 = new Student("李四", 21, "男", 170); Student student3 = new Student("王五", 19, "女", 160); Student student4 = new Student("趙六", 18, "女", 165); Student student5 = new Student("田七", 20, "男", 175); treeSet.add(student1); treeSet.add(student2); treeSet.add(student3); treeSet.add(student4); treeSet.add(student5); System.out.println(treeSet); } }
3.實(shí)體類
public class Teacher { private String name; public Teacher(){} public Teacher(String name){ this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Teacher{" + "name='" + name + '\'' + '}'; } }
4.測(cè)試類
public class TreeSetTest2 { public static void main(String[] args) { Teacher teacher1 = new Teacher("11"); Teacher teacher2 = new Teacher("12"); Teacher teacher3 = new Teacher("13"); Teacher teacher4 = new Teacher("14"); Teacher teacher5 = new Teacher("15"); TreeSet treeSet1 = new TreeSet(new Comparator() { @Override public int compare(Object o1, Object o2) { return o1.hashCode() - o2.hashCode(); } }); treeSet1.add(teacher1); treeSet1.add(teacher2); treeSet1.add(teacher3); treeSet1.add(teacher4); treeSet1.add(teacher5); System.out.println(treeSet1); } }
說(shuō)明:HashSet
去重是依靠hashCode
和equals()
方法,而TreeSet去重則 依靠的是比較器。
三、Map
存儲(chǔ)的雙列元素,Key是無(wú)序的,不可重復(fù),而Value是無(wú)序,可重復(fù)的。
1、HashMap
public class HashMapDemo { private Map map = null; public void init() { map = new HashMap(); map.put("a", "aaa"); map.put("b", "bbb"); map.put("c", "ccc"); System.out.println(map); } // 添加元素 public void testPut() { // V put(K key, V value) :把指定的key和value添加到集合中 map.put("a1", "aaa"); map.put("b1", "bbb"); map.put("c1", "ccc"); System.out.println(map); // void putAll(Map<? extends K,? extends V>m) :把指定集合添加集合中 Map map1 = new HashMap(); map1.put("e", "eee"); map1.put("f", "fff"); map.putAll(map1); System.out.println(map); // default V putIfAbsent(K key, V value) :如果key不存在就添加 map.putIfAbsent("a", "hello"); System.out.println(map); map.putIfAbsent("g", "ggg"); System.out.println(map); } // 修改元素 public void testModify() { // V put(K key, V value) :把集合中指定key的值修改為指定的值 map.put("a", "hello"); map.put("a", "world"); System.out.println(map); // 說(shuō)明,當(dāng)key相同時(shí),后面的值會(huì)覆蓋前面的值。 // default V replace(K key, V value) :根據(jù)key來(lái)替換值,而不做增加操作 Object replace = map.replace("b1", "java"); System.out.println(replace); System.out.println(map); //default boolean replace(K key, V oldValue,V newValue) } // 刪除元素 public void testRemove() { // V remove(Object key) :根據(jù)指定key刪除集合中對(duì)應(yīng)的值 Object c = map.remove("c"); System.out.println(c); System.out.println(map); // default boolean remove(Object key, Objectvalue) :根據(jù)key和value進(jìn)行刪除 map.remove("b", "bbb1"); System.out.println(map); // void clear() :清空集合中所有元素 map.clear(); System.out.println(map); } // 判斷元素 public void testJudge() { // boolean isEmpty() :判斷集合是否為空,如果是返回true,否則返回false System.out.println(map.isEmpty()); // boolean containsKey(Object key) :判斷集合中是否包含指定的key,包含返回true,否則返回false boolean flag = map.containsKey("a"); System.out.println(flag); // true flag = map.containsKey("a1"); System.out.println(flag); // false // boolean containsValue(Object value) :判斷集合中是否包含指定的value,包含返回true,否則返回false flag = map.containsValue("aaa"); System.out.println(flag); // true flag = map.containsValue("aaa1"); System.out.println(flag); // false } // 獲取元素 public void testGet() { // int size() :返回集合的元素個(gè)數(shù) int size = map.size(); System.out.println(size); // V get(Object key) :根據(jù)Key獲取值,如果找到就返回對(duì)應(yīng)的值,否則返回null Object val = map.get("a"); System.out.println(val); val = map.get("a1"); System.out.println(val); // null // default V getOrDefault(Object key, VdefaultValue) :根據(jù)Key獲取值,如果key不存在,則返回默認(rèn)值 val = map.getOrDefault("a1", "hello"); System.out.println(val); // Collection<V> values() :返回集合中所有的Value Collection values = map.values(); for (Object value : values) { System.out.println(value); } // Set<K> keySet() :返回集合中所有的Key Set set = map.keySet(); for (Object o : set) { System.out.println(o); } } // 迭代元素 public void testIterator() { // 第一種:通過(guò)key獲取值的方式 Set keySet = map.keySet(); Iterator it = keySet.iterator(); while (it.hasNext()) { Object key = it.next(); Object val = map.get(key); System.out.println(key + "=" + val); } System.out.println("------------------------ "); // 第二種:使用for循環(huán) for (Object key : map.keySet()) { System.out.println(key + "=" + map.get(key)); } System.out.println("------------------------ "); // 第三種:使用Map接口中的內(nèi)部類來(lái)完成,在框架中大量使用 Set entrySet = map.entrySet(); for (Object obj : entrySet) { Map.Entry entry = (Map.Entry) obj; System.out.println(entry.getKey() + "=" + entry.getValue()); } } }
說(shuō)明:在HashMap中鍵-值允許為空,但鍵唯一,值可重復(fù)。hashMap不是線程安全的。
2、TreeMap
是一個(gè)有序的集合,默認(rèn)使用的是自然排序方式。
public class Person implements Comparable { private String name; private int age; @Override public int compareTo(Object o) { if (o instanceof Person) { Person p = (Person) o; return this.age - p.age; } return 0; } public Person() {} public Person(String name, int age) { this.name = name; this.age = age; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
測(cè)試
public class TeeMapDemo { @Test public void testInteger() { TreeMap tm = new TreeMap(); tm.put(3, 333); tm.put(2, 222); tm.put(11, 111); tm.put(2, 222); System.out.println(tm); } @Test public void testString() { TreeMap tm = new TreeMap(); tm.put("hello", "hello"); tm.put("world", "world"); tm.put("about", ""); tm.put("abstract", ""); System.out.println(tm); } @Test public void testPerson() { TreeMap tm = new TreeMap(new Comparator(){ @Override public int compare(Object o1, Object o2) { if (o1 instanceof Person && o2 instanceof Person) { Person p1 = (Person) o1; Person p2 = (Person) o2; return p1.getAge() - p2.getAge(); } return 0; } }); tm.put(new Person("張三",18), null); tm.put(new Person("李四",17), null); System.out.println(tm); } }
說(shuō)明:從上面的代碼可以發(fā)現(xiàn),TreeMap的使用和TreeSet的使用非常相似,觀察HashSet集合的源代碼可以看出,當(dāng)創(chuàng)建 HashSet集合時(shí),其實(shí)是底層使用的是HashMap。
public HashSet() { map = new HashMap<>(); }
HashSet實(shí)際上存的是HashMap的Key。
3.ConcurrentHashMap
在Map集合中我們介紹了HashMap
,TreeMap
,在多線程的情況下這些集合都不是線程安全的,因此可能出現(xiàn)線程安全的問(wèn)題。
在Java中Hashtable是一種線程安全的HashMap
,Hashtable
在方法上與HashMap
并無(wú)區(qū)別,僅僅只是在方法使用了synchronized
以此來(lái)達(dá)到線程安全的目的,我們觀察Hashtable的源碼。
public synchronized V get(Object key) { Entry<?,?> tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { return (V)e.value; } } return null; }
以上是Hashtable的get源碼,可以看出它僅僅只是在方法上添加了鎖,這大大降低了線程的執(zhí)行效率,以犧牲效率的形式來(lái)達(dá)到目的,這顯然不是我們?cè)趯?shí)際中想要的,因此我們需要一種既能在線程安全方面有保障,在效率上還可以的方法。
ConcurrentHashMap采用的是分段鎖的原理,我們觀察源碼。
public V put(K key, V value) { return putVal(key, value, false); } final V putVal(K key, V value, boolean onlyIfAbsent) { if (key == null || value == null) throw new NullPointerException(); int hash = spread(key.hashCode()); int binCount = 0; for (Node<K,V>[] tab = table;;) { Node<K,V> f; int n, i, fh; if (tab == null || (n = tab.length) == 0) tab = initTable(); else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null))) break; // no lock when adding to empty bin } else if ((fh = f.hash) == MOVED) tab = helpTransfer(tab, f); else { V oldVal = null; synchronized (f) { if (tabAt(tab, i) == f) { if (fh >= 0) { binCount = 1; for (Node<K,V> e = f;; ++binCount) { K ek; if (e.hash == hash && ((ek = e.key) == key || (ek != null && key.equals(ek)))) { oldVal = e.val; if (!onlyIfAbsent) e.val = value; break; } Node<K,V> pred = e; if ((e = e.next) == null) { pred.next = new Node<K,V>(hash, key, value, null); break; } } } else if (f instanceof TreeBin) { Node<K,V> p; binCount = 2; if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key, value)) != null) { oldVal = p.val; if (!onlyIfAbsent) p.val = value; } } } } if (binCount != 0) { if (binCount >= TREEIFY_THRESHOLD) treeifyBin(tab, i); if (oldVal != null) return oldVal; break; } } } addCount(1L, binCount); return null; }
從源碼中可以看出ConcurrentHashMap
僅僅是在當(dāng)有線程去操作當(dāng)前數(shù)據(jù)的時(shí)候添加了鎖,因此效率大大提高了。
在線程安全的情況下提高了效率。
總結(jié)
本篇文章就到這里了,希望能對(duì)你有所幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
SpringBoot實(shí)現(xiàn)License生成和校驗(yàn)的過(guò)程詳解
在我們向客戶銷售商業(yè)軟件的時(shí)候,常常需要對(duì)所發(fā)布的軟件實(shí)行一系列管控措施,諸如驗(yàn)證使用者身份、軟件是否到期,以及保存版權(quán)信息和開(kāi)發(fā)商詳情等,所以本文給大家介紹了SpringBoot實(shí)現(xiàn)License生成和校驗(yàn)的過(guò)程,需要的朋友可以參考下2024-09-09application作用域?qū)崿F(xiàn)用戶登錄擠掉之前登錄用戶代碼
這篇文章主要介紹了application作用域?qū)崿F(xiàn)用戶登錄擠掉之前登錄用戶代碼,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11JavaFX實(shí)現(xiàn)拖拽結(jié)點(diǎn)效果
這篇文章主要為大家詳細(xì)介紹了JavaFX實(shí)現(xiàn)拖拽結(jié)點(diǎn)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-12-12Java構(gòu)造方法 super 及自定義異常throw合集詳解用法
異常是程序中的一些錯(cuò)誤,但不是所有錯(cuò)誤都是異常,且錯(cuò)誤有時(shí)候是可以避免的,super可以理解為是指向自己超(父)類對(duì)象的一個(gè)指針,而這個(gè)超類指的是離自己最近的一個(gè)父類,構(gòu)造器也叫構(gòu)造方法、構(gòu)造函數(shù),是一種特殊類型的方法,負(fù)責(zé)類中成員變量(域)的初始化2021-10-10java 實(shí)現(xiàn)web項(xiàng)目啟動(dòng)加載properties屬性文件
這篇文章主要介紹了java 實(shí)現(xiàn)web項(xiàng)目啟動(dòng)加載properties屬性文件,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08SpringBoot中自定義首頁(yè)(默認(rèn)頁(yè))及favicon的方法
這篇文章主要介紹了SpringBoot中如何自定義首頁(yè)(默認(rèn)頁(yè))及favicon,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-08-08將本地jar包安裝進(jìn)入maven倉(cāng)庫(kù)(實(shí)現(xiàn)方法)
下面小編就為大家?guī)?lái)一篇將本地jar包安裝進(jìn)入maven倉(cāng)庫(kù)(實(shí)現(xiàn)方法)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06