1 泛型机制
- 集合的右侧使用
<数据类型>
的方式来明确要求集合中可以存放的对象
- 泛型的本质就是参数化类型
- 实际参数可以传递各种各样广泛的数据类型, 因此得名为泛型.
<T> T[] toArray(T[] a);
package com.lagou.task15;
/**
* @author 西风月
* @date 2020/7/30
* @description 自定义泛型类Person
* 其中T相当于形式参数负责占位,具体数值由实参实现
*/
public class Person<T> {
private String name;
private int age;
private T gender;
public Person(String name, int age, T gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
public Person() {
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", gender=" + gender +
'}';
}
//自定义方法实现将参数指定数组中的所有元素打印出来
public static <T1> void printArray(T1[] arr) { //返回值前加 <T1>
for (T1 tt: arr) {
System.out.println("tt = " + tt);
}
}
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 /*static*/ T getGender() {
return gender;
}
public void setGender(T gender) {
this.gender = gender;
}
}
package com.lagou.task15;
/**
* @author 西风月
* @date 2020/7/30
* @description
*/
//public class SubPerson extends Person { //丢弃泛型 不保留泛型并且没有指定类型, 此时T默认类型 Object 擦除
//public class SubPerson extends Person<String> { //不保留泛型, 但是指定了泛型的类型,此时Person中的T被指定为String类型
//public class SubPerson<T> extends Person<T> { //保留父类的泛型, 可以在构造对象时指定T的类型
public class SubPerson<T, T1> extends Person<T> { //保留父类的泛型, 同时在子类增加新的泛型
}
package com.lagou.task15;
/**
* @author 西风月
* @date 2020/7/30
* @description
*/
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person("liubei", 30, "男");
System.out.println(p1);
Person<String> p2 = new Person<>();
p2.setGender("女");
System.out.println(p2);
Person<Boolean> p3 = new Person<>("libei", 30, true);
p3.setGender(true);
System.out.println(p3);
//call泛型方法
Integer[] arr = {11, 22, 23, 88, 99};
Person.printArray(arr);
}
}
? 可以代表任意类型,所以主要用于声明时的限制情况。
当考虑到要add时候,对于安全性的考虑,应该使用泛型的<? super XXX>模式,
当考虑到要对list进行get类型显示的时候,就要考虑用到<? extends XXX>,这样类型安全并且简洁.
extends 可用于的返回类型限定,不能用于参数类型限定。
super 可用于参数类型限定,不能用于返回类型限定。
package com.lagou.task15;
import java.util.LinkedList;
import java.util.List;
/**
* @author 西风月
* @date 2020/7/30
* @description
*/
public class GenericTest {
public static void main(String[] args) {
List<Animal> lt1 = new LinkedList<>();
List<Dog> lt2 = new LinkedList<>();
//试图梁lt2的数值赋值给lt1, 也就是发生List<Dog>类型向List<Animal>类型的转换
//lt1 = lt2;
// 使用通配符作为泛型类型的公共父类
List<?> lt3 = new LinkedList<>();
lt3 = lt1; //List<Animal> --> List<?>
lt3 = lt2; //List<Dog> --> List<?>
//向公共父类中增加元素与获取元素
//lt3.add(new Animal()); // Error:不能存放Animal类型的对象
//lt3.add(new Dog()); Error:不能存放Dog类型的对象,不支持元素的添加操作
Object o = lt3.get(0); //ok 支持元素的获取, 返回值全部当做Object类型处理
System.out.println("--------------------------------------");
//使用有限制的通配符进行使用
List<? extends Dog> lt4 = new LinkedList<>();
//lt4.add(new Animal());
//lt4.add(new Dog());
//lt4.add(new Object());
Animal animal = lt4.get(0);
System.out.println("--------------------------------------");
List<? super Animal> lt5 = new LinkedList<>();
lt5.add(new Animal());
lt5.add(new Dog());
//lt5.add(new Object()); //Error :超过了Animal的范围
Object object = lt5.get(0);
}
}
2. Set集合(熟悉)
1. 基本概念
- Set集合没有重复 且 没有先后顺序, 但是不表示无序
HashSet 哈希表进行数据管理
- 哈希表其实就是一维数组,数组元素时单向链表(链表数组)
哈希表
2. 常用的方法
3. 元素放入HashSet集合的原理
疑问:
HashSet类里增加元素时, 当两个元素调用equals方法相等时证明这两个元素相同
重写hashCode方法保证这两个元素得到的哈希码值相同, 由同一个哈希算法生成的索引位置相同
此时遍历该索引位置对应的单向链表, 若比较后发现均不相同, 则在链表尾部新增元素.
这里有个疑问, 既然hashCode都相等了, 为什么还要判断equals是否相同等, 是考虑到泛型吗?
两个对象相等,hashcode一定相等
两个对象不等,hashcode不一定不等
hashcode相等,两个对象不一定相等
hashcode不等,两个对象一定不等
4. TreeSet集合的概念
- 二叉树
- 有序的二叉树
- 红黑树也是有序的二叉树
- TreeSet集合的底层是采用红黑树进行数据管理, 当有新元素插入到TreeSet集合时,需要使用新元素与集合中的已有的元素比较来确定元素的合理位置
- 比较元素大小的规则有两种:
- (1) 自然排序规则
- 元素类型需事先 java.lang.Comparable接口
- (2) 比较器规则进行排序
- 构造TreeSet集合时传入java.util.Comparator接口 ```java package com.lagou.task15;
import java.util.Set; import java.util.TreeSet;
/**
- @author 西风月
- @date 2020/7/31
@description */ public class TreeSetTest { public static void main(String[] args) {
//1. 准备一个TreeSet集合并打印 Set<String> s1 = new TreeSet<>(); System.out.println("s1 = " + s1); //2. 向集合中添加String类型的对象并打印 System.out.println(s1.add("aa")); System.out.println("s1 = " + s1); System.out.println(s1.add("cc")); System.out.println("s1 = " + s1); System.out.println(s1.add("bb")); //由于TreeSet集合中的内部是采用红黑树实现的,因此元素有大小的次序, 默认从小到大打印 System.out.println("s1 = " + s1);
} }
- String类其实已经实现了Comparable<String>接口
![image.png](https://cdn.nlark.com/yuque/0/2020/png/1567843/1596210746508-303e3017-15fc-4675-b7c6-be1edd0b2e9b.png#align=left&display=inline&height=60&margin=%5Bobject%20Object%5D&name=image.png&originHeight=67&originWidth=586&size=6270&status=done&style=none&width=526)
```java
package com.lagou.task15;
/**
* @author 西风月
* @date 2020/8/1
* @description
*/
public class Student implements Comparable<Student> {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() {
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + 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;
}
@Override
public int compareTo(Student o) {
//return 0; //调用对象和参数对象相等, 调用对象就是新增加的对象
//return -1; //后来的都小于前面的,放在前面
//return 1; //后来的都大于前面的,放在后面
//return this.getName().compareTo(o.getName()); //比较姓名
//return this.getAge() - o.getAge();
/*int i = this.getName().compareTo(o.getName());
if(i == 0)
return this.getAge() - o.getAge();
return i;*/
int ia = this.getName().compareTo(o.getName());
return 0 == ia ? this.getAge() - o.getAge() : ia;
}
}
package com.lagou.task15;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
/**
* @author 西风月
* @date 2020/7/31
* @description
*/
public class TreeSetTest {
public static void main(String[] args) {
//1. 准备一个TreeSet集合并打印
Set<String> s1 = new TreeSet<>();
System.out.println("s1 = " + s1);
//2. 向集合中添加String类型的对象并打印
System.out.println(s1.add("aa"));
System.out.println("s1 = " + s1);
System.out.println(s1.add("cc"));
System.out.println("s1 = " + s1);
System.out.println(s1.add("bb"));
//由于TreeSet集合中的内部是采用红黑树实现的,因此元素有大小的次序, 默认从小到大打印
System.out.println("s1 = " + s1);
System.out.println("===============================================================");
//4. 准备一个比较器对象作为参数传递给构造方法
//匿名内部类 接口/父类类型 引用变量名 = new 类型() {方法的重写};
/*Comparator<Student> comparator = new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) { //o1表示新增加的对象, o2表示集合中已有的对象
return o1.getAge() - o2.getAge();
//return 0;
}
};*/
//从Java8开始支持Lambda表达式 (参数列表) -> {方法体}
Comparator<Student> comparator = (Student o1, Student o2) -> {return o1.getAge() - o2.getAge();};
//3. 准备一个TreeSet放入自定义类型的对象
//Set<Student> s2 = new TreeSet<>();
Set<Student> s2 = new TreeSet<>(comparator); //比较器优先于自然排序
System.out.println(s2.add(new Student("liubei", 40)));
System.out.println(s2.add(new Student("guanyu", 38)));
System.out.println(s2.add(new Student("zhangfei", 29)));
//java.lang.ClassCastException: class com.lagou.task15.Student cannot be cast to class java.lang.Comparable
System.out.println("s2 = " + s2);
}
}
3. Map集合(重点)
1. 基本概念
- HashSet的也是用HashMap实现的 ```java public HashSet() { map = new HashMap<>(); }
public boolean add(E e) { return map.put(e, PRESENT)==null; } private static final Object PRESENT = new Object();
<a name="xH47G"></a>
## 2. 常用的方法
![image.png](https://cdn.nlark.com/yuque/0/2020/png/1567843/1596269891420-68abc17d-4d7d-41e7-8d48-676897c70bbb.png#align=left&display=inline&height=494&margin=%5Bobject%20Object%5D&name=image.png&originHeight=554&originWidth=814&size=246108&status=done&style=none&width=726)<br />默认加载因子是 0.75 初始容量是16<br />默认加载因子与初始容量的乘积是12
- 介绍了HashMap初始化与add元素时扩容的原理:
```java
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted //加载因子
}
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
类似于Set的增加元素流程,若已存在相同K值得元素, 则更新V后返回OldV
package com.lagou.task15;
import java.util.HashMap;
import java.util.Map;
/**
* @author 西风月
* @date 2020/8/1
* @description
*/
public class MapCountTest {
public static void main(String[] args) {
String str = "123,456,789,123,456";
Map<String, Integer> m1 = new HashMap<>();
int i = 0;
for (String s: str.split(",")) {
m1.merge(s, 1, Integer::sum);
}
System.out.println(m1);
}
}
package com.lagou.task15;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* @author 西风月
* @date 2020/8/1
* @description
*/
public class MapTest {
public static void main(String[] args) {
//1. 准备一个Map集合并打印
Map<String, String> m1 = new HashMap<>();
System.out.println("m1 = " + m1);
System.out.println(m1.put("1", "One"));
System.out.println("m1 = " + m1);
System.out.println(m1.put("2", "Two"));
System.out.println("m1 = " + m1);
System.out.println(m1.put("3", "Three"));
System.out.println("m1 = " + m1);
System.out.println(m1.put("1", "Eleven")); //返回原来覆盖前的值
System.out.println("m1 = " + m1);
System.out.println("-----------------------------------");
System.out.println(m1.containsKey("11"));
System.out.println(m1.containsKey("1"));
System.out.println(m1.containsValue("One"));
System.out.println(m1.containsValue("Eleven"));
System.out.println(m1.get("5"));
System.out.println(m1.get("3"));
System.out.println("-----------------------------------");
System.out.println(m1.remove("1")); //Eleven
System.out.println("m1 = " + m1); //m1 = {2=Two, 3=Three}
System.out.println(m1.keySet()); //返回此方法中键值的Set视图 对Map集合的遍历
for (String ts: m1.keySet()) {
System.out.println(ts + "=" + m1.get(ts));
}
System.out.println("----------------------------------------");
// 获取Map集合中所有的Value并组成Collection视图
Collection<String> co = m1.values();
for(String ts : co) {
System.out.println("ts = " + ts);
}
System.out.println("----------------------------------------");
//获取Map集合中所有的键值对
Set<Map.Entry<String, String>> entries = m1.entrySet();
for( Map.Entry<String, String> me : entries) {
System.out.println(me);
}
}
}
4. Collections类
1 基本概念
2. 常用的方法有
package com.lagou.task15;
import java.io.Serializable;
import java.util.*;
/**
* @author 西风月
* @date 2020/8/1
* @description
*/
public class CollectionsTest {
public static void main(String[] args) {
List<Integer> lt1 = Arrays.asList(11, 22, 23, 64);
System.out.println(Collections.max(lt1));
System.out.println(Collections.min(lt1));
Collections.reverse(lt1);
System.out.println(lt1);
/*Collections.shuffle(lt1);
System.out.println(lt1);*/
Collections.sort(lt1);
System.out.println(lt1);
//List<Integer>lt2 = new ArrayList<>(20);
List<Integer>lt2 = Arrays.asList(new Integer[10]);
System.out.println(lt1.size());
System.out.println(lt2.size());
Collections.copy(lt2, lt1);
System.out.println(lt2);
}
}
总结
- 泛型机制(熟悉)
- 概念和本质、自定义泛型接口、类、方法、继承方面的体现、通配符
- Set集合(熟悉)
- 概念、主要实现类、常用的方法、TreeSet类两种方式的规则
- Map集合(重点)
- 概念、主要实现类、常用的方法 && 遍历方式
- Collections工具类
- 概念和常用的方法