集合

集合与数组

数组:
1.一旦初始化,长度就不可修改了;
2.一旦定义后,就只能操作指定类型的数据了;
3.数组提供的方法非常有限,且效率不高;
4.获取数组中实际元素的个数,没有现成的属性和方法可调用;
5.存储特点:有序,可重复。
集合:Java集合分为Collection和Map两种体系

Collection接口:单列数据,存储一个一个的对象
<1> List接口:元素有序,可重复,可以存null,遍历元素使用迭代器以及下标遍历get()
实现类:
ArrayList,作为List接口的主要实现类;线程不安全,效率高;底层使用Object[] elementData存储
LinkedList,对于频繁插入,删除效率较高;底层使用双向链表储存
Vector,作为List接口的古老实现类;线程安全,效率低;底层使用Object[] elementData存储
<2> Set接口:元素无序,不可重复,只能存一个null,便利元素时只能使用迭代器遍历
实现类:
HashSet,作为Set接口的主要实现类;线程不安全;可以存储null
LinkedHashSet,HashSet的子类;遍历时可以按照添加的顺序遍历;对于频繁遍历效率较高
TreeSet,要求数据是同一个类型,可按照对象的指定属性进行排序
Map接口:双列数据,存储具有映射关系”key - value对“
实现类:
HashMap,作为Map的主要实现类;线程不安全,效率高;能存储null的键值对
-> LinkedHashMap,保证在遍历元素时,可以按照添加顺序进行遍历;对于频繁遍历效率较高
->原因:在原有的HashMap底层结构基础上,添加了一对指针,指向前、后的元素。
TreeMap,按照添加的key-value进行排序;底层采用红黑树
Hashtable,作为Map的古老实现类;线程安全,效率低;不能存存储null的键值对
-> Properties,常用来处理配置文件。key和value都是String类型

Collection接口中的方法

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
@Test
public void test1() {
Collection coll1 = new ArrayList();
// add(Object e) 将元素e添加到集合中
coll1.add("aaa");
coll1.add(123);//自动装箱
coll1.add(new Date());
// size() 获取添加元素的个数
System.out.println(coll1.size());// 3
// addAll(Collection coll) 将coll的所有元素添加到当前集合中
Collection coll2 = new ArrayList();
coll2.addAll(coll1);
System.out.println(coll2);// 自动调用toString() [aaa, 123, Wed Mar 04 15:58:00 CST 2020]
// isEmpty() 判断当前集合是否为空
System.out.println(coll1.isEmpty());// false
// clear() 清空集合元素
coll1.clear();
System.out.println(coll1.isEmpty());// true
// contains(Object e) 是否包含某个元素
// 会调用obj类对象的equals()
Collection coll3 = new ArrayList();
coll3.add(1);
coll3.add("abc");
coll3.add(new String("hello"));
System.out.println(coll3);// [1, abc, hello]
System.out.println(coll3.contains("hello"));// true
// containAll(Collection coll) 判断形参coll中所有的元素是否在当前集合中
Collection coll4 = Arrays.asList("abc", 1);
System.out.println(coll3.containsAll(coll4));// true
}

@Test
public void test2() {
Collection coll1 = new ArrayList();
coll1.add(1);
coll1.add(2);
coll1.add(3);
coll1.add("abc");
System.out.println(coll1);// [1, 2, 3, abc]
// remove(Object e)
coll1.remove("abc");
System.out.println(coll1);// [1, 2, 3]
// removeAll(Collection coll) 从当前集合中移除coll中所有的元素
Collection coll2 = Arrays.asList(1, 2);
coll1.removeAll(coll2);
System.out.println(coll1);// [3]
// retainAll(Collection coll) 返回交集
Collection coll3 = new ArrayList();
coll3.add(1);
coll3.add(2);
Collection coll4 = Arrays.asList(1, 2, 3);
coll3.retainAll(coll4);
System.out.println(coll3);// [1, 2]
// toArray() 集合->数组
// 数组->集合 Arrays.asList()
}

迭代器Iterator接口

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
 @Test  //集合元素的遍历,调用迭代器Iterator接口
public void test3() {
Collection coll1 = new ArrayList();
coll1.add(1);
coll1.add(2);
coll1.add(3);
// 实例化迭代器对象
Iterator iterator = coll1.iterator();
// for (int i = 0; i < coll1.size(); i++) {
// System.out.println( iterator.next());
// }
// 起始指针位于第一个元素之前
// hasNext() 判断是否还有下一个元素
// next() 指针下移,将下移以后的集合位置上的元素返回
while(iterator.hasNext()){
Object obj = iterator.next();
if(obj.equals(new Integer(1))){
iterator.remove();
}
}
iterator = coll1.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
}

foreach循环

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
@Test
public void test1(){
Collection coll1 = new ArrayList();
coll1.add(1);
coll1.add(2);
coll1.add(3);
coll1.add(4);
// 使用foreach进行遍历 for(集合元素类型 局部变量 : 集合对象)
for (Object obj:coll1) {//将coll1的元素依次取出并赋给obj
System.out.println(obj);
}
}

@Test
public void test2(){
int[] arr1 = new int[]{1,2,3,4,5};
for (int a:arr1) {
a = 6;
}// 对原数组没有影响
for (int i = 0; i < arr1.length; i++) {
arr1[i] = 6;
}// 对原数组的值进行修改

for (int i = 0; i < arr1.length; i++) {
System.out.println(arr1[i]);
}
}

List常用方法

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
@Test //List常用方法
public void test1(){
// void add(int index, Object ele):在index位置插入ele元素
// boolean addAll(int index, Collection eles):从index位置开始将eles中 的所有元素添加进来
// Object get(int index):获取指定index位置的元素
ArrayList list1 = new ArrayList();
list1.add(1);
list1.add(2);
list1.add(3);
list1.add(4);
list1.add(3);
System.out.println(list1.get(1));// 2
// int indexOf(Object obj):返回obj在集合中首次出现的位置
System.out.println(list1.indexOf(4));// 3
// int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
System.out.println(list1.lastIndexOf(3));// 4
// Object remove(int index):移除指定index位置的元素,并返回此元素
System.out.println("删除索引为2的元素:"+list1.remove(2));// 删除索引为2的元素:3
System.out.println(list1);// [1, 2, 4, 3]
// Object set(int index, Object ele):设置指定index位置的元素为ele
list1.set(1,5);
System.out.println(list1);//[1, 5, 4, 3]
// List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex 位置的子集合
System.out.println(list1.subList(1,4));// [5, 4, 3]
}

LinkedList的可顺序遍历性

1
2
3
4
5
6
7
8
9
10
11
12
13
  @Test // LinkedList可按顺序进行遍历  使用foreach遍历
// LinkedList对数据进行储存时添加了两个引用,用于记录前一个元素和后一个元素的位置,
// 因此可按元素的添加顺序进行遍历,并且对频繁的遍历效率较高
public void test2(){
LinkedList linkedList1 = new LinkedList();
linkedList1.add(1111);
linkedList1.add(2222);
linkedList1.add(3333);
for(Object obj : linkedList1){
System.out.println(obj);
}// 1111 2222 3333
}
}

Set常用方法

HashSet并没有添加新的方法,沿用Collection的方法

HashSet的添加

向Set中添加数据一定要重写hashCode()和equals()方法,相等的对象必须具有相同的散列码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Test // HashSet 无序性  使用迭代器遍历
public void test1(){
// 无序性:不等于随机性,存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值
// 不可重复性:保证添加的元素按equals()进行判断时,不能返回true
/*
添加元素的过程:
向HashSet中添加元素a,首先调用元素a所在类的hashCode(),计算元素a的哈希值,
此哈希值与数组长度-1做与运算,计算出在HashSet底层数组中的存放位置,判断此位置上是否有其他元素:
如果此位置没有其他元素,则元素a添加成功
如果此位置上有元素b(或以链表形式存在的多个元素),则比较a,b的哈希值:
如果哈希值不相同,则元素a添加成功(以链表形式,元素a指向原来的元素,并取代原来元素的位置;jdk8:采用红黑树(查找速度快))
如果哈希值相同,进而调用元素a所在类的equals():
equals()返回true,则元素a添加失败
equals()返回false,则添加成功
*/
HashSet set1 = new HashSet();
set1.add(111);
set1.add(222);
set1.add(333);
Iterator iterator = set1.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}// 333 222 111
}

TreeSet按指定属性进行排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Test //TreeSet按指定属性遍历
public void test3(){
TreeSet treeSet1 = new TreeSet();
// 按大小排序输出
treeSet1.add(1);
treeSet1.add(-1);
treeSet1.add(0);
Iterator iterator = treeSet1.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}// -1 0 1

TreeSet treeSet2 = new TreeSet();
treeSet2.add(new Person("Tom","male",20));
treeSet2.add(new Person("Taylor","female",18));
treeSet2.add(new Person("Troye","male",22));
treeSet2.add(new Person("Justin","male",22));

Iterator iterator2 = treeSet2.iterator();
while(iterator2.hasNext()){
System.out.println(iterator2.next());
}
}

Person类实现Comparable接口并重写toCompare()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override // 按照年龄排序
public int compareTo(Object o) {
if (o instanceof Person) {
Person person = (Person)o;
int compare = this.age-person.age;// 从小到大
if(compare==0){// 年龄相同则按姓名进行排序
return this.name.compareTo(person.name);
}
else{
return compare;
}
}
else{
throw new RuntimeException("输入类型不匹配");
}
}

输出结果:


使用定制排序

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
@Test
public void test4(){
//定制排序
// 实例化Comparator类
Comparator comparator = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof Person && o1 instanceof Person){
return ((Person) o1).getAge()-((Person)o2).getAge();
}
else{
throw new RuntimeException("传入参数错误");
}
}
};
TreeSet treeSet2 = new TreeSet(comparator);// 传入comparator参数
treeSet2.add(new Person("Tom","male",20));
treeSet2.add(new Person("Taylor","female",18));
treeSet2.add(new Person("Troye","male",22));
treeSet2.add(new Person("Justin","male",15));

Iterator iterator2 = treeSet2.iterator();
while(iterator2.hasNext()){
System.out.println(iterator2.next());
}
}

List与数组之间的相互转换和遍历

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
public class Test1 {
public static void main(String[] args) {
Integer[] arr = {1, 2, 3, 4, 5};
// 数组转集合
/**
* Arrays.asList(arr) 返回的不是真正的 ArrayList 而是 Arrays中的一个内部类
*/
ArrayList<Integer> arrToList = new ArrayList<Integer>(Arrays.asList(arr));
System.out.println(arrToList); // [1, 2, 3, 4, 5]
/**
* 遍历方式
*/
// forEach 遍历
for (int num : arrToList) {
System.out.println(num);
}
// java8函数式接口遍历
arrToList.stream().forEach(System.out::println);
arrToList.stream().forEach(num -> {
System.out.println(num);
});

// 集合转数组
ArrayList<Integer> list = new ArrayList<>();
for (int i = 1; i <=5; i++) {
list.add(i);
}
// 初始化数组,指定类型,防止泛型丢失
Integer[] listToArr = new Integer[list.size()];
// 使用有参数的toArray(T[] a)
arrToList.toArray(listToArr);
System.out.println(Arrays.toString(listToArr));// [1, 2, 3, 4, 5]
// 遍历
Arrays.stream(listToArr).forEach(System.out::println);
}
}

Map接口

Map的结构:
Map中的key:无序,不可重复,使用Set储存所有的key,key所在的类要重写equals()和 hashCode()
Map中的value:无序,可重复,使用List存储所有的value,value所在类要重写equals()
一个键值对构成一个entry对象
Map中的entry:无序,不可重复,使用Set储存所有的entry
HashMap的底层实现原理:
HashMap map = new HashMap();
实例化后,底层创建了一个长度为16的一维数组Entry[] table
map.put(key1,value1);
在jdk7中,首先调用key1所在类的hashCode()计算key的哈希值,此哈希值通过某种算法后,得到在Entry数组中的存放位置;
如果此位置为空,则添加成功,
否则将key1的哈希值与此位置上的一个或多个元素(key2,value2)的哈希值比较,
如果哈希值不同,则添加成功,
否则调用key1所在类的equals(),如果返回false,则添加成功,否则添加到链表头部(jdk8插入尾部)。
涉及到扩容时,容量扩大为原来的2倍,原有的数据全部被复制过来。

在jdk8中,没有马上就创建数组,而是在put()时创建Node[]数组

Map接口中的方法

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
  @Test
public void test1() {
// 添加、删除、修改操作:
// Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中
HashMap map1 = new HashMap();
map1.put("A",111);
map1.put("B",222);
map1.put("C",333);
System.out.println(map1);// {A=111, B=222, C=333}
// void putAll(Map m):将m中的所有key-value对存放到当前map中
HashMap map2 = new HashMap();
map2.putAll(map1);
System.out.println(map2);// {A=111, B=222, C=333}
// Object remove(Object key):移除指定key的key-value对,并返回value
Object a = map2.remove("A");
System.out.println(a);// 111
System.out.println(map2);// {B=222, C=333}
// void clear():清空当前map中的所有数据
map2.clear();
System.out.println(map2);// {}
// 元素查询的操作:
// Object get(Object key):获取指定key对应的value
Object b = map1.get("B");
System.out.println(b);// 222
// boolean containsKey(Object key):是否包含指定的key
System.out.println(map1.containsKey("D"));// false
// boolean containsValue(Object value):是否包含指定的value
System.out.println(map1.containsValue(222));// true
// int size():返回map中key-value对的个数
System.out.println(map2.size());// 0
System.out.println(map1.size());// 4
// boolean isEmpty():判断当前map是否为空
System.out.println(map2.isEmpty());// true
// boolean equals(Object obj):判断当前map和参数对象obj是否相等
// 元视图操作的方法:
// Set keySet():返回所有key构成的Set集合
Set set1 = map1.keySet();
Iterator iterator = set1.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}// A B C
// Collection values():返回所有value构成的Collection集合
Collection coll1 = map1.values();
Iterator iterator1 = coll1.iterator();
while(iterator1.hasNext()){
System.out.println(iterator1.next());
}// 111 222 333
// Set entrySet():返回所有key-value对构成的Set集合
Set set2 = map1.entrySet();
System.out.println(set2);// [A=111, B=222, C=333]
Iterator iterator2 = set2.iterator();
while(iterator2.hasNext()){
Object obj = iterator2.next();
Map.Entry entry = (Map.Entry) obj;
System.out.println(entry.getKey() +"==="+entry.getValue());
}
}

集合初始化时,指定集合初始值大小。如果暂时无法确定集合大小,那么指定相应的默认值,这也要求我们记得各种集合的默认值大小, ArrayList 大小为10 HashMap 默认值为 16 。

在HashMap中initialCapacity = (需要存储的元素个数 / 负载因子) + 1。注意负载因子(即 loader factor)默认为 0.75。

​ —–来自《阿里java开发手册》

集合工具类

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
    @Test
public void test1(){
// 排序操作:(均为static方法)
// reverse(List):反转List 中元素的顺序
List list = new ArrayList();
list.add(111);
list.add(222);
list.add(333);
list.add(444);
System.out.println(list);// [111, 222, 333, 444]
Collections.reverse(list);
System.out.println(list);// [444, 333, 222, 111]
// shuffle(List):对List 集合元素进行随机排序
Collections.shuffle(list);
System.out.println(list);
// sort(List):根据元素的自然顺序对指定List 集合元素按升序排序
Collections.sort(list);
System.out.println(list);// [111, 222, 333, 444]
// sort(List,Comparator):根据指定的Comparator 产生的顺序对List 集合元素进行排序
// swap(List,int,int):将指定list 集合中的i 处元素和j 处元素进行交换

// 查找、替换
// Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
// Object max(Collection,Comparator):根据Comparator 指定的顺序,返回给定集合中的最大元素
// Object min(Collection)
// Object min(Collection,Comparator)
// int frequency(Collection,Object):返回指定集合中指定元素的出现次数
// void copy(List dest,List src):将src中的内容复制到dest中

// boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换List 对象的所有旧值

}