1.Collections工具类
主要是为List集合服务,进行相关的List功能处理
注意: Collection与Collections要区分开
扩展了解复合泛型
<T extends Comparable<T>>: 所使用的T类型必须要实现Comparable
<T extends Comparable<? super T>>:所使用的T类型或T的父亲实现Comparable接口
//例如:此处List存Integer类型,定义复合类型说明Integer或父亲必须实现Comparable
class Student{
int age;
public Student(int age) {
this.age=age;
}
}
public class Test1 {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(3);
list.add(1);
list.add(2);
list.add(4);
Collections.reverse(list); //集合的反转
Collections.shuffle(list); //随机重置集合
System.out.println(list);
Collections.sort(list); //排序
System.out.println(list);
//------扩展思考:List存自定义对象,并进行排序------
List<Student> list2 = new ArrayList<Student>();
list2.add(new Student(30));
list2.add(new Student(25));
//Collections.sort(list2); //有约束,Student类必须实现Comparable接口才行
}
}
2.Set实现类HashSet
2.1. HashSet存储原理
HashSet实现类: 是Set接口的实现类
特点:无序,无下标,元素唯一
使用方式:
分析HashSet存储原理:通过hash算法进行存储
public class Test1 {
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
set.add(11);
set.add(33);
set.add(22);
set.add(33); //唯一的
System.out.println(set);
//因为无下标,所以不能使用基本for遍历
for(int i=0;i<set.size();i++) {}
for(Integer o:set) {
System.out.println(o);
}
}
}
HashSet存储原理:Hash算法
存储对象时,每个对象都能得到hash值,该值再hash表中,通过运算,得到下标位置,如果该位置值为空,则直接存储;否则,与该位置的链表元素一一比较,确定唯一性
//HashSet源码分析:
//1、调用add方法--内部通过HashMap的put方法实现
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)
//初始hash表是空,创建hash表空间,长度为16
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
//对象得到的hash值在hash表中可以算出下标位置,如果该位置为null,则直接存储;如果不为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))))
//需要比较hashCode与equals确定其唯一性
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;
}
2.2 hashSet验证原理
//验证存储原理:
//HashSet存储自定义对象student,所有对象如何确定唯一性
//例如: 学生对象zs ls zs
//目标: 2个 实际:3
//思考:假设两个属性的自定义对象存储;
===================存储单个属性的对象===================
class Student{
String name;
public Student(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student [name=" + name + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
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 (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
public class Test2 {
public static void main(String[] args) {
Set<Student> set = new HashSet<Student>();
set.add(new Student("zs"));
set.add(new Student("ls"));
set.add(new Student("zs"));
System.out.println(set);
//为什么下面的new String,它会根据属性的一致确定唯一性?
//根据存储原理分析结论得出:
//只要源码中的hashCode和equals一致,则确定唯一性,可以得出String类型肯定重写过
//如果存储自定义对象,要想根据属性确定唯一性,需要重写hashCode与equals
Set<String> set2 = new HashSet<String>();
set2.add(new String("zs"));
set2.add(new String("ls"));
set2.add(new String("zs"));
System.out.println(set2);
}
}
4. Set实现类TreeSet(重点)
4.1 TreeSet的使用特点
TreeSet:是Set接口的实现类
TreeSet存储特点:可排序,唯一,无下标
TreeSet的存储原理:通过二叉树进行存储
public class Test1 {
public static void main(String[] args) {
Set<Integer> set = new TreeSet<Integer>();
set.add(11);
set.add(33);
set.add(22);
set.add(11);
System.out.println(set);
//基本for: 不能
for(int i=0;i<set.size();i++) {}
//增强for
for(Integer o:set) {
System.out.println(o);
}
}
}
4.2存储原理分析
TreeSet存储原理:二叉树算法
存储第一个数,将该数作为根,再次存储,与根比较,如果没有,则直接存储;否则继续比较;如果比根大,查看根的右边是否有节点,如果没有则直接存储,否则继续比较,依次类推
do {
parent = t;
cmp = k.compareTo(t.key);
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;
实现排序的方式
1.自然排序法------存储对象的类实现Comparable接口
2.比较器法 ------带参构造方法传入比较器对象(Comparator)
验证原理-存储单个属性的对象
class Person implements Comparable<Person>{
String name;
public Person(String name) {
this.name=name;
}
@Override
public String toString() {
return "Person [name=" + name + "]";
}
@Override
public int compareTo(Person o) {
//按自己方式比较,认为姓名相同则确定唯一
//compareTo方法与二叉树原理匹配的>0 ==0 <0--->升序排列
//return this.name.compareTo(o.name);
return o.name.compareTo(this.name); //倒叙排列
//return 0; //如果返回0,只返回一个对象
}
}
public class Test1 {
public static void main(String[] args) {
Set<Person> set = new TreeSet<Person>();
set.add(new Person("zs")); //ClassCastException: Person cannot be cast to Comparable
set.add(new Person("ls"));
set.add(new Person("zs"));
set.add(new Person("ww"));
System.out.println(set);
}
}
验证原理-存储多个属性的对象
TreeSet存储对象为两个属性:
分析:需要判断两个属性都一致则认为是同一个对象
自然排序法---存储类型实现Comparable接口
class Student implements Comparable<Student>{
String name;
int age;
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) {
//排序规则:按年龄比较升序,如果年龄相同,则按姓名的降序
/*if(this.age==o.age) { //年龄相同
return o.name.compareTo(this.name); //姓名降序
}else {
return this.age-o.age; //年龄升序
}*/
//排序规则2:先按姓名的升序排,如果姓名相同,则按年龄的降序
if(this.name.equals(o.name)) {
return o.age-this.age; //年龄的降序
}else {
return this.name.compareTo(o.name);
}
}
}
public class Test2 {
public static void main(String[] args) {
Set<Student> set = new TreeSet<>();
set.add(new Student("zs",20));
set.add(new Student("ls",20));
set.add(new Student("zs",18));
set.add(new Student("ls",20));
System.out.println(set);
}
}
比较器法
案例:TreeSet存储两个属性的对象,使用比较器方式
class Teacher{
String name;
int age;
public Teacher(String name,int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Teacher [name=" + name + ", age=" + age + "]";
}
}
public class Test3 {
public static void main(String[] args) {
Set<Teacher> set = new TreeSet<Teacher>(new Comparator<Teacher>() {
@Override
public int compare(Teacher o1, Teacher o2) {
//规则: 先按姓名降序排列,如果姓名相同,在按年龄升序排列
if(o1.name.equals(o2.name)) {
return o1.age-o2.age;
}else {
return o2.name.compareTo(o1.name);
}
}
});
set.add(new Teacher("zs", 13));
set.add(new Teacher("ls", 13));
set.add(new Teacher("zs", 13));
set.add(new Teacher("ls", 15));
System.out.println(set);
}
}