Java集合

七. 集合

  • 集合是一个容器,可以容纳其他类型的数据(数组就是一个集合)

  • 集合不能存储基本数据类型,也不能直接存储java对象(只能存储内存地址/引用)

  • java中每一个不同的集合,底层对应不同的数据结构。往不同的集合中存储元素相当于把数据放到了不同的数据结构中

  • 所有的集合类和集合接口都在java.util包下

  • java中集合分为两大类:

    1. 单个方式存储元素,这一类集合中超级父接口:java.util.Collection
    2. 以键值对的方式存储元素,这一类集合中超级父接口:java.util.Map
  • Collection集合继承关系
    1. Iterable接口:表示可迭代、可遍历的,有iterator()方法

    2. Collection接口继承/泛化(is a)Iterable接口,表示所有集合都是可迭代、可遍历的

    3. Collection接口关联(has a)Iterator接口(集合的迭代器对象),Iterator接口有hasNext()、next()、remove()方法

    4. List接口泛化Collection接口。List集合存储元素特点:有序可重复,存储的元素有下标(有序指存入和取出时顺序相同,List集合下标从0开始,以1递增;可重复指存储元素可以相同)

      ArrayList集合实现List接口,底层采用数组数据结构,非线程安全

      LinkedList集合实现List接口,底层采用双向链表数据结构

      Vector集合底层采用数组数据结构,是线程安全的(所有方法都有synchronized关键字修饰,但效率较低,保证线程安全有其他方案,Vector使用较少)

    5. Set接口泛化Collection接口。Set集合存储元素特点:无序不可重复(存入和取出时顺序不一定相同,Set集合中元素没有下标)

      HashSet集合实现Set接口。new HashSet集合时在底层实际上new了一个HashMap集合,向HashSet集合中存储元素实际上是存储到HashMap集合中。HashMap集合是一个哈希表数据结构

      SortedSet接口泛化Set接口。SortedSet集合存储元素特点:继承Set集合无序不可重复,但SortedSet集合中的元素可以自动排序,称为可排序集合

      TreeSet方法实现SortSet接口。TreeSet集合底层实际是TreeMap集合,TreeMap底层采用二叉树数据结构

  • Map集合继承关系
    1. Map集合和Collection集合没有关系

      Map集合以key和value键值对方式存储元素,key和value存储的是对象内存地址

      所有Map集合的key都是无序不可重复的(Map集合的key和Set集合存储元素特点相同)

    2. HashMap类实现Map接口,HashMap集合底层是哈希表数据结构,非线程安全

    3. Hashtable类实现Map接口,底层是哈希表数据结构,是线程安全的(效率低,现在使用较少)

      Properties类继承Hashtable类,也是线程安全的。Properties的key和value只支持String类型,Properties被称为属性类

    4. SortedMap接口泛化Map接口。SortedMap集合存储key特点:无序不可重复,但会按照大小自动排序,称为可排序集合

      TreeMap类实现SortedMap接口,底层是二叉树数据结构,key可自动排序

1. Collection接口

  1. 没有使用“泛型”前,Collection可以存储Object的所有子类型;使用“泛型”后,Collection只能存储某个具体类型

  2. Collection中的常用方法:

    boolean add(Object e) 向集合中添加元素

    int size() 获取集合中元素个数

    void clear() 清空集合

Collection c = new ArrayList();		//多态
c.add(100);		//自动装箱Integer
c.add(true);
c.add(new Object());
System.out.println("集合中元素个数:" + c.size());		//元素个数为3
c.clear();		//清空集合c
System.out.println("集合中元素个数:" + c.size());		//元素个数为0

boolean contains(Object o) 判断集合中是否包含某元素

​ 底层调用equals()方法,集合中的元素要重写equals()方法

c.add("real");
c.add("heisenberg");
System.out.println(c.contains("heisenberg"));		//true
System.out.println(c.contains("burger"));		//false

//contains(Object o)方法底层调用equals()方法
Collection c = new ArrayList();
String s1 = new String("abc");
c.add(s1);
String s2 = new String("def");
c.add(s2);
String s3 = new String("abc");
System.out.println(c.contains(s3));		//s3.equals("abc")输出true

c.add(new Integer(20));
Integer i = new Integer(20);
System.out.println(c.contains(i));		//true

User u1 = new User("heisenberg");
c.add(u1);
User u2 = new User("heisenberg");
System.out.println(c.contains(u2));	//重写equals()方法前输出false,重写后为true
class User{
    private String name;
    public User(){}
    public User(String s){
        name = s;
    }
}

boolean remove(Object o) 从集合中删除某元素

​ 底层调用equals()方法

c.remove("real");
System.out.println(c.size());		//元素个数为1

Collection c1 = new ArrayList();
String s1 = new String("hello");
c1.add(s1);
String s2 = new String("hello");
c1.remove(s2);
System.out.println(c1.size());		//输出0

boolean isEmpty() 判断集合是否为空(集合中是否存在元素)

System.out.println(c.isEmpty());		//false
c.clear();
System.out.println(c.isEmpty());		//true

Object[] toArray() 把集合转换为数组(了解,使用较少)

c.add("100");
c.add("hello");
c.add(20);
Object[] obj = c.toArray();
for (int i = 0; i < obj.length; i++) {
	System.out.println(obj[i]);
}
  1. 关于Collection集合遍历/迭代(Map集合不适用)

    迭代器对象负责遍历/迭代集合中元素,初始时不指向第一个元素

    迭代器Iterator中的boolean hasNext()方法:判断是否有更多元素,返回true表示还有元素可以迭代,返回false表示没有元素可以迭代

    E next()方法:让迭代器前进一位,并且将指向的元素返回

Collection c = new ArrayList();		//有序
c.add("real");
c.add("heisenberg");
c.add(20);
c.add(new Object());
Iterator it = c.iterator();		//第一步:获取对象的迭代器对象Iterator
while (it.hasNext()){		//第二步:用迭代器对象迭代/遍历集合
	System.out.println(it.next());		//依次调用集合元素的toString()方法并输出
}

Collection c = new ArrayList();
c.add(1);
c.add(2);
c.add(3);
c.add(1);		//可重复
Iterator it = c.iterator();
while (it.hasNext()){
Object obj = it.next();
	if (obj instanceof Integer){
		System.out.println(obj);	//输出1 2 3 1,说明存进去的是Integer类型数据
	}
}

Collection c1 = new HashSet();		//无序不可重复
c1.add(10);
c1.add("heisenberg");
c1.add(20);
c1.add(10);		//输入两个10,最后只输出一个10(只存进去一个)
Iterator it1 = c1.iterator();
while (it1.hasNext()){
	System.out.println(it1.next());		//输出顺序与输入顺序不一定相同
}

集合结构改变必须重新获取迭代器(迭代器和集合状态必须一致)

迭代器的remove()方法可以同时删除迭代器和集合中元素

//以下代码报错:java.util.ConcurrentModificationException
Collection c = new ArrayList();
Iterator it = c.iterator();
c.add(1);		//创建迭代器后改变了集合结构
while (it.hasNext()){
	System.out.println(it.next());
}

//创建迭代器后使用remove()方法改变集合元素,同样报错(迭代器快照和原集合状态不同)
//调用Iterator的remove()方法则没问题(同时删除迭代器快照和原集合中的元素)
Collection c = new ArrayList();
c.add(1);
c.add(2);
c.add(3);
Iterator it = c.iterator();
while (it.hasNext()){
	Object o = it.next();
	//c.remove(o);		只在集合中删除,只输出1,后面报错
	it.remove();		//正常输出1 2 3
	System.out.println(o);
}
System.out.println(c.size());		//集合c已空
  • List接口常用方法

    void add(int index, E element)方法:在列表的指定位置插入元素,后面的元素向后移动

List mylist = new LinkedList();
mylist.add(1);
mylist.add(2);
mylist.add(3);
mylist.add(1, 20);		//将20插入下标为1的位置
Iterator it = mylist.iterator();
while (it.hasNext()){
	System.out.println(it.next());	//输出1 20 2 3
}

Object get(int index)方法:根据下标获取元素

Object firstObj = mylist.get(0);
System.out.println(firstObj);		//输出1

for (int i = 0; i < mylist.size(); i++) {		//List集合特有遍历方式
	Object o = mylist.get(i);
	System.out.println(o);
}

int indexOf(Object o)方法:获取指定对象第一次出现处的索引

System.out.println(mylist.indexOf(20));		//输出1

int lastIndexOf(Object o)方法:获取指定对象最后一次出现处的索引

mylist.add(3);
System.out.println(mylist.lastIndexOf(3));		//输出4

Object remove(int index)方法:删除指定下标位置的元素

mylist.remove(0);
System.out.println(mylist.size());

Object set(int index, Object element)方法:修改指定位置元素

mylist.set(0, "heisenberg");		//把下标为0的元素改为"heisenberg"
  • ArrayList集合
    1. ArrayList集合初始化容量是10(底层创建长度为0的数组,添加第一个元素时初始化为10)

    2. ArrayList集合底层是Object[]数组

    3. ArrayList集合的扩容:

      扩容后的容量是原容量的1.5倍(位运算右移一位)

      优化:ArrayList底层是数组,尽可能少的扩容,事先预估元素个数

    4. ArrayList集合使用最频繁(因为数组优点:查找、末尾添加元素效率高)

List list1 = new ArrayList();		//默认初始化容量10
//集合的size()方法获取的是当前集合中元素个数,不是获取集合容量
System.out.println(list1.size());	//输出0

List list2 = new ArrayList(20);		//指定初始化容量20
list2.add(20);
System.out.println(list2.size());		//输出1
  • 位运算符(>>、<<)

    二进制右移一位:>> 1 二进制右移两位:>> 2

    二进制左移一位:<< 1 二进制左移两位:<< 2

//左移一位相当于乘2,左移两位相当于乘2²……
System.out.println(10 << 1);		//输出20
//右移一位相当于除以2,右移两位相当于除以2²……
System.out.println(10 >> 1);		//输出5
System.out.println(11 >> 1);		//还是5(最后一位扔掉了)
System.out.println(15 >> 2);		//输出3
  • ArrayList集合构造方法:ArrayList(Collection c)
Collection c = new HashSet();
c.add(10);
c.add(20);
c.add(30);
c.add(40);
List list3 = new ArrayList(c);		//将HashSet集合转换为List集合
for (int i = 0; i < list3.size(); i++) {
	System.out.println(list3.get(i));
}
  • 链表数据结构
    1. 链表数据结构基本单元是节点Node
    2. 对于单向链表,任何一个节点Node都有两个属性:存储的数据和下一节点的内存地址
    3. 双向链表每个节点存储数据和上、下节点内存地址
    4. 链表优点:随机增删元素效率较高(不涉及大量元素位移)
    5. 链表缺点:查询效率低,每次查找都要从头节点开始遍历
    6. LinkedList集合没有初始化容量
  • Vector集合
    1. 底层也是数组,初始化容量为10
    2. Vector集合扩容后为原容量的两倍:10–>20–>40(ArrayList集合扩容后为原容量的1.5倍:10–>15,位运算)
  • 集合工具类java.util.Collections可以将非线程安全的ArrayList转换为线程安全的

List mylist = new ArrayList();
Collections.synchronizedList(mylist);
  • 泛型
    1. JDK5之后的新特性
    2. 泛型只在编译阶段起作用,给编译器参考
    3. 使用泛型后集合元素数据类型统一,但调用子类型特有方法时还是需要向下转换
public static void main(String[] args) {
	Cat c = new Cat();
	Bird b = new Bird();
	List list01 = new ArrayList();			//不使用泛型
	list01.add(c);
	list01.add(b);
	Iterator it = list01.iterator();
	while (it.hasNext()){
		//Animal a = it.next();		编译报错,返回值类型为Object
		Object obj = it.next();
		if (obj instanceof Animal){		//需要强制类型转换
			Animal a = (Animal)obj;
			a.move();
		}
	}

	//使用泛型List<Animal>表示List集合只允许存储Animal类型数据
	List<Animal> mylist = new ArrayList<Animal>();
	/*
	Object o = new Object();
	mylist.add(o);		编译报错
	*/
	mylist.add(c);
	mylist.add(b);
	//表示迭代器迭代的是Animal类型
	Iterator<Animal> it0 = mylist.iterator();
	while (it0.hasNext()){
		Animal a = it0.next();		//直接返回Animal类型数据,不需要类型转换
		a.move();
	}
}

class Animal{
    public void move(){
        System.out.println("动物移动");
    }
}
class Cat extends Animal{}
class Bird extends Animal{}
  • JDK8新特性:自动类型推断机制(又称为钻石表达式)
List<Animal> list = new ArrayList<>();		//省略后面<>中的内容
  • 自定义泛型
public class GenericTest03<E> {		//<>中的内容是标识符,随便写。一般用E或T
//E代表Element;T代表Type
	public void doSome(E e){
		System.out.println(e);
	}

	public static void main(String[] args) {
		GenericTest03<String> gt = new GenericTest03<>();	//指定String类型
		gt.doSome("heisenberg");
		GenericTest03<Integer> gt0 = new GenericTest03<>();	//指定Integer类型
		gt0.doSome(256);
		//不用泛型就是Object类型
	}
}
  • 增强for循环/foreach(JDK5新特性)
/*foreach语法格式:
for(元素类型 变量名 : 数组或集合){
	System.out.println(变量名);
}*/

int[] a = {1, 2, 3, 4, 5};
for (int i = 0; i < a.length; i++) {
	System.out.println(a[i]);
}
for (int data : a){		//数组使用foreach
	System.out.println(data);
}

List<String> list = new ArrayList<>();
list.add("real");
list.add("heisenberg");
//ArrayList三种遍历方式:
Iterator<String> it = list.iterator();		//1. 迭代器
while (it.hasNext()){
	System.out.println(it.next());
}

for (int i = 0; i < list.size(); i++) {		//2. get()方法+下标
	System.out.println(list.get(i));
}

for (String s : list){		//3. foreach遍历
	System.out.println(s);
}
  • Set集合
Set<Integer> set = new HashSet<>();
set.add(10);
set.add(20);
set.add(30);
set.add(20);
set.add(20);
for (Integer it : set){
	System.out.println(it);	//输出20 10 30,无序不可重复
}

2. Map接口

  1. Map和Collection没有直接继承关系
  2. Map集合以键值对方式存储数据,key和value都是引用数据类型
  3. key起主导作用,value是key的附属品
  • java.util.Map接口常用方法:

    V put(K key, V value) 向Map集合中添加键值对

Map<Integer, String> map = new HashMap<>();
map.put(1, "zhangsan");
map.put(2, "lisi");
map.put(3, "heisenberg");

V get(Object key) 通过key获取value

String value = map.get(3);
System.out.println(value);		//输出heisenberg

Collection< V > values() 获取Map集合中所有value,返回一个Collection集合

Collection<String> c = map.values();
for (String s : c){
	System.out.println(s);
}

int size() 获取Map集合中键值对个数

System.out.println(map.size());		//输出3

V remove(Object key) 通过key删除键值对

map.remove(2);
System.out.println(map.size());		//输出2

boolean containsKey(Object key) 判断Map中是否包含某个key

//Contains方法底层调用equals方法,所以自定义类型必须重写equals方法
System.out.println(map.containsKey(3));		//true
System.out.println(map.containsKey(new Integer(3)));	//true

boolean containsValue(Object vaule) 判断Map中是否包含某个value

System.out.println(map.containsValue("lisi"));		//false
System.out.println(map.containsValue(new String("lisi")));	//false

void clear() 清空Map集合

map.clear();
System.out.println(map.size());		//0

boolean isEmpty() 判断Map集合中元素个数是否为零

System.out.println(map.isEmpty());		//true

Set< K > keySet() 获取Map集合所有key,返回一个Set集合(详见遍历)

Set<Map.Entry<K, V>> entrySet() 将Map集合转换为Set集合(详见遍历)

元素类型为Map.Entry<K, V>,Map.Entry是Map中的静态内部类

public class MyClass {
    private static class InnerClass{
        public static void m1(){
            System.out.println("静态内部类m1方法执行");
        }
        public void m2(){
            System.out.println("静态内部类实例方法执行");
        }
    }
    class Test<E, T>{}
    public static void main(String[] args) {
        MyClass.InnerClass.m1();		//类名叫做MyClass.InnerClass
        MyClass.InnerClass mi = new MyClass.InnerClass();
        mi.m2();
        Set<MyClass.Test<Integer, String>> set = new HashSet<>();
    }
}
  • Map集合的遍历
Map<Integer, String> map = new HashMap<>();
map.put(1, "zhangsan");
map.put(2, "lisi");
map.put(3, "heisenberg");
//第一种方式:获取所有key,通过遍历key来遍历value
Set<Integer> keys = map.keySet();
Iterator<Integer> it = keys.iterator();
while (it.hasNext()){
	Integer key = it.next();
	String value = map.get(key);
	System.out.println(key + "=" + value);
}
//foreach
for (Integer i : map.keySet()){
	System.out.println(i + "=" + map.get(i));
}
//第二种方式:Set<Map.Entry<K, V>> entrySet()
//把Map集合转换为Set集合,Set集合元素类型为Map.Entry
Set<Map.Entry<Integer, String>> set = map.entrySet();
Iterator<Map.Entry<Integer, String>> it0 = set.iterator();
while (it0.hasNext()){
	//遍历Set集合,每次取出一个node
	Map.Entry<Integer, String> node = it0.next();
	Integer key = node.getKey();
	String value = node.getValue();
	System.out.println(key + "=" + value);
}
//foreach,这种方式效率高,适合大数据量
for (Map.Entry<Integer, String> node : set){
	System.out.println(node.getKey() + "=" + node.getValue());
}
  • HashMap集合
    1. HashMap集合底层是哈希表/散列表的数据结构
    2. 哈希表是数组和单向链表的结合体(数组查询效率高,随机增删效率低;单向链表随机增删效率高,查询效率低)
    3. HashMap集合底层源代码:
public class HashMap {
	Node<K, V>[] table;		//一维数组
	static class Node<K, V> {		//静态内部类
		final int hash;		//哈希值
        /*
        哈希值是key的hashCode()方法的执行结果,哈希值通过哈希函数/算法可以转换为数组下标
        hash值相同一定在同一个单向链表上,hash值不同也可能经过哈希算法后转换的数组下标相同(发生“哈希碰撞”)
        */
		final K key;		//存储到Map集合的key
		V value;		//存储到Map集合的value
		Node<K, V> next;		//下个节点的内存地址
	}
}
  • map.put(key, value)实现原理:

    1. 先将key,value封装到Node对象中
    2. 底层调用k的hashCode()方法得到哈希值,通过哈希函数/算法转换为数组下标。如果下标位置上没有任何元素,则把Node添加到此位置;如果下标对应位置上有链表,则用k和链表上每一个key进行equals方法比较。如果所有equals方法返回都是false,那么新节点会被添加到链表末尾;如果有一个equals方法返回true,该节点的value会被覆盖。
  • value = map.get(key)实现原理:

    先调用key的hashCode()方法得到哈希值,通过哈希算法转换为数组下标,如果该下标位置为空,返回null。如果该位置上有单向链表,则拿着key和单向链表上每个节点的key进行equals,如果所有结果为false,那么get方法返回null;只要有其中一个节点的key和参数key比较时返回true,get方法返回该节点的value值。

  • 存取都是先调用hashCode()方法再调用equals()方法(当数组下标位置是null时不会调用equals)

  • 哈希表的随机增删在链表上进行;查询只需要部分扫描,效率都很高

  • 放在HashMap集合key部分的元素和HashSet集合的元素需要重写hashCode()和equals()方法

  • HashMap集合key部分元素无序不可重复:

    无序:添加的元素不一定存放在哪个单向链表上

    不可重复:equals方法保证,如果key已经存在,原value会被覆盖

  • 如果将所有的hashCode()方法返回固定值,会导致哈希表变成单向链表,散列分布不均匀

    如果所有hashCode()方法返回值都不同,哈希表变成一维数组,散列分布不均匀

    散列分布均匀:假设有100个元素,10个单向链表,每个单向链表上有10个节点。重写hashCode()方法时要有技巧

  • HashMap集合的默认初始化容量是16,默认加载因子0.75(HashMap集合底层数组容量达到75%时开始数组扩容),HashMap集合初始化容量必须是2的倍数(为了达到散列均匀提高效率)

public class Student {
    private String name;
    //无参、有参构造,setter and getter
    //重写hashCode()和equals()方法
}

public static void main(String[] args) {
	Student s1 = new Student("zhangsan");
	Student s2 = new Student("zhangsan");
	//重写hashCode()方法前输出460141958,重写后输出-1432604525
	System.out.println(s1.hashCode());
	//重写hashCode()方法前输出1163157884,重写后输出-1432604525
	System.out.println(s2.hashCode());
	Set<Student> students = new HashSet<>();
	students.add(s1);
	students.add(s2);
	//只重写equals()方法时输出2,都重写后输出1
	System.out.println(students.size());	//hashCode()和equals()方法必须同时重写
}
  • JDK8之后,如果哈希表单向链表元素超过8个,单向链表变为红黑树。当红黑树的节点数量小于6时,会重新把红黑树变为单向链表

  • HashMap扩容:二进制左移一位(扩容后容量是原容量2倍)

  • HashMap集合key和value都可以是null,且只能有一个key为null

    Hashtable的key和value都不能是null

Map map = new HashMap();
map.put(null, null);
System.out.println(map.size());		//输出1
map.put(null, 20);
System.out.println(map.size());		//输出1,
System.out.println(map.get(null));		//输出20

Map map0 = new Hashtable();
map0.put(null, 20);		//NullPointerException
map0.put(20, null);		//NullPointerException
  • Hashtable集合
    1. Hashtable和HashMap底层都是哈希表数据结构
    2. Hashtable初始化容量11,默认加载因子0.75f
    3. Hashtable集合扩容:原容量 * 2 + 1
  • Properties类

    Properties继承Hashtable,key和value都是String类型,称为属性类对象

Properties pro = new Properties();
pro.setProperty("username", "root");
pro.setProperty("password", "123");

String username = pro.getProperty("username");
String password = pro.getProperty("password");
System.out.println(username + password);
  • TreeMap集合/TreeSet集合
    1. TreeMap底层是二叉树,放到TreeSet集合的元素等同于放到TreeMap集合的key部分
    2. TreeSet集合无序不可重复,但可以按元素大小自动排序
TreeSet<String> ts = new TreeSet<>();
ts.add("d");
ts.add("a");
ts.add("c");
ts.add("b");
for (String s : ts){
	System.out.println(s);		//输出a b c d,升序排序
}

TreeSet<Integer> ts0 = new TreeSet<>();
ts0.add(10);
ts0.add(8);
ts0.add(3);
for (Integer i : ts0){
	System.out.println(i);		//输出3 8 10,升序
}
  • 自定义类型排序方法一:实现Comparable类,重写compareTo方法
TreeSet<Person> ts1 = new TreeSet<>();
Person p1 = new Person("heisenberg");
ts1.add(p1);	//编译报错:ClassCastException 原因:Person类没有实现Comparable接口
class Person{
	private String name;
	//无参、有参构造
}

public class TreeSetTest02 {
    public static void main(String[] args) {
        TreeSet<People> ts = new TreeSet<>();
        People p1 = new People(20);
        People p2 = new People(30);
        ts.add(p1);
        ts.add(p2);
        for (People p : ts){
            System.out.println(p);
        }
    }
}

class People implements Comparable<People>{
    private Integer no;
    //无参、有参构造
    public int compareTo(People o) {		//重写compareTo方法,按数字排序
        return o.no - this.no;		//降序
        //return this.no - o.no;	升序
    }
    public String toString() {
        return "no." + no;
    }
}
  • String已经重写了compareTo方法
public class TreeSetTest03 {
    public static void main(String[] args) {
        TreeSet<Vip> vips = new TreeSet<>();
        vips.add(new Vip("abc", 20));
        vips.add(new Vip("abd", 20));
        vips.add(new Vip("xyz", 18));
        for (Vip vip : vips){
            System.out.println(vip);		//输出xyz abc abd
        }
    }
}

class Vip implements Comparable<Vip>{
    String name;
    int age;
    //有参、无参构造,toString方法
    public int compareTo(Vip o) {        //先按年龄升序,年龄相同再按姓名
        if (this.age == o.age){
            return this.name.compareTo(o.name);		//调用String类compareTo方法
        } else{
            return this.age - o.age;
        }
    }
}
  • 自定义类型排序方法二:使用比较器Comparator

    Comparator接口的设计符合OCP原则(开闭原则)

public class TreeSetTest04 {
    public static void main(String[] args) {
        TreeSet<Bat> bats = new TreeSet<>(new BatComparator());	//传比较器进去
/*或者用匿名内部类
TreeSet<Bat> bats = new TreeSet<>(new Comparator<Bat>() {
	public int compare(Bat o1, Bat o2) {
		return o1.age - o2.age;
	}
});

*/
        bats.add(new Bat(50));
        bats.add(new Bat(20));
        bats.add(new Bat(25));
        for (Bat bat : bats){
            System.out.println(bat);		//输出20 25 50
        }
    }
}

class Bat{
    int age;
    //toString方法、有参构造
}

class BatComparator implements Comparator<Bat>{		//比较器实现Comparator接口
    public int compare(Bat o1, Bat o2) {
        return o1.age - o2.age;
    }
}
  • 当比较规则不会改变或只有一个时,建议实现Comparable接口;比较规则有多个且切换频繁时,建议实现Comparator接口

  • 自平衡二叉树
    1. TreeSet/TreeMap是自平衡二叉树,遵循左小右大原则存放

    2. 二叉树前序遍历:根左右

      中序遍历:左根右

      后序遍历:左右根

      (前中后是指根的位置,左永远在右的左边)

    3. TreeSet/TreeMap采用中序遍历方式(Iterator迭代器、左根右)

  • java.util.Collections:集合工具类
public class CollectionsTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        Collections.synchronizedList(list);		//变成线程安全的
        list.add("abc");
        list.add("abb");
        list.add("aba");
        Collections.sort(list);
        for (String s : list){
            System.out.println(s);
        }

        List<Batman> bm = new ArrayList<>();
        bm.add(new Batman(666));
        bm.add(new Batman(20));
        bm.add(new Batman(25));
        Collections.sort(bm);		//对list集合元素排序,元素必须实现Comparable接口
        for (Batman b : bm){
            System.out.println(b);
        }
        
        Set<String> set = new HashSet<>();
        set.add("yxz");
        set.add("xzy");
        set.add("xyz");
        List<String> l = new ArrayList<>(set);		//Set集合排序:转换为List集合
        Collections.sort(l);
        for (String s : l){
            System.out.println(s);
        }
    }
}

class Batman implements Comparable<Batman>{
    int no;
    //toString方法、有参构造
    public int compareTo(Batman o) {
        return this.no - o.no;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值