一.泛型(JDK1.5以后出现的机制)
1.泛型由来
为什么会有泛型呢? 通过案例引入 早期的Object类型可以接收任意的对象类型,但是在实际的使用中,会有类型转换的问题。也就存在这隐患,所以Java提供了泛型来解决这个安全问题。
演示:
import java.util.ArrayList;
import java.util.Iterator;
/*
在此之前的集合,我们使用的时候,可以传入不同的数据类型的元素。
但是,实际开发中,一个集合只能够存储一种数据类型,为了就是将来获取元素处理的时候,处理方式能够统一
想一想之前也学习过一种容器,这种容器在定义的时候,就明确了元素的数据类型(数组)
现在集合想要模仿使用定义数组时的特点,可以明确一个集合元素的数据类型。
java为了实现这样的功能,提供了一个技术给我们使用:泛型
语句定义格式:<引用数据类型>
泛型的好处:
1、消除了程序中大部分的黄色警告
2、在获取元素的时候,不需要做向下转型
3、限制了集合存储的数据类型,将来处理的方式统一
*/
public class ArrayListDemo1 {
public static void main(String[] args) {
// //创建集合对象
// ArrayList list = new ArrayList();
//
// //创建元素对象并添加到集合中
// list.add("java");
// list.add("hadoop");
// list.add("hive");
// list.add("java");
// list.add("hbase");
list.add(12);
//
// //迭代器遍历
// Iterator iterator = list.iterator();
// while (iterator.hasNext()) {
// String s = (String) iterator.next();
// System.out.println(s + "--" + s.length());
// }
//加入了泛型创建集合对象
// ArrayList<E>
ArrayList<String> list2 = new ArrayList<>(); // 后面的尖括号泛型中的数据类型,根据前面定义时的泛型自动类型推断
// String[] arr;
//创建元素对象并添加到集合中
list2.add("java");
list2.add("hadoop");
list2.add("hive");
list2.add("java");
list2.add("hbase");
// list2.add(12);
//迭代器遍历
Iterator<String> iterator2 = list2.iterator();
while (iterator2.hasNext()) {
String s = iterator2.next();
System.out.println(s + "--" + s.length());
}
}
}
2.泛型应用
泛型类
把泛型定义在类上
格式: public class 类名<泛型类型1,…>
注意:泛型类型必须是引用类型
演示:
/*
开发代码的时候,写的泛型,将来使用的时候,应该传入一个引用数据类型给到开发时的泛型中接收
将类当作参数一样进行传递
<>中应该定义一个变量,用于接收将来使用时传入的引用数据类型,有点类似于形式参数
就要符合标识符的命名规则
如果泛型中只需要接收一个引用数据类型,定义时,只需要写一个大写的字母
*/
class Demo2<E> { // Demo2<Integer>
//E在一个类中是唯一的
public void fun(E a) { //Integer
System.out.println(a);
}
}
/*
泛型类:在开发代码的时候,将泛型写在类上
*/
public class FanXingDemo2 {
public static void main(String[] args) {
Demo2<Integer> demo2 = new Demo2<>();
demo2.fun(11);
}
}
泛型方法
把泛型定义在方法上
格式:public <泛型类型> 返回类型 方法名(泛型类型 ...)
演示:
class Demo3 {
//泛型方法,将来调用时可以传入任意的数据类型,与类上面的泛型无关
public <E> void fun3(E a) {
System.out.println(a);
}
}
/*
泛型方法:将泛型定义在方法上
*/
public class FanXingDemo3 {
public static void main(String[] args) {
Demo3 s3 = new Demo3();
s3.fun3(11);
s3.fun3('s');
}
}
泛型接口
把泛型定义在接口上
格式:public interface 接口名<泛型类型1…>
import java.util.ArrayList;
import java.util.List;
//如果一个类实现一个接口,这个接口有泛型的话,类定义时,也要有泛型,一般情况下与接口的泛型写法一致
interface Inter<E> {
void fun4(E a);
}
class InterImtl<E> implements Inter<E> {
@Override
public void fun4(E a) {
System.out.println(a);
}
}
public class FanXingDemo4 {
public static void main(String[] args) {
Inter<String> inter = new InterImtl<>();
inter.fun4("xuyou");
// inter.fun4(23);
List<String> list1 = new ArrayList<>();
list1.add("12");
System.out.println(list1);
// list1.add(12);
}
}
3.泛型通配符
第一种:
<?> 任意类型,如果没有明确,那么就是Object以及任意的Java类
第二种:
? extends E 向下限定,E及其子类
第三种:
? super E 向上限定,E及其父类
演示:
import java.util.ArrayList;
import java.util.Collection;
/*
开发程序时,泛型的高级用法:
泛型通配符<?>
任意类型,如果没有明确,那么就是Object以及任意的Java类了
? extends E
向下限定,E及其子类
? super E
向上限定,E及其父类
*/
class Animal {
public void fun() {
}
}
class Dog extends Animal {
}
class Cat extends Animal {
}
class Demo5<E> { E: Animal
public void fun(ArrayList<? super E> e) {
System.out.println(e);
}
}
public class FanXingDemo5 {
public static void main(String[] args) {
// 向下限定,E及其子类
ArrayList<? extends Animal> list1 = new ArrayList<Animal>();
ArrayList<? extends Animal> list2 = new ArrayList<Dog>();
ArrayList<? extends Animal> list3 = new ArrayList<Cat>();
// ArrayList<? extends Animal> list4 = new ArrayList<Object>();
//E: Animal
// ? super E 向上限定
Demo5<Animal> d = new Demo5<>();
//ddAll(Collection<? extends E> c)
// Collection<? extends E> 表示将来应该传入一个Collection集合对象,并且集合中的元素类型是E本身或者是E的子类类型
// E: Animal
ArrayList<Animal> list4 = new ArrayList<>();
ArrayList<Dog> list5 = new ArrayList<>();
ArrayList<Cat> list6 = new ArrayList<>();
ArrayList<Object> list7 = new ArrayList<>();
d.fun(list4);
d.fun(list7);
// d.fun(list5);
}
}
4.常用类型:
E:代表元素(Element)类型,通常在集合中使用,如 List<E>。
K:代表键(Key)的类型,通常在 Map 中使用,如 Map<K, V>。
V:代表值(Value)的类型,通常在 Map 中使用,如 Map<K, V>。
T:代表任意类型(Type),通常在方法中使用,如 public <T> T method(T obj)。
N:代表数字类型,如 Number 类型。
R:代表返回类型,如 public <T> R method(T obj)
二. 增强for循环
1.概述
(1)是用于遍历Collection集合和数组的
(2)增强for循环存在的目的,主要适用于替代Collection中的迭代器的。
2.格式
for(元素数据类型 变量 : 数组或者Collection集合) {
语句体 (使用变量即可,该变量就是元素)
}
3.注意事项:
增强for的目标要判断是否为null
演示:
import java.util.ArrayList;
import java.util.Objects;
/*
增强for循环:是用于遍历Collection集合和数组的
语句定义格式:
for(元素数据类型 变量名 : Collection集合/数组){
直接使用变量名;
}
增强for循环存在的目的,主要适用于替代Collection中的迭代器的。
*/
public class Demo1 {
public static void main(String[] args) {
//遍历数组
// int [] arr = null;
int [] arr ={11,22,33,44,55};
for (int i : arr) {
System.out.println(i);
}
System.out.println("+++++++++++++++++++++++++++++++++++++++++++");
//遍历集合
ArrayList<String> list1 = new ArrayList<>();
list1.add("nihao");
list1.add("xuyou");
list1.add("caozei");
list1.add("11");
for (String s : list1) {
System.out.println(s);
}
}
}
三.静态导入
1.概述:
(1)格式:import static 包名….类名.方法名;
(2)可以直接导入到方法的级别
2.注意事项
(1)方法必须是静态的
(2)如果有多个同名的静态方法,容易不知道使用谁?这个时候要使用,必须加前缀。
四.可变参数
1.概述:指的是方法将来调用时,可以传入若干个指定类型的参数
2.使用场景:定义方法的时候不知道该定义多少个参数时使用
3.注意:
(1)一个方法定义中只能有一个可变参数
(2)如果一个方法有多个参数和可变参数,那么可变参数要最后一个定义
演示:
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
/*
可变参数:指的是方法将来调用时,可以传入若干个指定类型的参数
注意:
1、一个方法定义中只能有一个可变参数
2、可变参数必须在参数列表中的最后一个定义
*/
public class KeBianDemo {
public static void main(String[] args) {
//需求1:定义一个方法,求四个int类型值的和
System.out.println(getSum(1, 2, 3, 4));
//需求2:定义一个方法,传入姓名以及各科分数,求总成绩
getSum("小明", 11, 22, 33, 44);
}
//将来传入若干个int类型的元素值,JVM将其封装到一个数组中,这个数组名叫做arr
public static int getSum(int... arr) {
int scoreSum = 0;
for (int i : arr) {
scoreSum += i;
}
return scoreSum;
}
public static void getSum(String name, int... arr) {
int scoreSum = 0;
for (int i : arr) {
scoreSum += i;
}
System.out.println(name + " " + scoreSum);
}
}
五. List集合练习
1.集合的嵌套遍历
import java.util.ArrayList;
/*
集合的嵌套遍历
学校(集合),目前有4个班级 1,2,3
每个班级也是一个集合 ArrayList<Student> class = new ArrayList<>();
*/
class Student {
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.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 String toString() {
return "name:" + name + ",age:" + age;
}
}
public class ListTest1 {
public static void main(String[] args) {
ArrayList<Student> class1 = new ArrayList<>();
class1.add(new Student("小1", 18));
class1.add(new Student("小2", 14));
ArrayList<Student> class2 = new ArrayList<>();
class1.add(new Student("小3", 16));
class1.add(new Student("小4", 18));
ArrayList<Student> class3 = new ArrayList<>();
class1.add(new Student("小5", 13));
class1.add(new Student("小6", 17));
class1.add(new Student("小7", 21));
ArrayList<ArrayList<Student>> school = new ArrayList<>();
school.add(class1);
school.add(class2);
school.add(class3);
for (ArrayList<Student> clazz : school) {
for (Student student : clazz) {
System.out.println(student);
}
}
}
}
2.获取10个1-20之间的随机数,要求不能重复
import java.util.ArrayList;
import java.util.Random;
//2.获取10个1-20之间的随机数,要求不能重复
public class ListDemo2 {
public static void main(String[] args) {
ArrayList<Integer> numberLIst = new ArrayList<>();
Random random = new Random();
while (numberLIst.size() < 10) {
int number = random.nextInt(20)+1;
if (!numberLIst.contains(number)) {
numberLIst.add(number);
}
}
System.out.println(numberLIst);
}
}
3.键盘录入多个数据,以0结束,要求在控制台输出这多个数据中的最大值
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
/*
键盘录入多个数据,以0结束,要求在控制台输出这多个数据中的最大值
*/
public class LIstTest3 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
ArrayList<Integer> list = new ArrayList<>();
int number = -1;
int i = 1;
while (number!=0) {
System.out.print("请输入第" + i + "个数据:");
i++;
int number1 = sc.nextInt();
if(number1==0){
break;
}
list.add(number1);
}
//方式一:
//将集合中的第一个元素默认为最大值
Integer maxNumber = list.get(0);
for (Integer num : list) {
if (num>maxNumber){
maxNumber=num;
}
}
System.out.println("最大值为:"+maxNumber);
//方式二:利用Collecyions工具类
// System.out.println("最大值为:"+Collections.max(list));
}
}
六.Set接口及其子类HashSet
1.概述
Set(接口) :元素无序且唯一 (无序:存储和取出的顺序不一致)
--HashSet : 底层数据结构是哈希表,线程不安全的,效率高
2.HashSet
(1)构造方法:
HashSet() 创建一个空的集合
(2)注意:
(1)HashMap实例具有默认初始容量(16)和负载因子(0.75)
(2)HashSet中要想保证元素唯一,就要保证元素类中要重写equals和hashCode方法
演示:
import java.util.HashSet;
import java.util.Objects;
/*
使用HashSet存储学生对象,当学生对象的姓名和年龄一样的时候,表示是同一个学生,应该要进行去重
结论:
HashSet中要想保证元素唯一,就要保证元素类中要重写equals和hashCode方法。
*/
class Student {
String name;
int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@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 && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
public class HashSetDemo {
public static void main(String[] args) {
//创建一个HashSet集合
HashSet<Student> studentSet= new HashSet<>();
//创建学生对象studentSet
Student student1 = new Student("小明", 12);
Student student2 = new Student("小花", 14);
Student student3 = new Student("小二", 18);
Student student4 = new Student("小李", 22);
Student student5 = new Student("小明", 12);
//将元素添加到HashSet集合中
studentSet.add(student1);
studentSet.add(student2);
studentSet.add(student3);
studentSet.add(student4);
studentSet.add(student5);
//遍历集合
for (Student student : studentSet) { //此时遍历后的结果并没有去重,原因是底层源码中没有重写equals和hashCode方法,比较的是地址值
System.out.println(student);
}
}
}
HashSet中的add()方法源码分析:
public class HashSet<E> implements Set<E>{
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
public HashSet() {
map = new HashMap<>();
}
//set1.add("pitaya");
public boolean add(E e) {
//HashSet中的add方法底层是调用了HashMap中的put方法
// e -- student1
// PRESENT -- new Object()
return map.put(e, PRESENT)==null;
}
}
public class HashMap<K,V> implements Map<K,V>{
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
public V put(K key, V value) {
// key -- "pitaya"
// value -- new Object()
// 与元素类型中的hashCode方法有关
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;
//第一次初始化一个hashTable出来
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;
//判断待插入的元素与已经在哈希表中的元素
//1、哈希值是否一样 元素类.hashCode()方法
//2、内容值是否一样 元素类.equals()方法
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;
}
}
3. LinkedHashSet类
1.概述:
(1)为HashSet的子类,底层数据结构是哈希表与双向链表
(2)元素有序唯一,由链表保证元素有序,由哈希表保证元素唯一
2.演示:
/*
Collection
- List
-- ArrayList
-- Vector
-- LinkedList
- Set(接口) 元素无序且唯一
-- HashSet 底层数据结构是哈希表,线程不安全的,效率高
--子类:LinkedHashSet 底层数据结构是哈希表与双向链表
*/
import java.util.LinkedHashSet;
public class LinkedHashSetDemo {
public static void main(String[] args) {
//创建LinkedHashSet集合
LinkedHashSet<Integer> set = new LinkedHashSet<>();
//添加元素
set.add(11);
set.add(2);
set.add(33);
set.add(11);
set.add(23);
//遍历
for (Integer i : set) {
System.out.println(i);
}
}
}
4.TreeSet类
1.概述:
是set接口实现的具体子类
2.排序方式:
(1)使用元素的自然顺序Comparable<T>对元素进行排序(自然排序)
(2)根据创建 set 时提供的Comparator进行排序(比较器排序)
如何使用这两种排序具体取决于使用的构造方法
3.TreeSet是如何保证元素的排序和唯一性的:
底层数据结构是红黑树(红黑树是一种自平衡的二叉树)
4.构造方法
5.自然排序:
(1)演示:
import java.util.TreeSet;
/*
Collection
- List
-- ArrayList
-- Vector
-- LinkedList
- Set(接口) 元素无序且唯一
-- HashSet 底层数据结构是哈希表,线程不安全的,效率高
--子类:LinkedHashSet 底层数据结构是哈希表与双向链表
-- TreeSet 底层数据结构是红黑树(可排序的),线程不安全,效率高
TreeSet中的排序方式有两种:
1、自然排序 Comparable
2、比较器排序 comparator
需求1:使用TreeSet存储字符串对象,并遍历
通过观察源码发现,因为我们创建TreeSet集合对象的时候,使用的是无参构造方法
所以底层创建TreeMap的时候也是无参构造方法,comparator是null,不走比较器排序
走的是自然排序
*/
public class TreeSetDemo1 {
public static void main(String[] args) {
//创建TreeSet集合
TreeSet<Integer> treeSet = new TreeSet<>();
//添加元素
treeSet.add(11);
treeSet.add(22);
treeSet.add(87);
treeSet.add(43);
treeSet.add(98);
//遍历
for (Integer i : treeSet) {
System.out.println(i); //元素从小到大排序
}
}
}
(2)实例:创建学生对象,加入到TreeSet集合中,按照年龄从小到大排序输出,要求使用自然排序
演示:
import java.util.TreeSet;
/*
使用TreeSet集合存储自定义对象Student2(String name,int age)
要求:最终要按照年龄从小到大排序,并且当学生的姓名和年龄一样的时候要进行去重
我们按照存储字符串一样的逻辑存储自定义对象,运行的时候报错了
ClassCastException:
com.shujia.day13.Student2 cannot be cast to java.lang.Comparable
原因是因为,我们现在使用的时候自然排序方式,需要元素的类实现Comparable接口,才可以进行转型
*/
class Student implements Comparable<Student> {
String name;
int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
//显式条件:按照年龄从大到小排序
//this -- 待插入的元素
//o -- 已经在树中的根
int cha = o.age - this.age;
return (cha == 0) ? o.name.compareTo(this.name):cha;
}
}
public class TreeSetDemo2 {
public static void main(String[] args) {
//创建集合
TreeSet<Student> treeSet = new TreeSet<>();
//新建学生对象
Student student1 = new Student("小一", 9);
Student student2 = new Student("小二", 34);
Student student3 = new Student("小三", 12);
Student student4 = new Student("小四", 6);
Student student5 = new Student("小五", 9);
//添加元素
treeSet.add(student1);
treeSet.add(student2);
treeSet.add(student3);
treeSet.add(student4);
treeSet.add(student5);
//遍历
for (Student student : treeSet) {
System.out.println(student);
}
}
}
上述代码Student类如果没有实现Comparable<>接口,就会报以下错误,原因是我们现在使用的时候自然排序方式,需要元素的类实现Comparable<>接口,才可以进行转型
注意: 重写Comparable<>接口中的compareTo()方法,可以让我们实现元素的排序问题
6.比较器排序:
在创建TreeSet对象的时候使用匿名内部类Comparator重写compareTo()方法
*
比较器排序:
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}
小总结:
1、如果要使用自然排序,在创建TreeSet对象的时候,使用无参构造方法,
但是要保证元素的类实现Comparable接口,重写compareTo方法。
2、如果要使用比较器排序,在创建TreeSet对象的时候,使用有参构造方法,
传入一个实现了Comparator接口的子类对象,重写compare方法
*/
import java.util.Comparator;
import java.util.TreeSet;
class Student1 {
String name;
int age;
public Student1() {
}
public Student1(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class TreeSetDemo3 {
public static void main(String[] args) {
//创建集合
TreeSet<Student> treeSet = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
//o1 -- 待插入的元素
//o2 -- 已经存在树中的根
int cha = o2.age - o1.age;
return (cha == 0) ? o2.name.compareTo(o1.name):cha;
}
});
//新建学生对象
Student student1 = new Student("小一", 9);
Student student2 = new Student("小二", 34);
Student student3 = new Student("小三", 12);
Student student4 = new Student("小四", 6);
Student student5 = new Student("小五", 9);
//添加元素
treeSet.add(student1);
treeSet.add(student2);
treeSet.add(student3);
treeSet.add(student4);
treeSet.add(student5);
//遍历
for (Student student : treeSet) {
System.out.println(student);
}
}
}
7.自然排序和比较器排序的底层源码:
public class TreeSet<E>{
private transient NavigableMap<E,Object> m;
private static final Object PRESENT = new Object();
public TreeSet() {
this(new TreeMap<E,Object>()); // this(..)
}
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
}
// treeSet.add(s2);
public boolean add(E e) {
//TreeSet中add方法底层调用的是TreeMap中的put方法
//e -- s2
//PRESENT -- new Object()
return m.put(e, PRESENT)==null;
}
}
public class TreeMap{
private final Comparator<? super K> comparator;
private transient Entry<K,V> root;
public TreeMap() {
comparator = null;
}
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
public V put(K key, V value) {
// key -- "hadoop"
// value -- new Object()
Entry<K,V> t = root; // null
//如果说是第一个元素插入进来,这个元素就作为红黑树的根保存
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null); // s1
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
Comparator<? super K> cpr = comparator; // null
//比较器排序
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
//自然排序
else {
if (key == null) // s2
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t; // "java"
cmp = k.compareTo(t.key); // 1
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
}
8.TreeSet中的排序总结:
1、如果要使用自然排序,在创建TreeSet对象的时候,使用无参构造方法, 但是要保证元素的类实现Comparable接口,重写compareTo方法。
2、如果要使用比较器排序,在创建TreeSet对象的时候,使用有参构造方法, 传入一个实现了Comparator接口的子类对象,重写compare方法。
七.Collection集合继承体系总结:
八.Map接口及其实现子类HasMap
1.概述:
(1)Map集合中的元素是一个键值对
(2)一个键对应一个值,键不允许重复,键是唯一的
(3)值可以发生重复
2.Map接口成员方法(一)
V remove(Object key) 根据键删除整个键值对,因为键是唯一的,返回键对应的值
V remove(Object key) 判断集合中是否包含键
boolean containsKey(Object key) 判断集合中是否包含键
boolean containsValue(Object value) 判断集合中是否包含值
boolean isEmpty() 判断集合是否为空
void clear() 清空集合
int size() 获取元素的个数,键值对的个数
演示:
import java.util.HashMap;
import java.util.Map;
/*
Map集合继承体系:
1、Map集合中的元素是一个键值对
2、一个键对应一个值,键不允许重复,键是唯一的
3、值可以发生重复
子类:HashMap<K,V>
成员方法:
V put(K key,V value)
V remove(Object key)
void clear()
boolean containsKey(Object key)
boolean containsValue(Object value)
boolean isEmpty()
int size()
*/
public class MapDemo1 {
public static void main(String[] args) {
//创建HashMap的对象
//HashMap()
//构造一个空的 HashMap ,默认初始容量(16)和默认负载系数(0.75)。
HashMap<String, Integer> map1 = new HashMap<>();
//V put(K key,V value) 向集合中添加一个元素键值对,如果键已经存在集合中,值会进行覆盖处理,返回被覆盖的值
System.out.println(map1.put("小王", 19)); //null
System.out.println("++++++++++++++++++++++++++++++++++++++++++");
System.out.println(map1.put("小王", 33)); //19
System.out.println(map1); //{小王=33}
map1.put("小李", 18);
map1.put("小帅", 13);
map1.put("小七", 12);
//V remove(Object key) 根据键删除整个键值对,因为键是唯一的,返回键对应的值
System.out.println(map1.remove("小七")); //12
System.out.println(map1); //{小李=18, 小王=33, 小帅=13}
//boolean containsKey(Object key) //判断集合中是否包含键
System.out.println(map1.containsKey("小")); //false
//boolean containsValue(Object value) //判断集合中是否包含值
map1.put("小九",12);
System.out.println(map1.containsValue(12)); //true
//void clear() //清空集合
// map1.clear();
// System.out.println(map1); //{}
//boolean isEmpty() //判断集合是否为空
System.out.println(map1.isEmpty());
//int size() 获取元素的个数,键值对的个数
System.out.println(map1.size());
}
}
3.与遍历有关的成员方法(二)
Set<K> keySet() 获取所有的键组成一个Set集合返回
Set<K> keySet() 获取所有的键组成一个Set集合返回
Collection<V> values() 获取所有的值组成一个Set集合返回
Set<Map.Entry<K,V>> entrySet() 获取所有的键值对组成一个Set集合返回
演示:
import java.util.Collection;
import java.util.HashMap;
import java.util.TreeSet;
/*
成员方法:
V get(Object key)
Set<K> keySet()
Collection<V> values()
Set<Map.Entry<K,V>> entrySet()
Map集合遍历的方式:
1、先获取所有的键,遍历键获取对应的值
2、直接获取所有的键值对,遍历每一个键值对,就能够得到每一个键和值
*/
public class MapDemo2 {
public static void main(String[] args) {
HashMap<Integer, String> map = new HashMap<>();
map.put(1001,"曹操");
map.put(1002,"曹丕");
map.put(1003,"曹纯");
map.put(1004,"曹叡");
map.put(1005,"曹冲");
//V get(Object key) 根据键获取值
System.out.println(map.get(1001)); //曹操
//Set<K> keySet() 获取所有的键组成一个Set集合返回
System.out.println(map.keySet()); //[1001, 1002, 1003, 1004, 1005]
//Collection<V> values() 获取所有的值组成一个Set集合返回
Collection<String> values = map.values();
System.out.println(values); //[曹操, 曹丕, 曹纯, 曹叡, 曹冲]
// Set<Map.Entry<K,V>> entrySet() 获取所有的键值对组成一个Set集合返回
System.out.println(map.entrySet()); //[1001=曹操, 1002=曹丕, 1003=曹纯, 1004=曹叡, 1005=曹冲]
}
}
4.Map集合遍历的方式:
(1)先获取所有的键,遍历键获取对应的值
(2)直接获取所有的键值对,遍历每一个键值对,再获得每一个键和值
演示:
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/*
Map集合遍历的方式:
1、先获取所有的键,遍历键获取对应的值
2、直接获取所有的键值对,遍历每一个键值对,就能够得到每一个键和值
*/
public class MapDemo3 {
public static void main(String[] args){
HashMap<Integer, String> map = new HashMap<>();
map.put(1001,"曹操");
map.put(1002,"曹丕");
map.put(1003,"曹纯");
map.put(1004,"曹叡");
map.put(1005,"曹冲");
//方式一:先获取所有的键,遍历键获取对应的值
Set<Integer> keys = map.keySet();
for (Integer key : keys) {
System.out.println(key+"=="+map.get(key));
}
System.out.println("---------------------------------------------");
//方式二:先获取所有的键值对,遍历键值对获取键和值
for (Map.Entry<Integer, String> keyValue : map.entrySet()) {
System.out.println(keyValue.getKey()+"=="+keyValue.getValue());
}
}
}
九.TreeMap类
1.概述:
(1)TreeMap: 底层数据结构是红黑树
(2)根据创建时调用的构造方法不同,map中的键排序的规则不同
(3)创建TreeMap是无参构造方法的话,将来map中的键是以自然排序
(4)创建TreeMap是有参构造方法,传入Comparator<>接口的实现类对象(匿名内部类的方式)
演示:
import java.util.Comparator;
import java.util.TreeMap;
/*
TreeMap: 底层数据结构是红黑树
根据创建时调用的构造方法不同,map中的键排序的规则不同
创建TreeMap是无参构造方法的话,将来map中的键是以自然排序
创建TreeMap是有参构造方法,传入Comparator接口的实现类对象(匿名内部类的方式)
*/
public class TreeMapDemo1 {
public static void main(String[] args) {
// TreeMap<Integer, String> treeMap = new TreeMap<>(); //自然排序
TreeMap<Integer, String> treeMap = new TreeMap<>(new Comparator<Integer>() { //比较器排序
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
treeMap.put(1004, "小名");
treeMap.put(1005, "小花");
treeMap.put(1003, "小王");
treeMap.put(1002, "小李");
treeMap.put(1001, "小帅");
System.out.println(treeMap); //{1001=小帅, 1002=小李, 1003=小王, 1004=小名, 1005=小花}
}
}
注意:所有的包装类都实现了Comparator<>接口,但是我们自己自定义的类必须要手动添加Comparator<>接口。
2.Map集合练习
(1)"aababcabcdabcde",获取字符串中每一个字母出现的次数要求结果:a(5)b(4)c(3)d(2)e(1)
import java.util.Set;
import java.util.TreeMap;
/*
"aababcabcdabcde",获取字符串中每一个字母出现的次数要求结果:a(5)b(4)c(3)d(2)e(1)
*/
public class TreeMapTest1 {
public static void main(String[] args) {
TreeMap<Character, Integer> treeMap = new TreeMap<>();
String s = "aababcabcdabcde";
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (!treeMap.containsKey(c)) {
treeMap.put(c, 1);
} else {
treeMap.put(c, treeMap.get(c) + 1);
}
}
//遍历
Set<Character> keys = treeMap.keySet();
for (Character key : keys) {
// System.out.print(key+"("+treeMap.get(key)+")"); //a(5)b(4)c(3)d(2)e(1)
}
}
}
(2)完成对HashMap嵌套ArrayList的遍历
import java.security.Key;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/*
HashMap嵌套ArrayList
每一个人都有一些爱好
HashMap<Person,ArrayList<String>>
*/
class Person {
String name;
int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String
toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class TreeMapTest2 {
public static void main(String[] args) {
//创建学生对象
Person person1 = new Person("小明", 23);
Person person2 = new Person("小李", 33);
Person person3 = new Person("小王", 13);
//创建ArrayList集合对象,并添加爱好
ArrayList<String> arrayList1 = new ArrayList<>();
arrayList1.add("打羽毛球");
arrayList1.add("打蓝球");
ArrayList<String> arrayList2 = new ArrayList<>();
arrayList2.add("养生");
arrayList2.add("旅游");
ArrayList<String> arrayList3 = new ArrayList<>();
arrayList3.add("吃好吃的");
arrayList3.add("和小伙伴玩");
HashMap<Person, ArrayList> hashMap = new HashMap<>();
hashMap.put(person1, arrayList1);
hashMap.put(person2, arrayList2);
hashMap.put(person3, arrayList3);
//遍历
Set<Map.Entry<Person, ArrayList>> keyValues = hashMap.entrySet();
for (Map.Entry<Person, ArrayList> keyValue : keyValues) {
for (Object habby : keyValue.getValue()) {
System.out.println("姓名和年龄:"+keyValue.getKey() + " "+"爱好:" + habby);
}
}
}
}
十.Collections类
1.概述:
针对集合操作的工具类
2.Collections成员方法
(1)public static <T> void sort(List<T> list) 对集合排序
(2)public static <T> int binarySearch(List<?> list,T key) 二分查找
(3)public static <T> T max(Collection<?> coll) 获取最大值
(4)public static void reverse(List<?> list) 使集合中元素逆序
(5)public static void shuffle(List<?> list) 将集合中的元素随机打乱
这里的shuffle和hadoop中的shuffle不是一个概念
注意:Collections工具类中的synchronizedXxx(xxx)方法可以将线程不安全的类变成线程安全的类,转换后使用方式不变
演示:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/*
Collections工具类可以将线程不安全的类变成线程安全的类,使用方式不变
synchronizedXxx(xxx)
*/
public class CollectionsDemo2 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
List<String> list1 = Collections.synchronizedList(list);
list1.add("java");
list1.add("java");
list1.add("java");
list1.add("java");
System.out.println(list1);
}
}