文章目录
- Java中的泛型机制
- 泛型类:
- 泛型接口:
- 泛型方法:
- 泛型的通配符以及向上限定、向下限定:
- 新式for循环
- asList方法
- set类集合
- 散列集(HashSet)
- LinkedHashSet类
- 树集(TreeSet)
Java中的泛型机制
- 泛型:是JDK1.5之后引入的一个概念,它的存在是指将类型明确工作推迟到创建对象或调用方法时才明确;
- 语法格式:<数据类型,数据类型,……>
- 泛型可以用在类、接口、方法上
- 泛型的好处:1、避免了向下转型;2、将问题提前到编译期
- 泛型的特点:泛型只在编译期有效,在运行期间就擦除了;
import java.util.ArrayList;
public class MyTest1
{
public static void main(String[] args)
{
ArrayList list = new ArrayList();
list.add(new Student("张三",18));
list.add(13);
Object o = list.get(0);
Object o1 = list.get(1);
//我们可以看到使用集合获取方法获取到的元素都是默认为Object类型,如果我们需要使用必须向下转型
}
}
- 泛型可以避免向下转型,同时可以提高代码的扩展性
泛型类:
//测试类:
public class MyTest2
{
public static void main(String[] args)
{
MyClass<String, Integer, Character> myClass1 = new MyClass("张三",19,'男');
Character r = myClass1.show();//调用方法时,编译器就会自动提示你这是一个Character类返回值
//MyClass{t=张三, u=19, r=男}
System.out.println(r);
}
}
//工具类
public class MyClass<T,U,R>
{
T t;
U u;
R r;
public MyClass(T t, U u, R r) {
this.t = t;
this.u = u;
this.r = r;
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
public U getU() {
return u;
}
public void setU(U u) {
this.u = u;
}
public R getR() {
return r;
}
public void setR(R r) {
this.r = r;
}
public MyClass() {
}
public R show(){
System.out.println("MyClass{" +
"t=" + t +
", u=" + u +
", r=" + r +
'}');
return r;
}
}
泛型接口:
//测试类:
public class MyTest2
{
public static void main(String[] args)
{
MyTest3 test = new MyTest3();
String show = test.show(115);
System.out.println(show);
}
}
//明确了泛型的类:
public class MyTest3 implements MyInterface<String,Integer>
{
@Override
public String show(Integer integer) {
System.out.println("这是实现了泛型接口的类");
return integer.toString();
}
}
//泛型接口:
public interface MyInterface<T,R>
{
public T show(R r);
}
如果实现了接口的类不想明确类型,将交由创建对象时明确类型
//实现了泛型接口的类:
public class MyTest3<T,R> implements MyInterface<T,R>
{
@Override
public T show(R r) {
return null;
}
}
//泛型接口:
public interface MyInterface<T,R>
{
public T show(R r);
}
//测试类:
public class MyTest2
{
public static void main(String[] args)
{
new MyInterface<String,Integer>(){
@Override
public String show(Integer integer) {
System.out.println(integer.toString());
return null;
}
}.show(20);
}
}
泛型方法:
//工具类:
public class MyClass
{
public<T> void show(T t){
System.out.println("这是泛型方法"+t);
}
}
//测试类:
public class MyTest3
{
public static void main(String[] args)
{
MyClass myClass = new MyClass();
myClass.show("abc");//这是泛型方法abc
myClass.show(12);//这是泛型方法12
myClass.show(new Object());//这是泛型方法java.lang.Object@1540e19d
myClass.show('好');//这是泛型方法好
}
}
泛型的通配符以及向上限定、向下限定:
//测试类:
import java.util.ArrayList;
public class MyTest3
{
public static void main(String[] args)
{
//这里的<?>代表后面new对象的时候可以是任意类型,它是泛型的通配符
ArrayList<?> obj1=new ArrayList<>();
//这里的<? super Cat>代表向上限定,也就是后面new对象的时候只能是Cat类或者是它的父类
ArrayList<? super Cat> obj2=new ArrayList<Cat>();//可以是Animal类,或者Object类,也可以是它本身Cat类
//这里的<? extends Animal>代表向下限定,也就是后面new对象的时候只能是Animal类或者是它的子类
ArrayList<? extends Animal> obj3=new ArrayList<>();//可以是Animal类,或者Cat类、Dog类
}
}
//Animal类:
class Animal{}
class Cat extends Animal{}
class Dog extends Animal{}
新式for循环
- JDK1.5之后,有一个新的语法规则,可以用来一次处理容器中的每一个元素,而不必为指定下标值而分心。
- 语法格式:for(容器的数据类型 容器中元素的名字:容器名) {语法块}
因此我们只需要明确遍历的这个容器数据类型是什么,容器名字叫什么。 - 注意:这种增强for循环不能在遍历该容器的时候去增加或者删除里面的元素,否则就会报出并发修改异常的错误,但相反,在普通for循环当中就不会发生这种问题。
import java.util.ArrayList;
public class MyTest1
{
public static void main(String[] args)
{
int[] arr={10,20,30,40,50};
for (int i : arr)
{
System.out.println(i);
}
ArrayList<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("王五");
list.add("赵六");
list.add("田七");
for (String s : list)
{
//在程序遍历的途中去增删元素,与之前迭代器出现的问题一样
//ConcurrentModificationException——并发修改异常
if(s.equals("王五"))
{
list.add("s001");
}
if(s.equals("赵六"))
{
list.remove("赵六");
}
}
}
}
asList方法
- 之前我们在Collection集合当中见到一个方法是将当前集合转为数组,代码如下:
import java.util.ArrayList;
import java.util.Arrays;
public class MyTest2
{
public static void main(String[] args)
{
ArrayList<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("王五");
list.add("赵六");
list.add("田七");
Object[] array = list.toArray();
System.out.println(Arrays.toString(array));
//[张三, 李四, 王五, 赵六, 田七]
}
}
- 关于可变参数:
我们可以看到asList()方法中的参数是泛型类的,并且参数为可变的,关于可变参数,顾名思义,就是可以接受多个参数;
它的语法:(参数数据类型… 参数名 )。可变参数本质上是一个数组,并且需要注意的是,可变参数必须位于参数列表的最后位置。
public class MyTest3
{
public static void main(String[] args)
{
int max= addMethod(32*48,28*54);
System.out.println(max);
}
private static int addMethod(int... i)
{
int max=i[0];
for (int j = 1; j < i.length; j++)
{
if(i[j]>max)
max=i[j];
}
return max;
}
}
- 反过来,也存在一个将数组转为集合的方法,是Arrays类里面的静态的方法:
代码如下:
public class MyTest3
{
public static void main(String[] args)
{
int[] arr={10,20,30,40,50,60};
List<int[]> list = Arrays.asList(arr);
int[] a = list.get(0);
System.out.println(Arrays.toString(a));
//[10, 20, 30, 40, 50, 60]
//上面的代码可以看出,这个方法将原来数组arr中的所有元素当作一个集合元素存在了集合当中
System.out.println(list.get(0)[2]);//30
Integer[] arr1={10,20,30,40,50,60};
List<Integer> list1 = Arrays.asList(arr1);
Integer num = list1.get(0);
System.out.println(num);//10
//list1调用get()取元素后纠错发现返回值是一个Integer类型的值
//和上面的结果不一样,为什么?
//其实细想就可以明白,集合中只能存储引用类型的数据,上面的数组显然是被当做一个元素存进集合里了
//而Integer类型的数据是引用类型的数据,集合在存储的时候把每一个数据都当作集合的元素
for (Integer ele : arr1) {
System.out.println(ele);
}
Integer[] arr2={110,120,130,140,150,160};
List<Integer[]> list2 = Arrays.asList(arr1, arr2);
System.out.println(list2.get(0)[0]);//10
System.out.println(list2.get(1)[0]);//110
//上面的代码执行之后可以发现,这个方法在你传入多个引用类型的数组之后
//就会将两个数组分别当作元素存入集合当中
//也就是这时候集合当中存储的是两个元素,他们都是Integer[]数组类型
//arr2.add();//调不到
}
}
- 【总结】
1、如果给你的数组是基本类型的数组,他会把数组对象装到集合当中;
2、如果给你的数组是引用类型的数组,他将数组元素取出来,再存进去;
3、如果你传入的是两个以上的对象数组,他会将数组装到集合当中;
4、如果你用转换过来的集合调用add()和remove()方法,发现调不到,这是因为这个集合只能取元素,不能再添加或删除元素;
set类集合
- 查看API :
一个不包含重复元素的 collection;更确切地讲,set 不包含满足 e1.equals(e2) 的元素对 e1 和 e2,并且最多包含一个 null 元素;正如其名称所暗示的,此接口模仿了数学上的 set 抽象。
在所有构造方法以及 add、equals 和 hashCode 方法的协定上,Set 接口还加入了其他规定,这些规定超出了从 Collection 接口所继承的内容。出于方便考虑,它还包括了其他继承方法的声明(这些声明的规范已经专门针对 Set 接口进行了修改,但是没有包含任何其他的规定)。
散列集(HashSet)
- 链表和数组可以按照人们的意愿排列元素的次序。但是要想查看某个指定的元素,却又忘了它的位置,就需要访问所有的元素,直到找到为止。如果集合当中包含了很多元素,将会耗费许多时间。
- 有一种众所周知的数据结构,可以快速的查找所需的对象,这就是散列表。散列表为每个对象计算一个整数,称为散列码,它是由不同数据域的对象产生的不同的散列码。String类的散列码是由hashCode()产生的。如果自定义,就要负责实现这个类的hashCode()方法,注意,自己实现的hashCode()应该与equals()兼容。
- 在Java中,散列表用数组+链表实现。每个链表被称为桶。要想查表中对象的位置,就要先计算它的散列码,然后与桶的总数取余,所得到的结果就是保存这个元素的桶的索引。或许会很幸运,在这个桶里面没有其他元素,此时将元素直接插入桶中即可。当然,有时也会遇到被桶占满的情况,这是不可避免的。这种现象被称为散列冲突。这时,需要新对象与桶中的所有元素进行对比,查看这个对象是否存在。如果散列码是合理且随机分布的,桶的数目也足够大,需要比较的次数就会很少。
- 如果想要更多的控制散列表的运行性能,就要制定一个初始的桶数。桶数是指用于收集具有相同散列值的桶的数目。如果要插入的元素太多,就会增加冲突的可能性,降低运行性能。
- 如果散列表太慢满,就需要再散列。例如,如果装填因子为0.75,也就是表中存储的元素超过75%,就需要再散列。
- Java集合类库中提供的HashSet类,它实现了基于散列表的集。它的底层数据结构是哈希表,就像新华字典;集合内的元素无序且唯一;并且允许存储null值;实现不同步,线程不安全效率高。
import java.util.HashSet;
public class Set集合
{
public static void main(String[] args)
{
HashSet<String> set = new HashSet<>();
set.add("aaa");
set.add("bbb");
set.add("ccc");
set.add("ddd");
set.add("eee");
set.add("fff");
set.add("bbb");
set.add("ccc");
System.out.println(set);
//[aaa, ccc, bbb, eee, ddd, fff]
//很明显,元素无序且唯一
HashSet<Integer> set1 = new HashSet<>();
set1.add(100);
set1.add(200);
set1.add(300);
set1.add(400);
set1.add(500);
set1.add(200);
set1.add(300);
set1.add(400);
System.out.println(set1);
//[400, 100, 500, 200, 300]
}
}
- 下面来看一段代码:
测试类:
import java.util.HashSet;
public class Set集合2
{
public static void main(String[] args)
{
HashSet<Student> stuHash = new HashSet<>();
stuHash.add(new Student("张三", 18));
stuHash.add(new Student("李四", 19));
stuHash.add(new Student("王五", 20));
stuHash.add(new Student("张三", 18));
stuHash.add(new Student("李四", 19));
stuHash.add(new Student("王五", 20));
stuHash.add(new Student("赵六", 21));
for (Student hash : stuHash)
{
System.out.println(hash);
}
//依旧是无序,但是却存在重复元素,为什么?
/*Student{name='李四', age=19}
Student{name='李四', age=19}
Student{name='赵六', age=21}
Student{name='张三', age=18}
Student{name='王五', age=20}
Student{name='张三', age=18}
Student{name='王五', age=20}*/
}
}
Student类:
import java.util.Objects;
public class Student
{
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 Student() {}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- 为什么会出现这种情况?我们可以查看.add方法的源码,看看这个方法在接收了对象之后进行了怎样的处理:
部分源码:
public boolean add(E e)
{
//你在调用hashSet集合当中的add方法时,添加的元素被e接收,然后又调用map里面的put方法
return map.put(e, PRESENT)==null;
}
//可以看到这里的map本质是hashMap类,也就是hashSet底层是用hashMap存储的
private transient HashMap<E,Object> map;
public V put(K key, V value)
{
//put方法里面调用putVal(),这里的key就是传过来的添加的对象,而value是一个固定的值
return putVal(hash(key), key, value, false, true);
}
static final int hash(Object key)
{
//hash这个方法在接收了你添加的对象之后,返回该对象的hashcode()值(前提是这个对象不是null),并对这个值进行了位运算
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
/**
* Implements Map.put and related methods
*
* @param hash hash for key——使用hash方法计算出来的hashcode值
* @param key the key——你要添加的对象
* @param value the value to put——固定的值
* @param onlyIfAbsent if true, don't change existing value——固定的值
* @param evict if false, the table is in creation mode.——固定的值
* @return previous value, or null if none
*/
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;
//源码里面调用了key(也就是你添加的对象)的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;
}
- 看过上面的源码之后,发现.add()底层调用了你添加的对象的equals()和hashCode(),我们认为一个Student对象,只要它的成员变量的值一样,就认为两个对象的值一样,但是由于自己定义的Student类里面没有重写这两个方法,因此默认调用父类Objects类里面的这两个方法,比较的是地址值,每new一个对象,即使成员变量值一样,也会在堆区开辟一块新的空间,因此编译器并不认为成员变量相同的对象一样。修改后的代码:
import java.util.HashSet;
public class Test
{
public static void main(String[] args)
{
HashSet<Student> stuHash = new HashSet<>();
stuHash.add(new Student("张三", 18));
stuHash.add(new Student("李四", 19));
stuHash.add(new Student("王五", 20));
stuHash.add(new Student("张三", 18));
stuHash.add(new Student("李四", 19));
stuHash.add(new Student("王五", 20));
stuHash.add(new Student("赵六", 21));
for (Student hash : stuHash)
{
System.out.println(hash);
}
//无序且唯一
/*Student{name='赵六', age=21}
Student{name='张三', age=18}
Student{name='王五', age=20}
Student{name='李四', age=19}*/
}
}
Student类:
import java.util.Objects;
public class Student
{
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 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);
}
}
- hashSet之所以能保证元素的唯一性是因为重写了equals(),那为什么要重写hashCode(),其实是为了减少碰撞。如果你给代码中的hashCode()返回值写为死值0,再去统计equals()方法,发现次数很多,而如果你在hashCode()中返回的是关于对象的一个特殊的值,equals()调用次数明显下降很多。
测试类:
import java.util.HashSet;
public class Test
{
public static void main(String[] args)
{
HashSet<Student> stuHash = new HashSet<>();
stuHash.add(new Student("张三", 18));
stuHash.add(new Student("李四", 19));
stuHash.add(new Student("王五", 20));
stuHash.add(new Student("张三", 18));
stuHash.add(new Student("李四", 19));
stuHash.add(new Student("王五", 20));
stuHash.add(new Student("赵六", 21));
for (Student hash : stuHash)
{
System.out.println(hash);
}
//无序且唯一
//hashCode()返回值为0的时候,equals()的调用
/*Student{name='张三', age=18}-----Student{name='李四', age=19}
Student{name='张三', age=18}-----Student{name='王五', age=20}
Student{name='李四', age=19}-----Student{name='王五', age=20}
Student{name='张三', age=18}-----Student{name='张三', age=18}
Student{name='张三', age=18}-----Student{name='李四', age=19}
Student{name='李四', age=19}-----Student{name='李四', age=19}
Student{name='张三', age=18}-----Student{name='王五', age=20}
Student{name='李四', age=19}-----Student{name='王五', age=20}
Student{name='王五', age=20}-----Student{name='王五', age=20}
Student{name='张三', age=18}-----Student{name='赵六', age=21}
Student{name='李四', age=19}-----Student{name='赵六', age=21}
Student{name='王五', age=20}-----Student{name='赵六', age=21}
Student{name='张三', age=18}
Student{name='李四', age=19}
Student{name='王五', age=20}
Student{name='赵六', age=21}*/
//hashCode()返回值为关于对象的值的时候,equals()的调用
/*Student{name='张三', age=18}-----Student{name='张三', age=18}
Student{name='李四', age=19}-----Student{name='李四', age=19}
Student{name='王五', age=20}-----Student{name='王五', age=20}
Student{name='李四', age=19}
Student{name='张三', age=18}
Student{name='王五', age=20}
Student{name='赵六', age=21}*/
}
}
Student中的方法:
@Override
public boolean equals(Object o)
{
System.out.println(o+"-----"+this);
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 0;
//return this.name.hashCode()+this.age*11;返回值有关于对象
//自动生成的代码返回值:
//return Objects.hash(name, age);
}
LinkedHashSet类
- 元素有序且唯一;链表保证了有序,哈希表保证了唯一;
- 注意,此实现不是同步的;线程不安全,效率高。
import java.util.HashSet;
import java.util.LinkedHashSet;
public class LinkedHashSet集合
{
public static void main(String[] args)
{
//LinkedHashSet:元素有序且唯一,链表保证了有序,哈希表保证了唯一
LinkedHashSet<String> set = new LinkedHashSet<>();
set.add("abc");
set.add("def");
set.add("ccc");
set.add("ddd");
set.add("eeg");
set.add("def");
set.add("ccc");
set.add("eee");
System.out.println(set);
//[abc, def, ccc, ddd, eeg, eee]
HashSet<String> list = new HashSet<>();
list.add("abc");
list.add("def");
list.add("abc");
list.add("def");
list.add("abc");
list.add("def");
list.add("abc");
list.add("def");
LinkedHashSet<String> set1 = new LinkedHashSet<>(list);
System.out.println(set1);
//[abc, def]
}
}
树集(TreeSet)
- TreeSet类与散列集十分类似,不过,它比散列集有所改进。树集是一个有序集合。可以以任意顺序将元素插到集合当中。在对集合进行遍历的时,每个值将自动的按照排序后的顺序呈现。
import java.util.TreeSet;
public class Test
{
public static void main(String[] args)
{
TreeSet<Object> sorter = new TreeSet<>();
sorter.add("Bob");
sorter.add("Amy");
sorter.add("Carl");
for (Object o : sorter)
{
System.out.print(o+"\t");
//Amy Bob Carl
}
}
}
- 正如TreeSet类名所示,排序是用树结构完成的(红黑树)。将一个元素添加到树中要比添加到散列表中慢,但是,与检查数组或链表中的重复元素相比还是快很多。
- TreeSet集合比较器排序:
package com.baidu.MyDemo2;
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSet1
{
public static void main(String[] args)
{
TreeSet<Student> set = new TreeSet<>();
set.add(new Student("张三22222222222", 23));
set.add(new Student("李四11111111111", 23));
set.add(new Student("王五1333333333333333333333", 21));
set.add(new Student("赵六55555555555555", 22));
set.add(new Student("田七6666666666666", 19));
set.add(new Student("李八7777777777777777", 18));
set.add(new Student("刘星88888888888", 17));
for (Student stu : set) { System.out.println(stu); }
}
}
- 你给TreeSet类的集合存学生对象的时候,一存就会报错,为什么?
前面说这个集合可以根据你的需要进行排序,但是它在存储学生对象的时候,没有办法进行比较;
- 排序分为自然排序和比较器排序:
1、如果new一个TreeSet对象的时候采用空参构造,就默认为是自然排序。你给集合当中添加Integer类型的数据时,就不会报错,这是因为Integer类当中实现了Comparable接口,重写了这个接口中的compare()。因此自定义的Student类如果想要添加到树集当中,就必须实现接口Comparable,并重写compare()。
2、比较器排序,采用有参构造,你给这个TreeSet对象的构造方法中传递一个比较器,实现接口Comparator,并重写compare()。
3、compare()的返回值代表元素应该添加到哪,后面添加的元素与根节点相比较,为0说明集合当中存在同样的对象,就存不进去了;因此如果你在代码当中将compare()返回值写死为0,就会出现一种现象,集合中只存进去根节点元素,其余都存不进去。为正数添加到红黑树的左边,为负数添加到红黑树的右边;如果compare()的返回值写成任意正负数,全部的元素都存储在了一个数组当中。
比较器排序:
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSet1
{
public static void main(String[] args)
{
//在这里我们发现比较器只使用一次,那我们需要写一个类来实现接口,然后new这个类的对象吗?
//我们可以直接使用匿名内部类:
TreeSet<Student> set = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
//按照姓名长度来排序
int num=o1.getName().length()-o2.getName().length();
int num1=(num==0)?(o1.getName().compareTo(o2.getName())):num;
int num2=(num1==0)?(o1.getAge()-o2.getAge()):num1;
return num2;
}
});
set.add(new Student("张三22222222222", 23));
set.add(new Student("李四11111111111", 23));
set.add(new Student("王五1333333333333333333333", 21));
set.add(new Student("赵六55555555555555", 22));
set.add(new Student("田七6666666666666", 19));
set.add(new Student("李八7777777777777777", 18));
set.add(new Student("刘星88888888888", 17));
for (Student stu : set) { System.out.println(stu); }
/*Student{name='刘星88888888888', age=17}
Student{name='张三22222222222', age=23}
Student{name='李四11111111111', age=23}
Student{name='田七6666666666666', age=19}
Student{name='赵六55555555555555', age=22}
Student{name='李八7777777777777777', age=18}
Student{name='王五1333333333333333333333', age=21}*/
}
}
自然排序:
//测试类:
import java.util.TreeSet;
public class TreeSet2
{
public static void main(String[] args)
{
TreeSet<Student> treeSet = new TreeSet<>();
//空参构造,采用的时候是自然排序
treeSet.add(new Student("张三22222222222", 23));
treeSet.add(new Student("李四11111111111", 23));
treeSet.add(new Student("王五1333333333333333333333", 21));
treeSet.add(new Student("赵六55555555555555", 22));
treeSet.add(new Student("田七6666666666666", 19));
treeSet.add(new Student("李八7777777777777777", 18));
treeSet.add(new Student("刘星88888888888", 17));
for (Student student : treeSet) {
System.out.println(student);
}
}
}
//Student类:
public class Student implements Comparable<Student>
{
private String name;
private int age;
@Override
public int compareTo(Student o)
{
//需求是按照姓名的长短来排序:
int i = this.name.length() - o.name.length();
//如果长度一样,还得比较姓名内容
int i1=i==0?this.name.compareTo(o.name):i;
//当长度一样,内容一样,还得比较年龄
int i2=i1==0?this.age-o.age:i1;
return i2;
//里面的比较逻辑需要你自己写,它只要一个返回值正负0
}
...
}
- 其实不光是TreeSet集合有比较器这一说,其它集合也会用到,比如ArrayList里面的sort(),就用到了;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
public class MyTest1
{
public static void main(String[] args)
{
ArrayList<Integer> list = new ArrayList<>();
list.add(14);
list.add(21);
list.add(13);
list.add(47);
list.add(1112);
list.add(222);
list.add(309);
list.add(4);
//对list集合中的元素排序
list.sort(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1-o2;
}
});
System.out.println(list);
//[4, 13, 14, 21, 47, 222, 309, 1112]
Integer[] arr={10,2,16,43,23,67};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
//[2, 10, 16, 23, 43, 67]
Arrays.sort(arr, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
});
System.out.println(Arrays.toString(arr));
//[67, 43, 23, 16, 10, 2]
}
}