1、Set
Set集合概述和特点
- 不包含重复元素的集合
没有带索引的方法,不能使用普通for循环遍历
public class SetDemo {
public static void main(String[] args) {
//创建对象
Set<String> set = new HashSet<>();
set.add("王小恒");
set.add("王中恒");
set.add("王大恒");
//遍历
for(String s : set) {
System.out.println(s);
}
Iterator<String> it = set.iterator();
while (it.hasNext()) {
String s = it.next();
System.out.println(s);
}
}
}
哈希值
- 哈希值:是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
- Object类中有一个方法可以获取对象的哈希值
- public int hashCode():返回对象的哈希码值
- 对象的哈希值的特点
- 同一个对象多次调用hashCode()方法返回的哈希值是相同的
- 默认情况下,不同对象的哈希值是不同的。重写hashCode()方法,可以实现让不同对象的哈希值相同
HashSet集合概述和特点
HashSet集合特点
- 底层数据结构是哈希表
- 对集合的迭代顺序不作任何保证,也就是说不保证存储和取出的元素顺序一致
- 没有带索引的方法,所以不能使用普通的for循环遍历
由于是Set集合,所以是不包含重复元素的集合
public class HashSetDemo {
public static void main(String[] args) {
//创建对象
HashSet<String> hashSet = new HashSet<>();
hashSet.add("王小恒");
hashSet.add("王中恒");
hashSet.add("王大恒");
//遍历
for(String s : hashSet) {
System.out.println(s);
}
System.out.println("----------");
Iterator<String> it = hashSet.iterator();
while(it.hasNext()) {
String s = it.next();
System.out.println(s);
}
}
}
```java public class HashSetDemo { public static void main(String[] args) { //创建对象 HashSet
hs = new HashSet<>(); Student s1 = new Student(“王小恒”,18); Student s2 = new Student(“王中恒”,19); Student s3 = new Student(“王大恒”,20);
Student s4 = new Student(“王小恒”,18);
hs.add(s1); hs.add(s2); hs.add(s3); hs.add(s4);
for(Student s : hs) {
System.out.println(s.getName() + "," + s.getAge());
} } }
public class Student { private String name; private int age;
public Student() {
}
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 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);
}
}
- 数据结构之哈希表
- JDK8之前,底层采用数组+链表实现,可以说是一个元素为链表的数组。
![image.png](https://cdn.nlark.com/yuque/0/2022/png/26723396/1652001401323-8b91d736-47d6-415e-b51c-4ccaf1865b52.png#clientId=u596a5bbd-c863-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=261&id=u90dd84b2&margin=%5Bobject%20Object%5D&name=image.png&originHeight=521&originWidth=1615&originalType=binary&ratio=1&rotation=0&showTitle=false&size=274620&status=done&style=none&taskId=uba25e145-2f69-4444-a498-08704fc0a98&title=&width=807.5)
2、LinkedHashSet集合概述和特点
- LinkedHashSet集合特点
- 哈希表和链表实现的Set接口,具有可预测的迭代次序
- 由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
- 由哈希表保证元素唯一,也就是说没有重复的元素
3、TreeSet
- TreeSet集合概述和特点
- 元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方式取决于构造方法
- TreeSet():根据其元素的自然排序进行排序
- TreeSet(Comparator comparator):根据指定的比较器进行排序
- 没有带索引的方法,所以不能使用普通的for循环遍历
- 由于是Set集合,所以不包含重复元素的集合
- 自然排序Comparable的使用
- 存储学生对象并遍历,创建TreeSet集合使用**无参构造方法**
- 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
- 结论:
- 用TreeSet集合存储自定义对象,无参构造方法使用的**自然排序**对元素进行排序的
- 自然排序,就是**让元素所属的类实现Comparable接口**,重写compareTo(T o)方法
- 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
```java
/*
存储学生对象并遍历,创建集合使用无参构造方法
要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
*/
public class TreeSetDemo {
public static void main(String[] args) {
//创建对象、
TreeSet<Student> ts = new TreeSet<>();
Student s1 = new Student("wangxiaoheng",18);
Student s2 = new Student("wangzhongheng",20);
Student s3 = new Student("wangdaheng",19);
Student s4 = new Student("aaa",19);
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
for(Student s : ts) {
System.out.println(s.getName() + "," + s.getAge());
}
}
}
public class Student implements Comparable<Student>{
private String name;
private int age;
public Student() {
}
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 int compareTo(Student s) {
// return 0; 代表两者相同
// return 1; 代表升序
// return -1; 代表降序
//s1不需要跟任何比
//s2要和s1比 this就代表s2 o就代表s1
//如果num>0 就说明s2比s1大,升序
int num = this.age - s.age;
int num2 = num==0? this.name.compareTo(s.name):num;
return num2;
}
}
比较器排序Comparator的使用
- 存储学生对象并遍历,创建TreeSet集合使用带参构造函数
- 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
结论:
- 用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的
- 比较器排序,就是让集合构造方法接受Comparator的实现类对象,重写compare(T 01, T 02)方法
重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写 ```java public class TreeSetDemo { public static void main(String[] args) { //创建对象 使用内部类实现接口Comparator的实现 TreeSet
ts = new TreeSet<>(new Comparator () { @Override
public int compare(Student s1, Student s2) {
//this.age - s.age
//s1 s2
int num = s1.getAge() - s2.getAge();
int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
return num2;
}
});
Student s1 = new Student(“wangxiaoheng”,18); Student s2 = new Student(“wangzhongheng”,20); Student s3 = new Student(“wangdaheng”,19);
Student s4 = new Student(“aaa”,19);
ts.add(s1); ts.add(s2); ts.add(s3); ts.add(s4); for(Student s : ts) {
System.out.println(s.getName() + "," + s.getAge());
} } }
```java
public class TreeSetDemo {
public static void main(String[] args) {
//创建对象
TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
int num = (s2.getMathSco() + s2.getLanSco()) - (s1.getMathSco() + s1.getLanSco());
int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
return num;
}
});
Student s1 = new Student("王小恒", 50, 60);
Student s2 = new Student("王中恒", 65, 65);
Student s3 = new Student("王大恒", 20, 35);
ts.add(s1);
ts.add(s2);
ts.add(s3);
for (Student s : ts) {
System.out.println(s.getName() + ", 总分为:" + (s.getMathSco() + s.getLanSco()));
}
}
}
public class TreeSetDemo2 {
public static void main(String[] args) {
//创建对象
// TreeSet<Integer> ts = new TreeSet<>(); 使用TreeSet会自动进行排序
//
// Random r = new Random();
//
// while (ts.size() <= 10) {
// ts.add(r.nextInt(20) + 1);
// }
//
// System.out.println(ts);
//使用HashSet不会自动排序
HashSet<Integer> hs = new HashSet<>();
Random r = new Random();
while (hs.size()<10) {
hs.add(r.nextInt(20) + 1);
}
System.out.println(hs);
}
}
2、泛型
泛型概述
- 泛型:是JDK5中引入的特性,提供了编译时类型安全检测机制,该机制允许在编译时检测到非法的类型
- 本质是参数化类型,所操作类型被指定为一个参数
- 参数,定义方法时有形参,然后调用方法时传递实参
- 参数化类型,就是将类型由原来的具体的类型参数化,然后在使用/调用时传入具体的类型
- 这种参数类型可以用在类、方法和接口中,分别被称为泛型类、泛型方法、泛型接口
泛型定义格式:
- <类型>:指定一种类型的格式。此类型可以看成形参
- <类型1,类型2>:指定多种类型的格式,多种类型之间用逗号隔开。此类型可以看成时形参
- 将来具体调用的时候给定的类型可以看成是实参,并且实参的类型只能是引用数据类型
泛型的好处:
- 把运行时期的问题提前到了编译期间
- 避免了强制类型转换
泛型类
- 泛型类的定义格式:
- 格式:修饰符 class 类名<类型> { }
- 范例:public class Generic
{ } - 此处T可以随便写任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
- 泛型类的定义格式:
泛型方法
- 泛型方法的定义格式:
- 格式:修饰符<类型>返回值类型 方法名(类型 变量名) { }
- 范例:public
void show(T t) { }
- 泛型方法的定义格式:
泛型接口
- 泛型接口的定义格式:
- 格式:修饰符 interface 接口名<类型>{ }
- 范例:public interface Generic
{ }
- 泛型接口的定义格式:
类型统配符
- 为了表示各种泛型List的父类,可以使用类型通配符
- 类型通配符:<?>
- List<?>:表示元素类型未知的List,它的元素可以匹配任何类型
- 这种带通配符的List仅表示它是各种泛型List的父类,并不能把元素添加到其中
- 如果说不希望List<?>是任何泛型List的父类,只希望它代表某一类泛型List的父类,可以使用类型通配符的上限
- 类型通配符上限:<?extends类型>
- List<?extends Number>:表示的类型是Number或者字类型
- 除了可以指定类型通配符的上限,也可以指定类型通配符的下限
- 类型通配符下限:<?super类型>
- List<?super Number>:它表示的类型是Number或者夫类型
- 为了表示各种泛型List的父类,可以使用类型通配符
可变参数
可变参数又称为参数个数可变,用作方法的形参出现,那么方法参数个数就是可变的
- 格式:修饰符 返回值类型 方法名(数据类型…变量名) { }
范例:public static int sum(int…a) { }
public class GenericDemo2 {
public static void main(String[] args) {
System.out.println(sum(1,2,3));
System.out.println(sum(2,3,4,5));
System.out.println(sum(3,4,5,6,7,8,9));
}
public static int sum(int... a) {
int sum = 0;
for(int i : a) {
sum += i;
}
return sum;
}
}
可变参数注意事项:
- 这里的变量 a 其实是一个数组
- 如果一个方法有多个参数,包含可变参数,可变参数要放在最后
- 可变参数的使用
- Arrays工具类中有一个静态方法:
- public static
List asList(T… a):返回由指定数组支持的固定大小的列表 - 返回的集合不能做增删操作,可以做修改操作
- public static
- List接口中有一个静态方法:
- public static
List of(E… elements):返回包含任意数量元素的不可变列表 - 返回的集合不能做增删改操作
- public static
- Set接口中有一个静态方法:
- public static
Set of(E… elements):返回一个包含任意数量元素的不可变集合 - 返回的集合不能做增删操作,没有修改的方法
- public static
- Arrays工具类中有一个静态方法: