JAVA集合框架详解(一)

文章目录

  • 集合概述
    • Collection集合
      • Collection父接口
      • Collection子接口
        • List集合
        • List实现类
          • ArrayList【重点】
          • ArrayList源码分析
          • Vector
          • LinkedList
          • LinkedList源码分析
          • ArrayList和LinkedList区别
        • 泛型概述
          • 泛型类
          • 泛型接口
          • 泛型方法
          • 泛型集合
        • Set集合概述
          • Set子接口
        • Set实现类
          • HashSet【重点】
          • TreeSet

集合概述

  • 概念:对象的容器,定义了对多个对象进行操作的的常用方法。可实现数组的功能。
  • 和数组的区别:
    1. 数组长度固定,集合长度不固定。
    2. 数组可以存储基本类型和引用类型,集合只能存储引用类型。
  • 位置: java.util.*;

Collection集合

JAVA集合框架详解(一)_第1张图片

Collection父接口

  • 特点:代表一组任意类型的对象,无序、无下标、不能重复。
  • 方法:
boolean add(Object obj) //添加一个对象。
boolean addAll(Collection c) //讲一个集合中的所有对象添加到此集合中。
void clear() //清空此集合中的所有对象。
boolean contains(Object o) //检查此集合中是否包含o对象。
boolean equals(Object o) //比较此集合是否与指定对象相等。
boolean isEmpty() //判断此集合是否为空。
boolean remove(Object o) //在此集合中移除o对象。
int size() //返回此集合中的元素个数。
Object[] toArray() //姜此集合转换成数组。
//使用迭代器(迭代器专门用来遍历集合的一种方式)
        //hasnext();判断是否有下一个元素
        //next();获取下一个元素
        //remove();删除当前元素
        Iterator iterator=collection.Itertor();
        while(iterator.hasnext()){
     
            String object=(String)iterator.next();
            System.out.println(s);
            //删除操作
            //迭代过程中不能使用collection的删除方法
            //collection.remove(s);引发错误:并发修改异常
            //iterator.remove();应使用迭代器的方法
//      * 4.判断
        System.out.println(collection.contains("西瓜"));//true
        System.out.println(collection.isEmpty());//false
        }

Collection子接口

List集合

  • 特点:有序、有下标、元素可以重复。
  • 方法:
void add(int index,Object o) //在index位置插入对象o。
boolean addAll(index,Collection c) //将一个集合中的元素添加到此集合中的index位置。
Object get(int index) //返回集合中指定位置的元素。
List subList(int fromIndex,int toIndex) //返回fromIndex和toIndex之间的集合元素。
/**
 * List子接口的使用(一)
 * 特点:1.有序有下标 2.可以重复
 * 
 * 1.添加元素
 * 2.删除元素
 * 3.遍历元素
 * 4.判断
 * 5.获取位置
 */
public class Demo3 {
     
	public static void main(String[] args) {
     
		List list=new ArrayList<>();
		//1.添加元素
		list.add("tang");
		list.add("he");
		list.add(0,"yu");//插入操作
		System.out.println("元素个数:"+list.size());
		System.out.println(list.toString());
		//2.删除元素
		list.remove(0);
		//list.remove("yu");结果同上
		System.out.println("删除之后:"+list.size());
		System.out.println(list.toString());
		//3.遍历元素
		//3.1 使用for遍历
		for(int i=0;i<list.size();++i) {
     
			System.out.println(list.get(i));
		}
		//3.2 使用增强for
		for(Object object:list) {
     
			System.out.println(object);
		}
		//3.3 使用迭代器
		Iterator iterator=list.iterator();
		while (iterator.hasNext()) {
     
			System.out.println(iterator.next());
		}
		//3.4使用列表迭代器,listIterator可以双向遍历,添加、删除及修改元素。
		ListIterator listIterator=list.listIterator();
		//从前往后
		while (listIterator.hasNext()) {
     
			System.out.println(listIterator.next());		
		}
		//从后往前(此时“遍历指针”已经指向末尾)
		while(listIterator.hasPrevious()) {
     
			System.out.println(listIterator.previous());
		}
		//4.判断
		System.out.println(list.isEmpty());
		System.out.println(list.contains("tang"));
		//5.获取位置
		System.out.println(list.indexOf("tang"));
	}
}

/**
 * List子接口的使用(二)
 * 1.添加元素
 * 2.删除元素
 * 3.遍历元素
 * 4.判断
 * 5.获取位置
 */
public class Demo4 {
     
	public static void main(String[] args) {
     
		List list=new ArrayList();
		//1.添加数字数据(自动装箱)
		list.add(20);
		list.add(30);
		list.add(40);
		list.add(50);
		System.out.println("元素个数:"+list.size());
		System.out.println(list.toString());
		//2.删除元素
		list.remove(0);
		//list.remove(20);很明显数组越界错误,改成如下
		//list.remove(Object(20));
		//list.remove(new Integer(20));
		System.out.println("元素个数:"+list.size());
		System.out.println(list.toString());
		//3-5不再演示,与之前类似
		//6.补充方法subList,返回子集合,含头不含尾
		List list2=list.subList(1, 3);
		System.out.println(list2.toString());	
	}
}

List实现类

ArrayList【重点】
  • 数组结构实现,查询块、增删慢;
  • 运行效率快、线程不安全。
/**
 * ArrayList的使用
 * 存储结构:数组;
 * 特点:查找遍历速度快,增删慢。
 * 1.添加元素
 * 2.删除元素
 * 3.遍历元素
 * 4.判断
 * 5.查找
 */
public class Demo5 {
     
	public static void main(String[] args) {
     
		ArrayList arrayList=new ArrayList<>();
		//1.添加元素
		Student s1=new Student("唐", 21);
		Student s2=new Student("何", 22);
		Student s3=new Student("余", 21);
		arrayList.add(s1);
		arrayList.add(s2);
		arrayList.add(s3);
		System.out.println("元素个数:"+arrayList.size());
		System.out.println(arrayList.toString());
		//2.删除元素
		arrayList.remove(s1);
		//arrayList.remove(new Student("唐", 21));
		//注:这样可以删除吗(不可以)?显然这是两个不同的对象。
		//假如两个对象属性相同便认为其是同一对象,那么如何修改代码?
		//3.遍历元素
		//3.1使用迭代器
		Iterator iterator=arrayList.iterator();
		while(iterator.hasNext()) {
     
			System.out.println(iterator.next());
		}
		//3.2使用列表迭代器
		ListIterator listIterator=arrayList.listIterator();
		//从前往后遍历
		while(listIterator.hasNext()) {
     
			System.out.println(listIterator.next());
		}
		//从后往前遍历
		while(listIterator.hasPrevious()) {
     
			System.out.println(listIterator.previous());
		}
		//4.判断
		System.out.println(arrayList.isEmpty());
		//System.out.println(arrayList.contains(new Student("何", 22)));
		//注:与上文相同的问题。
		//5.查找
		System.out.println(arrayList.indexOf(s1));
	}
}

注:Object里的equals(this==obj)用地址和当前对象比较,如果想实现代码中的问题,可以在学生类中重写equals方法:

@Override
public boolean equals(Object obj) {
     
	//1.是否为同一对象
	if (this==obj) {
     
		return true;
	}
	//2.判断是否为空
	if (obj==null) {
     
		return false;
	}
	//3.判断是否是Student类型
	if (obj instanceof Student) {
     
		Student student=(Student) obj;
		//4.比较属性
		if(this.name.equals(student.getName())&&this.age==student.age) {
     
			return true;
		}
	}
	//不满足,返回false
	return false;
}

ArrayList源码分析
  • 默认容量大小:private static final int DEFAULT_CAPACITY = 10;
  • 存放元素的数组:transient Object[] elementData;
  • 实际元素个数:private int size;
  • 创建对象时调用的无参构造函数:
//这是一个空的数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {
     };
public ArrayList() {
     
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

这段源码说明当你没有向集合中添加任何元素时,集合容量为0。那么默认的10个容量怎么来的呢?

这就得看看add方法的源码了:

public boolean add(E e) {
     
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

假设你new了一个数组,当前容量为0,size当然也为0。这时调用add方法进入到ensureCapacityInternal(size + 1);该方法源码如下:

private void ensureCapacityInternal(int minCapacity) {
     
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

该方法中的参数minCapacity传入的值为size+1也就是 1,接着我们再进入到calculateCapacity(elementData, minCapacity)里面:

private static int calculateCapacity(Object[] elementData, int minCapacity) {
     
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
     
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

上文说过,elementData就是存放元素的数组,当前容量为0,if条件成立,返回默认容量DEFAULT_CAPACITY也就是10。这个值作为参数又传入ensureExplicitCapacity()方法中,进入该方法查看源码:

private void ensureExplicitCapacity(int minCapacity) {
     
    modCount++;
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

我们先不要管modCount这个变量。

因为elementData数组长度为0,所以if条件成立,调用grow方法,重要的部分来了,我们再次进入到grow方法的源码中:

private void grow(int minCapacity) {
     
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

这个方法先声明了一个oldCapacity变量将数组长度赋给它,其值为0;又声明了一个newCapacity变量其值为oldCapacity+一个增量,可以发现这个增量是和原数组长度有关的量,当然在这里也为0。第一个if条件满足,newCapacity的值为10(这就是默认的容量,不理解的话再看看前面)。第二个if条件不成立,也可以不用注意,因为MAX_ARRAY_SIZE的定义如下:

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

这个值太大了以至于第二个if条件没有了解的必要。

最后一句话就是为elementData数组赋予了新的长度,Arrays.copyOf()方法返回的数组是新的数组对象,原数组对象不会改变,该拷贝不会影响原来的数组。copyOf()的第二个自变量指定要建立的新数组长度,如果新数组的长度超过原数组的长度,则保留数组默认值。

这时候再回到add的方法中,接着就向下执行elementData[size++] = e;到这里为止关于ArrayList就讲解得差不多了,当数组长度为10的时候你们可以试着过一下源码,查一下每次的增量是多少(答案是每次扩容为原来的1.5倍)。

Vector
  • 数组结构实现,查询快、增删慢;
  • 运行效率慢、线程安全。
/**
 * Vector的演示使用
 * 
 *1.添加数据
 *2.删除数据
 *3.遍历
 *4.判断
 */
public class Demo1 {
     
	public static void main(String[] args) {
     
		Vector vector=new Vector<>();
		//1.添加数据
		vector.add("tang");
		vector.add("he");
		vector.add("yu");
		System.out.println("元素个数:"+vector.size());
		//2.删除数据
		/*
		 * vector.remove(0); vector.remove("tang");
		 */
		//3.遍历
		//使用枚举器
		Enumeration enumeration=vector.elements();
		while (enumeration.hasMoreElements()) {
     
			String s = (String) enumeration.nextElement();
			System.out.println(s);
		}
		//4.判断
		System.out.println(vector.isEmpty());
		System.out.println(vector.contains("he"));
		//5. Vector其他方法
		//firstElement()  lastElement()  ElementAt();
	}
}
LinkedList
  • 链表结构实现,增删快,查询慢。
/**
 * LinkedList的用法
 * 存储结构:双向链表
 * 1.添加元素
 * 2.删除元素
 * 3.遍历
 * 4.判断
 */
public class Demo2 {
     
	public static void main(String[] args) {
     
		LinkedList linkedList=new LinkedList<>();
		Student s1=new Student("唐", 21);
		Student s2=new Student("何", 22);
		Student s3=new Student("余", 21);
		//1.添加元素
		linkedList.add(s1);
		linkedList.add(s2);
		linkedList.add(s3);
		linkedList.add(s3);
		System.out.println("元素个数:"+linkedList.size());
		System.out.println(linkedList.toString());
		//2.删除元素
		/*
		 * linkedList.remove(new Student("唐", 21));
		 * System.out.println(linkedList.toString());
		 */
		//3.遍历
		//3.1 使用for
		for(int i=0;i<linkedList.size();++i) {
     
			System.out.println(linkedList.get(i));
		}
		//3.2 使用增强for
		for(Object object:linkedList) {
     
			Student student=(Student) object;
			System.out.println(student.toString());
		}
		//3.3 使用迭代器
		Iterator iterator =linkedList.iterator();
		while (iterator.hasNext()) {
     
			Student student = (Student) iterator.next();
			System.out.println(student.toString());
		}
		//3.4 使用列表迭代器(略)
		//4. 判断
		System.out.println(linkedList.contains(s1));
		System.out.println(linkedList.isEmpty());
		System.out.println(linkedList.indexOf(s3));
	}
}

LinkedList源码分析

LinkedList首先有三个属性:

  • 链表大小:transient int size = 0;
  • (指向)第一个结点/头结点:transient Node first;
  • (指向)最后一个结点/尾结点:transient Node last;

关于Node类型我们再进入到类里看看:

private static class Node<E> {
     
    E item;
    Node<E> next;
    Node<E> prev;

    Node(Node<E> prev, E element, Node<E> next) {
     
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

首先item存放的是实际数据;next指向下一个结点而prev指向上一个结点。

Node带参构造方法的三个参数分别是前一个结点、存储的数据、后一个结点,调用这个构造方法时将它们赋值给当前对象。

LinkedList是如何添加元素的呢?先看看add方法:

public boolean add(E e) {
     
    linkLast(e);
    return true;
}

进入到linkLast方法:

void linkLast(E e) {
     
    final Node<E> l = last;
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}

假设刚开始new了一个LinkedList对象,first和last属性都为空,调用add进入到linkLast方法。

首先创建一个Node变量 l 将last(此时为空)赋给它,然后new一个newNode变量存储数据,并且它的前驱指向l,后继指向null;再把last指向newNode。如下图所示:
JAVA集合框架详解(一)_第2张图片
如果满足if条件,说明这是添加的第一个结点,将first指向newNode:
JAVA集合框架详解(一)_第3张图片
至此,LinkedList对象的第一个数据添加完毕。假设需要再添加一个数据,我们可以再来走一遍,过程同上不再赘述,图示如下:
JAVA集合框架详解(一)_第4张图片

ArrayList和LinkedList区别
  • ArrayList:必须开辟连续空间,查询快,增删慢。
  • LinkedList:无需开辟连续空间,查询慢,增删快。
    JAVA集合框架详解(一)_第5张图片

泛型概述

  • Java泛型是JDK1.5中引入的一个新特性,其本质是参数化类型,把类型作为参数传递。

  • 常见形式有泛型类、泛型接口、泛型方法。

  • 语法:

    T称为类型占位符,表示一种引用类型。

  • 好处:

    提高代码的重用性。
    防止类型转换异常,提高代码的安全性。

泛型类
/**
 * 泛型类
 * 语法:类名
 * T是类型占位符,表示一种引用类型,编写多个使用逗号隔开
 * 
 */
public class myGeneric<T>{
     
	//1.创建泛型变量
	//不能使用new来创建,因为泛型是不确定的类型,也可能拥有私密的构造方法。
	T t;
	//2.泛型作为方法的参数
	public void show(T t) {
     
		System.out.println(t);
	}
	//泛型作为方法的返回值
	public T getT() {
     
		return t;
	}
}

/**
 * 注意:
 * 1.泛型只能使用引用类型
 * 2.不同泛型类型的对象不能相互赋值
 */
public class testGeneric {
     
	public static void main(String[] args) {
     
		//使用泛型类创建对象
		myGeneric<String> myGeneric1=new myGeneric<String>();
		myGeneric1.t="tang";
		myGeneric1.show("he");
		
		myGeneric<Integer> myGeneric2=new myGeneric<Integer>();
		myGeneric2.t=10;
		myGeneric2.show(20);
		Integer integer=myGeneric2.getT();
	}
}
泛型接口
/**
 * 泛型接口
 * 语法:接口名
 * 注意:不能创建泛型静态常量
 */
public interface MyInterface<T> {
     
    //创建常量
	String nameString="tang";
    
	T server(T t);
}

/**
 * 实现接口时确定泛型类
 */
public class MyInterfaceImpl implements MyInterface<String>{
     
	@Override
	public String server(String t) {
     
		System.out.println(t);
		return t; 
	}
}
//测试
MyInterfaceImpl myInterfaceImpl=new MyInterfaceImpl();
myInterfaceImpl.server("xxx");
//xxx
/**
 * 实现接口时不确定泛型类
 */
public class MyInterfaceImpl2<T> implements MyInterface<T>{
     
	@Override
	public T server(T t) {
     
		System.out.println(t);
		return t;
	}
}
//测试
MyInterfaceImpl2<Integer> myInterfaceImpl2=new MyInterfaceImpl2<Integer>();
myInterfaceImpl2.server(2000);
//2000
泛型方法
/**
 * 泛型方法
 * 语法: 返回类型
 */
public class MyGenericMethod {
     
	public <T> void show(T t) {
     
		System.out.println("泛型方法"+t);
	}
}
//测试
MyGenericMethod myGenericMethod=new MyGenericMethod();
myGenericMethod.show("tang");
myGenericMethod.show(200);
myGenericMethod.show(3.14);
泛型集合
  • 概念:参数化类型、类型安全的集合,强制集合元素的类型必须一致。

  • 特点:

    编译时即可检查,而非运行时抛出异常。

    访问时,不必类型转换(拆箱)。

    不同泛型指尖引用不能相互赋值,泛型不存在多态。

之前我们在创建LinkedList类型对象的时候并没有使用泛型,但是进到它的源码中会发现:

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable{
     //略}

它是一个泛型类,而我之前使用的时候并没有传递,说明java语法是允许的,这个时候传递的类型是Object类,虽然它是所有类的父类,可以存储任意的类型,但是在遍历、获取元素时需要原来的类型就要进行强制转换。这个时候就会出现一些问题,假如往链表里存储了许多不同类型的数据,在强转的时候就要判断每一个原来的类型,这样就很容易出现错误。

Set集合概述

Set子接口
  • 特点:无序、无下标、元素不可重复。
  • 方法:全部继承自Collection中的方法。
/**
 * 测试Set接口的使用
 * 特点:1.无序,没有下标;2.重复
 * 1.添加数据
 * 2.删除数据
 * 3.遍历【重点】
 * 4.判断
 */
public class Demo1 {
     
	public static void main(String[] args) {
     
		Set<String> set=new HashSet<String>();
		//1.添加数据
		set.add("tang");
		set.add("he");
		set.add("yu");
		System.out.println("数据个数:"+set.size());
		System.out.println(set.toString());//无序输出
		//2.删除数据
		/*
		 * set.remove("tang"); System.out.println(set.toString());
		 */
		//3.遍历【重点】
		//3.1 使用增强for
		for (String string : set) {
     
			System.out.println(string);
		}
		//3.2 使用迭代器
		Iterator<String> iterator=set.iterator();
		while (iterator.hasNext()) {
     
			System.out.println(iterator.next());
		}
		//4.判断
		System.out.println(set.contains("tang"));
		System.out.println(set.isEmpty());
	}
}

Set实现类

HashSet【重点】
  • 基于HashCode计算元素存放位置。
  • 当存入元素的哈希码相同时,会调用equals进行确认,如结果为true,则拒绝后者存入。
/**
 * HashSet集合的使用
 * 存储结构:哈希表(数组+链表+红黑树)
 * 1.添加元素
 * 2.删除元素
 * 3.遍历
 * 4.判断
*/
public class Demo3 {
     
	public static void main(String[] args) {
     
		HashSet<Person> hashSet=new HashSet<>();
		Person p1=new Person("tang",21);
		Person p2=new Person("he", 22);
		Person p3=new Person("yu", 21);
		//1.添加元素
		hashSet.add(p1);
		hashSet.add(p2);
		hashSet.add(p3);
        //重复,添加失败
        hashSet.add(p3);
        //直接new一个相同属性的对象,依然会被添加,不难理解。
        //假如相同属性便认为是同一个对象,怎么修改?
        hashSet.add(new Person("yu", 21));
		System.out.println(hashSet.toString());
		//2.删除元素
		hashSet.remove(p2);
		//3.遍历
		//3.1 增强for
		for (Person person : hashSet) {
     
			System.out.println(person);
		}
		//3.2 迭代器
		Iterator<Person> iterator=hashSet.iterator();
		while (iterator.hasNext()) {
     
			System.out.println(iterator.next());		
		}
		//4.判断
		System.out.println(hashSet.isEmpty());
        //直接new一个相同属性的对象结果输出是false,不难理解。
        //注:假如相同属性便认为是同一个对象,该怎么做?
		System.out.println(hashSet.contains(new Person("tang", 21)));
	}
}

注:hashSet存储过程:

根据hashCode计算保存的位置,如果位置为空,则直接保存,否则执行第二步。
执行equals方法,如果方法返回true,则认为是重复,拒绝存储,否则形成链表。

存储过程实际上就是重复依据,要实现“注”里的问题,可以重写hashCode和equals代码:

@Override
public int hashCode() {
     
    final int prime = 31;
    int result = 1;
    result = prime * result + age;
    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;
    Person other = (Person) obj;
    if (age != other.age)
        return false;
    if (name == null) {
     
        if (other.name != null)
            return false;
    } else if (!name.equals(other.name))
        return false;
    return true;
}

hashCode方法里为什么要使用31这个数字大概有两个原因:

  • 31是一个质数,这样的数字在计算时可以尽量减少散列冲突。
  • 可以提高执行效率,因为31*i=(i<<5)-i,31乘以一个数可以转换成移位操作,这样能快一点;但是也有网上一些人对这两点提出质疑。
TreeSet
  • 基于排序顺序实现不重复。
  • 实现了SortedSet接口,对集合元素自动排序。
  • 元素对象的类型必须实现Comparable接口,指定排序规则。
  • 通过CompareTo方法确定是否为重复元素。
/**
 * 使用TreeSet保存数据
 * 存储结构:红黑树
 * 要求:元素类必须实现Comparable接口,compareTo方法返回0,认为是重复元素 
 */
public class Demo4 {
     
	public static void main(String[] args) {
     
		TreeSet<Person> persons=new TreeSet<Person>();
		Person p1=new Person("tang",21);
		Person p2=new Person("he", 22);
		Person p3=new Person("yu", 21);
		//1.添加元素
		persons.add(p1);
		persons.add(p2);
		persons.add(p3);
		//注:直接添加会报类型转换错误,需要实现Comparable接口
		System.out.println(persons.toString());
		//2.删除元素
		persons.remove(p1);
		persons.remove(new Person("he", 22));
		System.out.println(persons.toString());
		//3.遍历(略)
		//4.判断
		System.out.println(persons.contains(new Person("yu", 21)));
	}
}

查看Comparable接口的源码,发现只有一个compareTo抽象方法,在Person 类中实现它:

public class Person implements Comparable<Person>{
     
    @Override
	//1.先按姓名比
	//2.再按年龄比
	public int compareTo(Person o) {
     
		int n1=this.getName().compareTo(o.getName());
		int n2=this.age-o.getAge();
		return n1==0?n2:n1;
	}
}

除了实现Comparable接口里的比较方法,TreeSet也提供了一个带比较器Comparator的构造方法,使用匿名内部类来实现它:

/**
 * TreeSet的使用
 * Comparator:实现定制比较(比较器)
 */
public class Demo5 {
     
	public static void main(String[] args) {
     
		TreeSet<Person> persons=new TreeSet<Person>(new Comparator<Person>() {
     
			@Override
			public int compare(Person o1, Person o2) {
     
				// 先按年龄比较
				// 再按姓名比较
				int n1=o1.getAge()-o2.getAge();
				int n2=o1.getName().compareTo(o2.getName());
				return n1==0?n2:n1;
			}			
		});
		Person p1=new Person("tang",21);
		Person p2=new Person("he", 22);
		Person p3=new Person("yu", 21);
		persons.add(p1);
		persons.add(p2);
		persons.add(p3);
		System.out.println(persons.toString());
	}
}

你可能感兴趣的:(JavaSE,Java集合,List,Set)