Java collcetion

写在开头

 图片的问题暂时还没有解决,主要是我没有备案的域名啊,而且对我来说也没有什么必要的,我正在想办法把图片转移到阿里云OSS上,还需要一点时间……emmmm我们多贴一点代码,代码才是王道!

 今天我们说一下Java编程的很重要的一部分:Java集合.

集合

为什么要引入集合

举个栗子

要求1: 存储3个学生的信息(姓名,年龄),能够将3个学生的信息展示出来
数组: 存储基本数据类型 int[] i = new int[3]; String[] s = new String[3];

要求2: 除了三个学生之外,后还要添加一个学生
原数组做不到!

原因是因为数组,是一个定长的容器,定义时给定长度,容量就不能改变

集合: 集合也是一个容器,承装一些数据,有一些集合的底层也是通过数组的方法来进行数据的存储. 但是,如果数据增加,集合底层在没有地方存储数据时,再为你开辟一个更大的空间用于存储数据,因此集合相对于数组来说,容量可变

代码:

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
package io.cnfox.collectiont;

public class Student {

private String name;
private int age;
//重写 get set
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;
}
//重写toString
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
//重写hashCode 和 equals
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
//有参构造
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
//空参构造
public Student() {
super();
// TODO Auto-generated constructor stub
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package io.cnfox.collectiont;

import java.util.Arrays;

public class Collectiont {

public static void main(String[] args) {
// 数组中存储引用数据类型
//设置数组的容量为3
Student[] s = new Student[3];
//对数组进行赋值
s[0]= new Student("张三", 21);
s[1]= new Student("李四", 23);
s[2]= new Student("王五", 25);
//对数组中数据进行输出
System.out.println(Arrays.toString(s));

//如果要求再增加一个学生的信息到数组容器中,原数组没法实现,只能重新创建数组
}
}

两者的相同与不同

相同点

  • 两者都是作为一个容器来进行数据的存储.

不同点

  • 数组定长,定义时必须给定一个长度,而且已经定义容器的大小就不能改变.

  • 数组不定长,定义是不必给定一个长度,是一个长度可变的容器.

  • 数组可以存储基本数据类型,也可以存储引用数据类型.

int [] i = new int[3]

  • 集合只能存储引用数据类型,如果想存储基本数据类型,需要进行Zion给装箱拆箱操作,将基本数据类型自动装箱,编程对应的应用数据类型,用数据包装类,存储在集合中.

  • 数组使用简单,方法继承自Object类中,属性求取数组的长度 length.

  • 数组中集合了很多方法,不仅仅继承自Object类中 还封装了很多自己的方法.

  • 如果数据内容固定,一般使用数组.

  • 如果要进行容器的增删改查操作,使用集合进行操作会相对简便.

Collection接口

框架集合分类图

Collection 接口的常用方法

Collection : 接口,java.util.Collection,需要找到一个实现类,要求实现类中将接口的所有抽象方法都重写了一遍

最常用的ArrayList java.util.ArrayList

定义方式: Collection c = new ArrayList(); // 接口的多态

方法:

  1. add(Object obj): 向集合中添加元素,默认添加在集合的末尾位置,返回值类型为boolean,如果项集合中添加元素成功,返回true,添加元素失败,返回false
  2. remove(Object obj): 将集合中的某一个元素删除掉
    将集合中的某个元素进行删除,删除成功返回值为true,没有删除成功,返回false
  3. clear() : 将集合进行清空,将里面的所有元素,全部删除,但是集合仍然存在的,没有返回值
  4. isEmpty() : 判断集合中是否有数据,没有数据,返回值为true,有数据,返回false
    isEmpty() : 使用场景,如果要针对集合进行遍历, 1. 先判断集合 != null 2. 集合中有数据遍历才有意义
  5. size() : 获取到集合中数据的个数,返回值int类型
  6. contains(Object obj): 判断集合中是否包含某一个元素obj,包含,返回true,不包含,返回false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.util.ArrayList;
import java.util.Collection;

public class CollectionDemo1 {

public static void main(String[] args) {
// 黄的的警告,今天先不理会,原因是定义集合时,没有给集合添加泛型,集合存储数据时,有可能会发生风险
Collection c = new ArrayList();
// add方法,向集合中添加元素,添加元素成功,就会返回true,但是这个返回一般对于程序的逻辑没有什么实际的用途
// List系列集合中,add方法返回值都是true,因为List中可以存储任意的对象类型
// 设置boolean返回值的原因: Collection 接口List Set(去重复,如果你向set中添加一个重复元素,结果false)
boolean b = c.add("a");
boolean b1 = c.add("b");
boolean b2 = c.add("c");
boolean b3 = c.add("d");

// 默认调用了对象的toString方法,索引ArrayList中重写了父类Object中的方法toString
System.out.println(c);
System.out.println(b + "----" + b1 + "----" + b2 + "----" + b3);
}

}
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
import java.util.ArrayList;
import java.util.Collection;

public class CollectionDemo1 {

public static void main(String[] args) {
// 黄的的警告,今天先不理会,原因是定义集合时,没有给集合添加泛型,集合存储数据时,有可能会发生风险
Collection c = new ArrayList();
// 1. add方法,添加元素成功,就会返回true,但是这个返回一般对于程序的逻辑没有什么实际的用途
// List系列集合中,add方法返回值都是true,因为List中可以存储任意的对象类型
// 设置boolean返回值的原因: Collection 接口List Set(去重复,如果你向set中添加一个重复元素,结果false)
boolean b = c.add("a");
boolean b1 = c.add("b");
boolean b2 = c.add("c");
boolean b3 = c.add("d");

System.out.println("------"+c.size());// 4
boolean b7 = c.contains("a");
System.out.println("+++++++++"+b7);//true

// 默认调用了对象的toString方法,索引ArrayList中重写了父类Object中的方法toString
System.out.println(c);// [a, b, c, d]
//true----true----true----true
System.out.println(b + "----" + b1 + "----" + b2 + "----" + b3);

// 2. 将集合中的某个元素进行删除,删除成功返回值为true,没有删除成功,返回false
boolean b4 = c.remove("a");
System.out.println(b4);// true
System.out.println(c);// [b, c, d]

// 3. 清空集合,将集合中的所有元素进行删除,但是集合仍然存在
c.clear();
System.out.println(c);// []

// 4. isEmpty() : 判断集合中是否有数据,没有数据,返回值为true,有数据,返回false
// isEmpty() : 使用场景,如果要针对集合进行遍历, 1. 先判断集合 != null 2. 集合中有数据遍历才有意义
boolean b5 = c.isEmpty();
System.out.println(b5);// true

// 5. size() : 获取到集合中数据的个数
int size = c.size();
System.out.println("------"+size);// 0

// 6. contains(Object obj) : 判断集合中是否包含某一个元素obj,包含,返回true,不包含,返回false

boolean b6 = c.contains("a");
System.out.println(b6);// false
}
}

其他ALL方法:

  1. addAll(Collection c): 表示将一个集合c添加到另外一个集合的末尾,添加成功返回true,
  2. removeAll(Collection c): 表示将集合c中在集合a(谁调用removeAll,a就代表谁)中重复元素进行移除(取a和c;两个集合的交集,在a中进行删除)
  3. containsAll(Collection c) : 判断c集合是否完全包含在调用方法的集合中,完全包含返回true,不完全包含返回false
  4. retainAll(Collection c): 获取两个集合的交集,将交集赋值给方法调用的集合,如果方法调用的集合内容改变,返回true,没改变,返回false
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
import java.util.ArrayList;
import java.util.Collection;

public class CollectionDemo2 {

public static void main(String[] args) {
Collection c = new ArrayList();
c.add("a");
c.add("b");
c.add("c");
c.add("d");

Collection c1 = new ArrayList();
c1.add("a");
c1.add("b");
c1.add("c");
c1.add("d");

// 1. addAll(Collection c) : 表示将一个集合c添加到另外一个集合(调用addAll方法的集合)的末尾
/* boolean b = c.addAll(c1);//[]
System.out.println(b);// true
System.out.println(c);// [a, b, c, d, a, b, c, d]
System.out.println(c1);// [a, b, c, d]
*/
// 2. removeAll(): 删除带有交集的数据部分,将删除后的集合赋值给了方法调用的集合
/*boolean b1 = c.removeAll(c1);
System.out.println(b1);// true
System.out.println(c);// []
System.out.println(c1);// [a, b, c, d]
*/
// 3. containsAll():判断c集合是否完全包含在调用方法的集合中
/*boolean b2 = c.containsAll(c1);
System.out.println(b2);// true
System.out.println(c);// [a, b, c, d]
System.out.println(c1);// [a, b, c, d]
*/
// 4. retainAll() : 将两个集合中的交集,保留下来,将保留的结果赋值给了方法调用的集合
// 结果是boolean类型: 当调用方法的集合内容没有改变,返回false,当调用方法集合内容改变了,返回true
boolean b3 = c.retainAll(c1);
System.out.println(b3);// false
System.out.println(c);// [a, b, c, d]
System.out.println(c1);// [a, b, c, d]
}
}

注意事项:

1
2
3
4
5
6
7
8
9
10
import java.util.ArrayList;
// import java.util.Collection;

public class Collection {// 自己定义类的时候,不能与JDK中已经存在类名字相同,影响使用
public static void main(String[] args) {
// Collection 使用的不是JDK中的java.util.Collection,是自己定义的这个类Collection
// Collection c = new ArrayList();// JDK的接口
}

}

Collection 集合的遍历方式

将集合中的元素,一个一个获取到
toArray() : 返回一个包含此集合中所有元素的数组。返回值Object[]

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
package com.zgjy.demo1;

import java.util.ArrayList;
import java.util.Collection;

import com.zgjy.demo.Student;

public class CollectionDemo3 {

public static void main(String[] args) {
Collection c = new ArrayList();
c.add("a");
c.add("b");
c.add("c");
c.add("d");

// toArray() : 返回一个Object[],再进行数组的遍历,相当于将集合中的所有元素获取到
// obj中的每一个元素的类型都是Object ; 就好比String[] s = {"a"};中,s中的元素都是String类型
// Object是所有类的直接或者间接父类
Object[] obj = c.toArray();

if(obj != null && obj.length !=0 ) {

for(int i = 0 ; i < obj.length ; i++) {
// 多态的向下转型 ,子类String指向父类Object的引用
String s = (String)obj[i];
System.out.println(s);
}
}


// String = 52 错误
// String[] s = {"a",52};
// Object = String
// Object = new Student("zha",20)
Object[] obj11 = {"a" , new Student("zha",20)};
}

}

迭代器

迭代器: 迭代,就指,将一个容器中数据一个一个的拿出来(可以理解成循环,遍历)
iterator()方法 : 获取集合的迭代器对象,返回值类型Iterator,
Iterator是一个接口,来自java.util.Iterator
Iterator it = new Iterator (); // 接口,不能实例化对象的,这个表达式不会出现
Iterator it = new 实现类();// 正确方式

Iterator 接口中的方法
hasNext() : 判断集合中是否有下一个元素,如果有,返回值为true,如果没有,返回值就是false
使用场景: 一般用于判断集合中是否有下一个元素,以此来确定是否要接着进行集合的遍历
next() : 获取集合中的元素,返回值类型可以理解成Object

说明:
迭代器在使用中比较推荐: 为什么? 原因是,

  1. 迭代器可以进行所有容器的遍历(数组和集合)
  2. 迭代器遍历的时候,不看索引,只看容器中有没有下一个元素
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
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class CollectionDemo4 {

public static void main(String[] args) {
Collection c = new ArrayList();
c.add("a");
c.add("b");
c.add("c");
c.add("d");

// 迭代器的方式进行集合的遍历
// 1. 通过iterator() 方法: 获取到当前集合的一个迭代器对象,iterator()方法来自于Collection接口

// 说明:Collection下的所有实现类,都重写了iterator()这个方法,List Set
/* Iterator it = c.iterator();
// 2. 通过Iterator中方法,hasNext() : 判断集合中是否有下一个元素,有元素,返回true
boolean b = it.hasNext();
System.out.println(b);

if(b) {// 有元素
// 3. 通过Iterator中方法,next() : 获取集合中的指定元素
String s = (String)it.next();
System.out.println(s);

}*/

// 抽成一个循环的方式,将集合中的数据一个一个获取到
// 迭代器在使用中比较推荐: 为什么? 原因是,
// 1. 迭代器可以进行所有容器的遍历(数组和集合)
// 2. 迭代器遍历的时候,不看索引,只看容器中有没有下一个元素
Iterator it1 = c.iterator();
// 循环,不确定c集合中的元素个数,于是使用while循环
while(it1.hasNext()) {// 判断集合中有元素吗? 有元素,循环中获取元素
System.out.println(it1.next());
}
}
}

迭代器原理和注意事项

注意事项:

  1. hasNext() : 方法只做集合中是否有下一个元素的判断
  2. next() : 将指针向下移动一位, 获取到指针对应的元素
    集合中没有元素,还通过next()获取,报出 : java.util.NoSuchElementException

迭代器并发修改异常

异常发生的原因: 在使用迭代器进行容器的遍历时,同时还对这个容器中的内容进行了修改(增加,删除),这个时候,报出异常: java.util.ConcurrentModificationException 并发修改异常

上述的情况如何解决:

  1. listIterator(): 方法来自于ArrayList 类中,将集合的迭代器获取到,返回值类型ListIterator,来自java.util.ListIterator

  2. ListIterator用于允许程序员沿任一方向遍历列表的列表的迭代器,在迭代期间修改列表,并获取列表中迭代器的当前位置(解决了Iterator迭代时,不能修改集合的问题)。

  3. ListIterator : 是一个接口,不能实例化对象,依靠实现类,listIterator() 方法的返回值实际上是ListIterator 接口的一个实现类对象

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
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.ListIterator;

// 要求: 如果在集合中匹配到了"b",就在集合中添加一个数据"hello"
public class CollectionDemo5 {

public static void main(String[] args) {
Collection c = new ArrayList();
c.add("a");
c.add("b");
c.add("c");
c.add("d");
getListIterator(c);
}

// 解决Iterator的并发修改异常
public static void getListIterator(Collection c) {
ArrayList list = new ArrayList();
list.add("a");
list.add("b");
list.add("c");
list.add("d");

// 本身就能实现迭代器的功能,在此基础上,支持集合中元素的修改,而不会发生异常
ListIterator li = list.listIterator();

while(li.hasNext()) {
String s = (String)li.next();
if("b".equals(s)) {
li.add("hello");
}
}
System.out.println(list);
}

// 该方法会抛出并发修改异常
public static void getIterator(Collection c) {

Iterator it = c.iterator();
while(it.hasNext()) {
// java.util.ConcurrentModificationException,并发修改异常
Object obj = it.next();
String s = (String)obj;
// 在进行字符串的内容比较时,使用equals 方法,但是,注意,尽量将常量写在前面,为了避免发生空指针异常
if("b".equals(s)) {
c.add("hello");
}
}
System.out.println(c);
}
}

List接口

List : 来自于 java.util.List 接口,不能实例化对象,依靠实现类,ArrayList

List中的特有方法:

  1. add(int index, E element) : index表示索引, element元素,将element添加到集合的指定索引位置上
  2. remove(int index) : index表示索引,将集合中指定索引位置的元素删除掉,返回之类类型E,理解为Object
  3. get(int index): 获取指定索引上的元素,常用,原因,可以做集合的遍历,List集合有索引
  4. set(int index,Object elements): 将指定索引上的元素替换成elements
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

import java.util.ArrayList;
import java.util.List;

public class ListDemo {

public static void main(String[] args) {
List list = new ArrayList();
// 1. 将element添加到集合的指定索引位置上
list.add(0, "a");
list.add(0, "b");
list.add(0, "c");

list.add(2, "hello");
// IndexOutOfBoundsException : 6越界,因为集合的长度一共才为4,索引0-3
//list.add(6, "324");错误代码

// add(int index,E elements): index的范围,从0-size()
// 第21行大妈,相当于在list集合的末尾进行元素添加
list.add(4,"345");
System.out.println(list);// [c, b, hello, a, 345]

// 2. remove(int index):将集合中指定索引位置的元素删除掉,返回之类类型E,理解为Object,
String s = (String)list.remove(3);
System.out.println(s);// a
System.out.println(list);// [c, b, hello, 345]

remove();

// 3. get(int index): 获取指定索引上的元素,常用,原因,可以做集合的遍历,List集合有索引

String s1 = (String)list.get(1);
System.out.println(s1);// b

// 4. set(int index,Object elements): 将指定索引上的元素替换成elements
list.set(2, "QQ");
System.out.println(list);//[c, b, QQ, 345]

}

public static void remove() {
List list = new ArrayList();// 多态remove调用子类重写
list.add(1);
list.add(2);
list.add(3);
list.add(4);

// list 优先调用 删除指定索引的方法
list.remove(2);// 删除的是2元素还是2索引上的元素
System.out.println(list);//[1, 2, 4]

}

}

ArrayList,LinkedList,Vector

区别

  1. 底层的数据结构不同
    ArrayList : 底层使用数组实现的
    LinkedList: 底层是链表结构(节点结构)
    Vector : 底层数组结构,目前已经被ArrayList取代了

  2. 使用性能不同
    ArrayList : 查询快,增删慢
    LinkedList : 查询慢,增删快
    Vector: 都慢,线程安全

  3. 数据结构图解

ArrayList的存储方式

LinkedList:

LinkedList特殊方法

  1. addFirst(E e): 向集合的开头添加一个元素
  2. addLast(E e) : 向结合的末尾添加一个元素
  3. getFirst() : 获取集合中的第一个元素
  4. getlast() : 获取集合中的最后一个元素
  5. removeFirst() : 删除集合中的第一个元素
  6. removeLast() : 删除集合中的最后一个元素
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

import java.util.LinkedList;

public class LinkedDemo {

public static void main(String[] args) {
LinkedList ll = new LinkedList();
ll.add("a");
ll.add("b");

// LinkedList中的头尾操作方法
// 1. addFirst(E e) : 向集合的开头添加一个元素

ll.addFirst("c");// [c, a, b]
ll.addFirst("e");//[e, c, a, b]
System.out.println(ll);

// 2. addlast(E e): 向结合的末尾添加一个元素
ll.addLast("h");
System.out.println(ll);// [e, c, a, b, h]

// 3. getFirst() : 获取集合中的第一个元素
String s = (String)ll.getFirst();
System.out.println(s);// e

// 4. getLast() :获取集合中的最后一个元素
String s1 = (String)ll.getLast();
System.out.println(s1);// h

// 5. removeFirst() : 删除集合中的第一个元素
Object obj = ll.removeFirst();
System.out.println(obj);// e
System.out.println(ll);// [c, a, b, h]

// 6. removeLast():删除集合中的最后一个元素
Object obj1 = ll.removeLast();
System.out.println(obj1);// h
System.out.println(ll);//[c, a, b]

}
}

Vector

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

/*Vector从JDK1.0版本就已经存在了,Collection 1.2版本
addElement(E obj): 向Vector中添加元素
elements(): 返回一个枚举类型的实现类对象,Enumeration<E>
Vector如何进行元素的遍历
Enumeration接口中,方法
1)hasMoreElements(): Vector中还有下一个元素吗,判断,返回值类型boolean
2)nextElement(): 获取到指定的元素,返回值Object
*/

import java.util.Enumeration;
import java.util.Vector;

public class VectorDemo {

public static void main(String[] args) {
Vector v = new Vector();
v.addElement("a");
v.addElement("b");

Enumeration en = v.elements();

while(en.hasMoreElements()) {

System.out.println(en.nextElement());
}
}
}

泛型

  1. 泛型: 广泛的类型,JDK中很多的方法,返回值类型Object,通常需要转型
    如果有一个类,里面有方法,这个方法定义时,返回时,针对于需要的类型并不确定,就可以在类上或者方法上使用泛型

    举例: Collection, E就表示是泛型,可以理解成Object类型,原因是Collection作为集合,是一个容器,容器中就可以存储多种数据类型,不限定数据类型,接口上面加了一个泛型,证明各种对象类型,都可以存储

  2. 泛型的定义方式: <泛型类型>

  3. 泛型好处:

    1. 提高代码的安全性,能将运行环节的问题,提前暴露,编译环节
    2. 不需要强制类型转换
    3. 日后使用集合的时候,加上泛型

没有添加泛型的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

import java.util.ArrayList;
import java.util.Iterator;

public class FanXingDemo1 {

public static void main(String[] args) {
ArrayList al = new ArrayList();
al.add("a");
al.add("b");
al.add("c");
al.add(new Person("qq",20));

Iterator it = al.iterator();
while(it.hasNext()) {
Object obj = it.next();
String s = (String)obj;
System.out.println(s);
}
}

}

加了泛型的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

import java.util.ArrayList;
import java.util.Iterator;

public class FanXingDemo1 {

public static void main(String[] args) {
ArrayList<String> al = new ArrayList<String>();
// ArrayList中的泛型是什么类型,add方法中的参数就是什么类型
al.add("a");
al.add("b");
al.add("c");
// 1. 提高代码的安全性,能将运行环节的问题,提前暴露,编译环节
//al.add(new Person("qq",20));
Iterator<String> it = al.iterator();
while(it.hasNext()) {
// 2.不需要强制类型转换
String s = it.next();
System.out.println(s);
}
}
}

泛型定义的注意事项

  1. 泛型添加时,前后的类型需要保持一致
    举例: ArrayList al = new ArrayList();

  2. 在JDK1.7版本之后,定义集合的时候,后面的泛型可以省略,根据前面定义的泛型为准
    // 前面有泛型,后面泛型可以省略
    ArrayList arrayList = new ArrayList<>();

  3. 泛型定义的书写规范: <泛型>
    ArrayList
    泛型可以使用英文字母进行表示: E ,T ,K,V, Q,W, 一般来说,泛型没有指定的必须规则,但是通常是一个大写字母表示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    import java.util.ArrayList;

    public class FanXingDemo2 {

    public static void main(String[] args) {
    // int[] arr = new String[3]; 错误的
    // ArrayList<String> arrayList = new ArrayList<Integer>(); 错误代码,前后泛型保持一致

    // ArrayList<Object> arrayList = new ArrayList<Integer>(); 错误代码,前后泛型保持一致

    // 前面有泛型,后面泛型可以省略
    ArrayList<String> arrayList = new ArrayList<>();
    }
    }

带有泛型的类

说明:

  1. 类在定义的时候,进行泛型的书写规范
  2. 类上声明的泛型,可以当做一个普通成员变量,在类中使用
  3. 在进行类的实例化对象时,可以给泛型进行类型的确定
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

import java.util.ArrayList;

// 1. 定义一个带有泛型的类,泛型尽量满足书写规范
public class FanXingClass<E> {

// 定义了一成成员,成员类型是ArrayList类型,ArrayList类中的泛型,与FanXingClass泛型保持一致
private ArrayList<E> arr = new ArrayList();

// 2. 类上声明的泛型,可以在类中当成一个成员变量使用
public void addFunction(E e) {// 功能: 是给集合中添加一个元素
arr.add(e);
System.out.println(arr);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

public class Test {

public static void main(String[] args) {
// 使用带有泛型的类, FanXingClass<E>
// 3. 在进行类的实例化对象时,可以给泛型进行类型的确定
// 类比方法定义思考泛型的定义和泛型的使用 :
FanXingClass<String> fxc = new FanXingClass<>();
fxc.addFunction("abc");

fun(78);// 方法的调用,需要传递一个实际的参数 ,例如 78
}
// 定义一个方法,需要一个int类型的参数
public static void fun(int i) {

}
}

带有泛型的方法

格式有点复杂 我放在代码块里了

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
1. 普通方法: 
1) 普通方法,可以直接使用类上声明的泛型
public void addFunction(E e) {// 功能: 是给集合中添加一个元素
arr.add(e);
System.out.println(arr);
}
2) 普通方法,可以自己定义泛型
定义方式:
修饰符<泛型类型> 返回值类型 方法名(参数列表){

}
说明: 方法自己定义的泛型,可以在方法的内部使用(相当于局部变量的概念)
// 2. 方法自己定义泛型
public<T> void addFunction2(T t) {

ArrayList<T> arr1 = new ArrayList();
arr1.add(t);
System.out.println(arr1);

}
2. 静态方法
静态都只属于类, 于是静态方法,只能定义自己的泛型

问题: 静态方法,不能使用类上面的泛型,因为类上面的泛型在实例化类对象的时候,给定的值,静态优先于对象存在的, 在类比中,类上面的泛型,理解成了一个普通成员变量,成员变量跟对象走

// 3. 静态方法只能定义自己的泛型
public static<K> void addFunction3(K k) {
ArrayList<K> arr1 = new ArrayList();
arr1.add(k);
System.out.println(arr1);
}
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


import java.util.ArrayList;

// 定义一个带有泛型的类,类上定义的泛型,可以理解为一个成员变量在使用
public class FanXingClass<E> {

// private E e;

// 定义了一成成员,成员类型是ArrayList类型,ArrayList类中的泛型,与FanXingClass泛型保持一致
private ArrayList<E> arr = new ArrayList();

// 1. 类上声明的泛型,可以在类中使用,也可以在类中的方法中使用, addFunction带有泛型的方法
public void addFunction(E e) {// 功能: 是给集合中添加一个元素
arr.add(e);
System.out.println(arr);
}

// 2. 方法自己定义泛型,方法自己定义的泛型,可以在方法的内部使用(相当于成员变量的概念)
public<T> void addFunction2(T t) {

ArrayList<T> arr1 = new ArrayList();
arr1.add(t);
System.out.println(arr1);

}

// 3. 静态方法只能定义自己的泛型,不能使用类上的泛型E e
public static<K> void addFunction3(K k) {
ArrayList<K> arr1 = new ArrayList();
arr1.add(k);
System.out.println(arr1);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

public class Test {

public static void main(String[] args) {
// 1. 使用带有泛型的类, FanXingClass<E>
// 类比方法定义思考泛型的定义和泛型的使用 :
// 实例化对象,可以给泛型确定类型, E 就表示String类型
FanXingClass<String> fxc = new FanXingClass<>();

// 使用了类上的泛型方法测试
fxc.addFunction("abc");
// 方法自己定义了泛型的测试
fxc.addFunction2(100);// 100----- new Integer(100)
// 静态方法自己定义泛型测试
FanXingClass.addFunction3(new Person("QQ",20));

}
}
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

public class Person {

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 Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Person() {
super();
// TODO Auto-generated constructor stub
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}

带有泛型的接口

说明:
接口上带泛型,实现类的实现方式有两种

  1. 实现类上不带泛型,要求实现的接口上,必须要给定泛型,推荐使用
  2. 实现类上带泛型,不需要指定接口上的泛型类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

// 接口上带泛型
public interface FanXingInterface<E> {
public abstract void fun(E e);
}

// 1. 实现类上不带泛型,要求实现的接口上,必须要给定泛型,推荐使用
class InterfaceImpl implements FanXingInterface<String>{

@Override
public void fun(String e) {

}
}

// 2. 实现类上带泛型,不需要指定接口上的泛型类型
/*class InterfaceImpl<E> implements FanXingInterface<E>{

@Override
public void fun(E e) {

}
}*/

Set集合

概述:

Interface Set : Set接口,不能实例化对象,依靠实现类,HashSet
List集合和Set集合的特点:
List集合特点 :

  1. 有序(数据的存储和取出顺序一致)
  2. 有索引
  3. 可以存储重复元素

Set集合特点:

  1. 无序(数据的存储和取出不一定一致)
  2. 没有索引
  3. set不存储重复元素

方法

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

import java.util.HashSet;
import java.util.Set;

public class SetDemo1 {

public static void main(String[] args) {
// Set<String> set = new HashSet();
// 1. add(Object obj) : 向set集合中添加元素
Set<String> set = new HashSet();
set.add("a");
set.add("a");
set.add("ah");
set.add("bh");
set.add("b");
set.add("c");
set.add("c");

// 2. 存取数据顺序不一定一致 ,去重复
System.out.println(set);// [a, b, c, bh, ah]

// 3. size() : 返回set集合中的元素个数
System.out.println(set.size());
}
}

Set集合的遍历

  1. toArray() : 方法,先将set集合中的所有数据封装到有个object[]中,再进行object[]的遍历
  2. toArray(T[] t): 方法,定义一个数组,toArray中的参数,是一个数组,带有泛型的数组,参数中数组的泛型与要遍历的集合中的泛型保持一致;表示将数组封装到t所表示的数组中,因此t类型的数组在定义时,长度要大于等于集合的长度,以便盛装数据
  3. 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
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

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class SetDemo2 {

public static void main(String[] args) {
Set<String> set = new HashSet();
set.add("a");
set.add("b");
set.add("c");
set.add("d");

//forFunction1(set);
//forFunction2(set);
forFunction3(set);
}

// 1. Collection 中方法 toArray() : 将集合封装成一个Object[]
public static void forFunction1(Set<String> set) {
Object[] obj = set.toArray();

for(int i = 0 ; i < obj.length ; i++) {
Object o = obj[i];
// 向下转型
String s = (String)o;
System.out.println(s);

}

}

// 2. Collection 中方法 toArray(T[] a):
public static void forFunction2(Set<String> set) {

// toArray中的参数,是一个数组,带有泛型的数组,
// 参数中数组的泛型与要遍历的集合中的泛型保持一致
//表示将数组封装到t所表示的数组中,因此t类型的数组在定义时,长度要大于等于集合的长度,以便盛装数据

String[] ss = new String[set.size()];
set.toArray(ss);

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

// 3. 迭代器遍历
public static void forFunction3(Set<String> set) {
// Collection 中有一个抽象方法 iterator() : 返回值类型一个Iterator<E>接口,但是实际返回的是Iterator<E>接口的一个实现类
// Set作为Collection的子接口,实现类HashSet重写iterator()方法

Iterator<String> it= set.iterator();
// Iterator接口中有两个抽象方法: 1. hasNext() : 判断集合中是否有下一个元素(只做判断)
// 2.next() : 1) 让指针向下移动一位 2) 将指针对应的元素获取到(返回值类型是Object)

while(it.hasNext()) {
String s = it.next();
System.out.println(s);
}
}
}

增强for循环

增强for : forEach, 也是一种循环,通过获取集合或者数组中的元素,来进行循环的
增强for底层其实也是通过迭代器的原理来实现的,使用起来更加简单,增强for获取元素不通过索引,所以适用性也很强

语法格式:
for(元素数据类型 变量名 : 集合或者数组 ){

}

说明:

  1. : 是英文的冒号
  2. 集合或者数组 : 表示要进行遍历的容器
  3. 元素数据类型: 需要与容器中的数据类型保持一致
  4. 变量名: 表示每次循环获取到的元素名称,但是每次遍历的过程中,变量名所代表的元素内容不一样
  5. 为什么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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class SetDemo2 {

public static void main(String[] args) {
Set<String> set = new HashSet();
set.add("a");
set.add("b");
set.add("c");
set.add("d");

forEach(set);
listForFunction();
}

// 4. 增强for遍历,实际的循环中,使用非常多
// 增强for,底层迭代器原理,也会有并发修改异常

public static void forEach(Set<String> set) {
for(String s : set) {
System.out.println(s);
}
}

// 5. ArrayList 通过索引进行遍历, 此种for循环,不会发生并发修改异常
public static void listForFunction() {
ArrayList<Integer> list = new ArrayList<>();
list.add(100); // 100 -----new Integer(100); 自动装箱
list.add(10);
list.add(-78);
list.add(100);

for(int i = 0 ; i < list.size() ; i++) {// i代表集合list的索引

if(10 == list.get(i)) {
list.add(999);
}

/*int j = list.get(i);// 自动拆箱
System.out.println(j);*/
}
System.out.println(list);
}
}

Set集合保证数据的唯一性

JDK中的类,基本上都重写了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
25
26
27
28
29
30
31
32
33
34
35
36

import java.util.HashSet;
import java.util.Set;

public class SetOnlyDemo {

public static void main(String[] args) {
getInteger();
getString();
}

public static void getInteger() {
Set<Integer> set = new HashSet();
set.add(100);
set.add(100);
set.add(100);
set.add(100);
set.add(10);

System.out.println(set);


}

public static void getString() {

Set<String> set = new HashSet();
set.add("100");
set.add("100");
set.add("100");
set.add("a");
set.add("10");

System.out.println(set);
}
}

定义类型通过set集合保证元素唯一性

Set通过什么方式来保证元素唯一, HsahSet 底层哈希表的结构(数组+链表)
比较不同的元素是否是相等的

hashCode : 哈希值,Object中的方法hashCode(),功能: 不同的对象,返回不一样的十进制数
equals: 比较,Object中方法,功能: 比较两个对象的地址是否一致,重写了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
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

public class Person {

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 Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Person() {
super();
// TODO Auto-generated constructor stub
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

import java.util.HashSet;
import java.util.Set;

import com.zgjy.demo.Person;

public class SetPersonDemo {

public static void main(String[] args) {

// 要求: 要求相同的姓名和年龄,就当成两个一样的对象,在set中,应该去重复
// 怎么做到的: Person类中重写了 hashCode 和 equals 方法
// 疑问: Person 没有直接父类,Object就是我的父类,
// 想让自定义类型做到去重复,重写 hashCode 和 equals 方法就可以
Set<Person> set = new HashSet();

set.add(new Person("QQ",20));
set.add(new Person("QQ",20));
set.add(new Person("QQ",21));

System.out.println(set);

}
}

Map集合

Map : 英文地图,也是一个容器
Interface Map<K,V> : Map是一个接口,双列集合的根接口
Map<K,V> : 两个泛型,K,V,两列, key 键 value 值,Map通过一个数据(key)找到另外一个数据(value), key与value之间的关系,称为映射关系,1对1的关系,一个key对应一个value

Map集合中特点:
key是唯一的,value不是唯一的,key在map集合中,不重复,value是可以重复的
Map中的key值,保持元素不重复的原理,与HashSet一样的

HashMap 哈希表

常用方法

Map是一个接口,依靠实现类进行功能的实现,HashMap
Map<K,V> map = new HashMap();

  1. put(K key,V value) : 向map集合中添加键值对的映射关系
    说明: 1)如果key在map中不重复,直接添加键值对成功
    2) 添加的key在map集合中已经存在,后面的key对应的值替换掉前面key的值
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

import java.util.HashMap;
import java.util.Map;

public class MapDemo1 {
// 向map集合中添加键值对元素
public static void main(String[] args) {
// 实例化一个map对象,两个泛型, new 对象的同时,需要给出, K 和 V的泛型类型, 泛型可表示任意引用类型
// K---- Integer V-----String
Map<Integer, String> map = new HashMap();
// 1. put(k,v) :
// 1)如果key在map中不重复,直接添加键值对成功
// 2) 添加的key在map集合中已经存在,后面的key对应的值替换掉前面key的值
String value = map.put(1, "a");
System.out.println(value);// null
map.put(2, "b");
map.put(3, "c");

System.out.println(map);//{1=a, 2=b, 3=c}

// map中value可以重复
map.put(4, "c");
System.out.println(map);// {1=a, 2=b, 3=c, 4=c}

// map中key不重复
String value1 = map.put(4, "QQ");
System.out.println(value1);// c
System.out.println(map);//{1=a, 2=b, 3=c, 4=QQ}
}
}
  1. size() : 求map集合中元素个数是多少,返回值为int类型
  2. clear() : 清空map中的数据,数据没有了,map集合仍然存在,返回值为void
  3. get(Object key): 通过map中的key值,获取到对应的value值,返回值类型为value值类型
    1) key存在于map中,获取到对应的value值
    2) key不存在于map中,得到的结果就是null
  4. containsKey(Object key) : 判断map集合中是否包含给定的key值,包含返回true,不包含返回false,返回值boolean类型
  5. containsValue(Object value) : 判断map集合中是否包含给定的value值,包含返回true,不包含返回false,返回值boolean类型
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

import java.util.HashMap;
import java.util.Map;

public class MapDemo2 {

public static void main(String[] args) {
Map<Integer, String> map = new HashMap();
// 1. put(k,v) :
// 1)如果key在map中不重复,直接添加键值对成功
// 2) 添加的key在map集合中已经存在,后面的key对应的值替换掉前面key的值
map.put(1, "a");
map.put(2, "b");
map.put(3, "c");

System.out.println(map);// {1=a, 2=b, 3=c}

// 2. size() : 获取map集合中,键值对映射关系数据的个数
int count = map.size();
System.out.println(count);// 3

// 4. get(Object key): 通过map中的key值,获取到对应的value值,返回值类型为value的值类型
// 1) key存在于map中,获取到对应的value值
// 2) key不存在于map中,得到的结果就是null
String value = map.get(2);// 2这个键在map中存在
System.out.println("-----"+value);

String value1 = map.get(7);// 7这个键在map中不存在
System.out.println("+++++"+value1);// null

// 5. containsKey(Object key) : 判断map集合中是否包含给定的key值,包含返回true,不包含返回false,结果boolean

boolean b1 = map.containsKey(3);
System.out.println(b1);// true

boolean b2 = map.containsKey(7);
System.out.println(b2);//false

// 6. containsValue(Object value) : 判断map集合中是否包含给定的value值,包含返回true,不包含返回false,结果boolean

boolean b3 = map.containsValue("a");
System.out.println("****"+b3);// true

boolean b4 = map.containsValue("hello");
System.out.println("****"+b4);//false

// 3. clear() : 清空map中的数据,数据没有了,map集合仍然存在
map.clear();
System.out.println(map);// {}
}
}

Map集合的两种遍历方式 ⭐ ⭐

  1. keySet() : 方法,来自实现类HashMap,表示,将Map集合中所有的key的值获取到,将key的值封装到一个set集合中,返回值结果,Set<泛型>,返回的Set集合中的泛型类型与Map中的key值的类型保持一致

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

import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

public class MapFor1 {

public static void main(String[] args) {
HashMap<Integer,String> map = new HashMap();
map.put(1, "a");
map.put(2, "b");
map.put(3, "c");

// keySet() : 获取到map中的所有的key值到集合set中
Set<Integer> set= map.keySet();
// 通过key的值获取对应的value值,循环的获取
// 1. 增强for遍历, i值就表示每一个key值
for(int i : set) {
// 通过每一个key值获取对应的value值
String value = map.get(i);
System.out.println(i+"-----"+value);
}

// 2. 迭代器进行遍历
Iterator<Integer> it= set.iterator();
while(it.hasNext()) {
// 得到每一个key值
int key = it.next();
// 通过key值获取对应的value值
String value = map.get(key);
System.out.println(key+"++++"+ value);
}
}
}

entrySet(): 表示将Map集合中的键值对映射关系获取到,返回值类型
Set<Map.Entry<K,V>>

Map.Entry<K,V> : Entry是接口,定义在Map这个接口的内部,Entry表示入口,
Entry<K,V>,就叫做键值对的映射关系
获取到了Entry<K,V>映射关系后,通过Entry中的方法
getkey() : 获取到映射关系中key值
getValue() : 获取到映射关系中的value值

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

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class MapFor2 {

public static void main(String[] args) {
Map<Integer,String> map = new HashMap();
map.put(1, "a");
map.put(2, "b");
map.put(3, "c");

// 通过entrySet() : 方法进行Map集合的遍历
// 1. 获取到Map中的映射关系对象,有多个,于是存放在Set集合中,因为映射关系Entry<K,V>来自于Map接口内部,因此Map.Entry<Integer,String>
Set<Map.Entry<Integer,String>> set= map.entrySet();

// 增强for的方式进行遍历
// 获取到每一个映射关系对象
for(Map.Entry<Integer,String> entry : set) {
// 获取映射关系中的key值
int key = entry.getKey();
// 获取映射关系中的value值
String value = entry.getValue();
System.out.println(key + "****" + value);

}

// 迭代器的遍历方式
Iterator<Map.Entry<Integer,String>> it = set.iterator();
while(it.hasNext()) {
Map.Entry<Integer,String> entry = it.next();
System.out.println(entry.getKey()+ "#####" + entry.getValue());

}
}
}

Map集合中将自定义类型作为key值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Map接口,依靠实现类HashMap
HashMap : 哈希表,HashSet,底层结构就是哈希表结构(数组+链表),HashSet的底层代码是通过HashMap来实现的

哈希表结构去除重复元素的原理:
1. 通过hashCode() 和 equals() 两个方法,进行去重复
2. 先计算每一个对象的hashCode()的值(哈希值),值是一个int类型的数据,判断集合中是否已经有其他对象也是相同的哈希值
没有,那么就直接添加到集合中,
有 ,那么再判断两个对象的equals方法比较内容是否相等
不相等 : 可以添加集合成功
相等: set集合直接添加失败; hashMap中将前面的元素替换成后面的元素(键值对)

Map集合中,key值不重复,HashMap中,key值底层是与hashSet的验证方式一致

1. map中key是JDK已经定义好的类型,自动的进行去重复,JDK中的类型基本上都重写了hashCode和equals方法,例如: String ,Integer
2. map中的key值,是自定义的类型,Person,如何做到在Map集合中去重复,重写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
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

public class Person {
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 Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Person() {
super();
// TODO Auto-generated constructor stub
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

import java.util.HashMap;
import java.util.Map;

public class MapPersonKey {

public static void main(String[] args) {
// 要求: 相同的人的姓名和年龄,当做是一个人,如果是同一个人添加到map集合作为key,应该添加失败
// 分析: map中的key去重,依靠 hashCode和equals两个方法
// Person继承使用Object中的hashCode方法,父类的方法功能是: 每一个不同的对象,都返回一个不同的int类型数据
// 实际,hashCode 应该比较对象中的内容所对应的哈希值,于是需要子类Person重写hashCode 方法
// 总结: 如果自定义的类型想要去重复(set集合, map集合作为key),那么就需要重写hashCode和equals 两个方法

Map<Person,String> map = new HashMap();
map.put(new Person("QQ",20), "20");
map.put(new Person("QQ",20), "21");
map.put(new Person("QQ",20), "22");
map.put(new Person("QQ",20), "23");

System.out.println(map);
}
}

练习

键盘录入一个字符串,统计每个字符出现的次数
例如,录入aaaabbccddd!@#@#$@#$%cc66ff
打印出来:a有4个,b有2个,c有4个,d有3个,!有1个,@有3个,$有2个,%有1个,6有2个,f有2个

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


import java.util.HashMap;
import java.util.Map;

/*
* 键盘录入一个字符串,统计每个字符出现的次数
例如,录入aaaabbccddd!@#@#$@#$%cc66ff
打印出来:a有4个,b有2个,c有4个,d有3个,!有1个,@有3个,$有2个,有1个,6有2个,f有2个
*
*
* 分析: 1. 定义一个字符串: aaaabbccddd!@#@#$@#$%cc66ff
* 2. 需要将字符串中的每一个字符获取到, String中有一个方法 toCharArray() , 将String
* 类型字符换转换成一个字符数组 char[] ch
* 3. 循环ch,结果 a----4, map集合的存储方式类似, map(不重复的字符,计数)
*
*
* */
public class MapString {

public static void main(String[] args) {
// 1. {@=3, a=4, !=1, b=2, c=4, #=3, d=3, $=2, %=1, 6=2, f=2}
//String s = "aaaabbccddd!@#@#$@#$%cc66ff";
String s = "12345423TYuui";//{1=1, 2=2, 3=2, 4=2, T=1, 5=1, u=2, Y=1, i=1}
// 2.
char[] ch = s.toCharArray();
// 用于存储字符与对应数量的映射关系
Map<Character,Integer> map = new HashMap();
/* 3. 通过循环,将字符串中的每一个字符都获取到
* */

for(char c : ch) {
// key---字符 value ----- 字符出现的次数
// 1) 当字符第一次添加到map集合中的时候,计数应该从1开始
// 2)问题: 当后期相同的字符多次加入的时候,入额每次让计数器+1
if(map.containsKey(c)) {
// 验证一下,字符c是不是第一次添加到map集合中,如果返回值为true,证明不是第一次添加,计数器需要增加
map.put(c, map.get(c)+1);

}else {// 如果返回值为false,证明第一次添加,计数器从1开始
map.put(c, 1);// a---1
}

}

System.out.println(map);
}
}

HashMap的子类 LikeedHashMap

LinkedHashMap : 是HashMap的一个子类,功能与HashMap基本一致,特点就是,能够使得通过LinkedHashMap 存储的数据,取出的顺序保持一致

HashSet : 也有一个子类LinkedHashSet, 功能上与hashSet基本一致,能保证,元素的存和取的顺序一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.util.HashMap;
import java.util.LinkedHashMap;

public class LinkedHashMapDemo {

public static void main(String[] args) {
HashMap<Integer,String> hash = new HashMap();
hash.put(2, "aac");
hash.put(1, "aac");
hash.put(99, "a");
System.out.println(hash);

LinkedHashMap<Integer,String> linked = new LinkedHashMap();
linked.put(2, "aac");
linked.put(1, "aac");
linked.put(99, "a");
System.out.println(linked);

}
}

hashmap和hashtable的区别 ⭐ 面试

Hashtable 也是用于存储键值对的映射关系,使用的相对少了
区别:
版本区别:
Hashtable JDK1.0版本
HashMap JDK1.2版本

存储数据区别:
Hashtable 键值都不能存储null类型,报错
HashMap 键值可以存储null类型

线程安全的区别:
Hashtable 线程安全,运行慢
HashMap 线程不安全,运行快

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

import java.util.HashMap;
import java.util.Hashtable;
import java.util.Set;

public class HashtableDemo {

public static void main(String[] args) {
Hashtable<Integer,String> table = new Hashtable();
/*table.put(1, null);
table.put(null, "a");*/
/*
Set<Integer> set= table.keySet();

for(int key : set) {
String value = table.get(key);
System.out.println(key+value);
}*/

HashMap<Integer,String> hash = new HashMap();
hash.put(2, null);
hash.put(null, "aac");
hash.put(99, "a");

Set<Integer> set1= hash.keySet();

for(Integer key : set1) {
String value = hash.get(key);
System.out.println(key+value);
}
}
}

Collection 工具类

Arrays : 对于数组的工具类
Collections : 对于集合的工具类,List集合,里面的方法都是静态方法,可以类名.直接使用

  1. binarySearch(List list ,T key) : 表示将key值在集合中的索引位置返回.
    1) 集合中没有这个元素,返回值是一个负数
    2) 存在这个元素,返回元素在集合中的索引值
  2. max(Collection c): 将集合中的最大元素进行返回
  3. min(Collection c): 将集合中的最小元素进行返回
  4. shuffle(List list) :将集合中的内容进行混乱排序
  5. sort(List 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
37
38

import java.util.ArrayList;
import java.util.Collections;

public class CollectionsDemo {

public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
// 查找元素4在集合list中的索引位置
int index = Collections.binarySearch(list, 4);
System.out.println(index);// 3

// [1,2,3,4,5,9] 负索引位置返回 -6
int index1 = Collections.binarySearch(list, 9);
System.out.println(index1);// -6

// 求集合中的最大值
int max = Collections.max(list);
System.out.println(max);// 5

// 求集合中的最小值
int min = Collections.min(list);
System.out.println(min);// 1

// 集合混乱排序
Collections.shuffle(list);
System.out.println(list);

// 集合升序排序
Collections.sort(list);
System.out.println(list);
}
}

斗地主案例

按照斗地主的规则,完成洗牌发牌的动作。

具体规则:
1. 组装54张扑克牌

  1. 将54张牌顺序打乱
    1. 三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张留作底牌。
    2. 查看三人各自手中的牌(按照牌的大小排序)、底牌

手中扑克牌从大到小的摆放顺序:大王,小王,2,A,K,Q,J,10,9,8,7,6,5,4,3

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

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class DouDiZhu {

public static void main(String[] args) {
// 1. 组合牌,一共54张,大王,小王 2....3 四个花色 ,并设定牌的大小关系(每一张牌和一个数组对应,0就代表最大,依次类推53表示最小)
// map用于装牌与编号的对应关系
Map<Integer, String> map = new HashMap();
// 用于存放所有牌的编号,后面进行洗牌和发牌
ArrayList<Integer> list = new ArrayList();
// 定义13张牌
String[] pooker = {"2","A","K","Q","J","10","9","8","7","6","5","4","3"};
// 定义4个花色
String[] flower = {"♥","♠","♦","♣"};
// 将0和1留给大小王,因此牌号从2开始设置
int count = 2;
// 将牌号和花色进行嵌套循环,使每张牌都有4个花色
for(String p : pooker) {
for(String f : flower) {
// 将牌号和牌放到map集合中
map.put(count, f+p);
// 将牌号放置到list中,后面用于洗牌发牌
list.add(count);
// 牌的编号每次+1
count ++ ;
}
}

// 将大王和编号0放置到map中
map.put(0, "大王");
list.add(0);
// 将小王和编号0放置到map中
map.put(1, "小王");
list.add(1);

// 2. 洗牌,将list集合里面的排序打乱
Collections.shuffle(list);

// 3. 发牌
// 准备好4个玩家容器,准备装牌
ArrayList<Integer> play1 = new ArrayList();
ArrayList<Integer> play2 = new ArrayList();
ArrayList<Integer> play3 = new ArrayList();
ArrayList<Integer> bottom = new ArrayList();

// 先发底牌3张,剩下的牌每个玩家发一张牌,直到牌没有了,每个玩家手里应该有17张牌
for(int i = 0 ; i < list.size() ; i++) {

if( i < 3) {// 给底牌发牌
bottom.add(list.get(i));

}else if( i % 3 == 0) {// 三个人发牌,每人一张,3是一个轮回,通过i值与3取模来决定该给那个玩家发牌
play1.add(list.get(i));

}else if(i % 3 == 1) {
play2.add(list.get(i));
}else if( i % 3 == 2) {

play3.add(list.get(i));
}
}

// 4. 看牌
lookPooker(play1,map,"玩家1");
lookPooker(play2,map,"玩家2");
lookPooker(play3,map,"玩家3");
lookPooker(bottom,map,"底牌");
}

public static void lookPooker(ArrayList<Integer> play,Map<Integer, String> map,String playName) {
// 1. 牌进行排序,升序排列,就是牌从大到小的过程
Collections.sort(play);
System.out.print(playName+"的牌为: ");
for(int number : play) {
String pook = map.get(number);
System.out.print(pook + " ");
}
System.out.println();
}
}

晚安

 今天就到这里了,明天见,加油!

-------------本文结束感谢您的阅读-------------
0%