本文最后更新于:2021年10月8日 晚上
集合、数组都是对多个数据进行存储操作的结构,简称Java容器
先寄出这张经典老图

Java 容器类主要分为以下两类:
一、Collection
常用方法,注:向Collection接口的实现类的对象中添加数据obj时,一般要求obj所在类要重写equals()
- contains(Object obj):判断当前集合是否包含obj
- containsAll(Collection list1):判断list1中存在的元素是否都存在当前集合中
- remove(Object obj):从当前集合中移除obj元素
- removeAll(Collection list1):从当前集合中移除list1中的所有元素
- retainAll(Collection list1):交集:获取当前集合和list1集合的交集,并返回给当前集合
- equals(Object obj):判断当前形参和当前集合的元素是否相同,如果实现Collection的是ArrayList类,则顺序也要一致才会返回true;
- hashCode():返回当前对象的哈希值
- toArray():将集合转换为数组 ————————> 拓展:将数组转化为集合,调用Array类的静态方法asList()
- iterator():返回iterator接口的实例,用于遍历集合的元素
1.List:有序的、可重复的
ArrayList
LinkedList
Vector
List接口的常用方法
- void add(intindex, Object ele):在index位置插入ele元素
- boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来
- Object get(int index):获取指定index位置的元素
- int indexOf(Object obj):返回obj在集合中首次出现的位置
- int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
- Object remove(int index):移除指定index位置的元素,并返回此元素
- Object set(int index, Object ele):设置指定index位置的元素为ele
- List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
| import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List;
public class ListTest {
@Test public void test3(){ ArrayList list = new ArrayList(); list.add(123); list.add(456); list.add("AA");
Iterator iterator = list.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); }
System.out.println("***************");
for(Object obj : list){ System.out.println(obj); }
System.out.println("***************");
for(int i = 0;i < list.size();i++){ System.out.println(list.get(i)); } }
@Test public void tets2(){ ArrayList list = new ArrayList(); list.add(123); list.add(456); list.add("AA"); list.add(new Person("Tom",12)); list.add(456); int index = list.indexOf(4567); System.out.println(index);
System.out.println(list.lastIndexOf(456));
Object obj = list.remove(0); System.out.println(obj); System.out.println(list);
list.set(1,"CC"); System.out.println(list);
List subList = list.subList(2, 4); System.out.println(subList); System.out.println(list); }
@Test public void test(){ ArrayList list = new ArrayList(); list.add(123); list.add(456); list.add("AA"); list.add(new Person("Tom",12)); list.add(456);
System.out.println(list);
list.add(1,"BB"); System.out.println(list);
List list1 = Arrays.asList(1, 2, 3); list.addAll(list1);
System.out.println(list.size());
System.out.println(list.get(2));
} }
|
关于remove()方法的面试题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import org.junit.Test;
import java.util.ArrayList; import java.util.List;
public class ListEver {
@Test public void testListRemove() { List list = new ArrayList(); list.add(1); list.add(2); list.add(3); updateList(list); System.out.println(list); }
private void updateList(List list) {
list.remove(new Integer(2)); } }
|
2.Set:无序的、不可重复的
- Set接口中没有定义新的方法,使用的都是collection中声明过的方法
- 要求:向Set(主要指:HashSet、LinkedHashSet)中添加的数据,其所在的类一定要重写hashCode()和equals()
- 要求:重写的hashCode()和equals()尽可能保持一致性:相等的对象必须具有相等的散列码
- 重写两个方法的小技巧:对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值。
2.1 HashSet:作为Set接口的主要实现类,线程不安全,可以存储null值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
|
IDE中对hashCode()方法的重写,为什么用31这个数字?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((id == null) ? 0 : id.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; }
|
- LinkedHashSet:作为HashSet的子类,遍历其内部数据时,可以按照添加的顺序遍历
2.2 TreeSet:可以按照添加的对象的指定属性,进行排序
- 向TreeSet中添加数据,要求是相同类的对象
- 两种排序方式:自然排序(实现Comparable接口)和自定义排序(Comparator)
- 自然排序中,比较两个对象是否有相同的标准:compareTo()返回0,不是equals().
- 定制排序中,比较两个对象是否相同的标准为:compare()返回0.不再是equals().
3.Queue
二、Map:双列数据,存储Key-Value对的数据
1.HashMap:Map的主要实现类:线程不安全的,效率高,可以存储null的key和value
HashMap的底层:数组+链表(jdk7及以前),数组+链表+红黑树(jdk8)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| HashMap的底层实现原理: JDK7: HashMap hashMap = new HashMap(); hashMap.put("xxx",xxx); ...可能已经执行多次put... hashMap.put(key,value); 首先,调用key所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置。 如果此位置上的数据为空,此时的key-value添加成功。 ---情况1 如果此位置上的数据不为空,意味着此位置上有一个或多个数据(以链表形式存在),比较key和已存在的一个或多个数据的哈希值: 如果key的哈希值与已经存在的数据的哈希值都不相同,此时key-value添加成功。 ---情况2 如果key的哈希值与已经存在的某个数据(key1,value1)的哈希值相同,继续比较:调用key所在类的equals()方法,比较: 如果equals()返回false:此时key-value添加成功。 ---情况3 如果equals()返回true:使用value替换value1。 注:关于情况2和清空3:此时的key-value和原来的数据以链表的方式存储 在不断的添加过程中,会涉及到扩容问题,当超出临界值(且要存放的位置非空)时,扩容。默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来。
JDK8 相较于JDK7在底层的实现方面的不通: 1.new HashMap():底层没有创建一个长度为16的数组。 2.JDK8底层的数组是Node[],而非Entry[] 3.在首次调用put()方法时,底层创建长度为16的数组 4.JDK7的底层结构只有:数组+链表。JDK8中结构:数组+链表+红黑树。 当数组的某一个索引位置上的元素以链表的形式存在的个数 > 8 且当前数组长度 >64,此时此索引位置上的所数据改为使用红黑树存储。
|
- LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序实现遍历。原因:在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。对于频繁的遍历操作,此类执行效率优于HashMap
2.HashTable:古老的实现类,线程安全,效率低,不能存储null的key和value
- Properties:常用用来处理配置文件,key和value都是String类型
3.TreeMap:保证按照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或自定义排序,底层使用红黑树