【Java集合】逻辑结构超强、表达十分清晰的“Collection接口”及“List接口、Set接口”解析

本文深入解析Java集合框架,涵盖Collection、List、Set接口及其主要实现类,如ArrayList、LinkedList、HashSet、TreeSet等。详细讲解了各集合的特性、使用场景及核心方法,适合Java初学者和进阶者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如标题,你将获得Collection、List、Set接口,以及其实现类ArrayList,LinkedList,HashSet、TreeSet类的讲解。众所周知,Java的学习就是从API里有啥、是啥、怎么用开始,再进阶至源码。

注意:

  • 文章较长,但非常基础和实用。
  • 一句废话也没有,再说我也不喜欢搞那些花里胡哨的。
  • 这是我学习笔记,其中一定有一部分是你能够拿去做成你的笔记的。
  • enjoy…

集合体系

在这里插入图片描述


诞生背景

为啥要诞生集合,还得从数组的缺陷说起。

数组(存储同一种数据类型的集合容器)的特点:

  1. 只能存储同一种数据类型的数据,例如在同一个容器中不能同时存储int型和double型。
  2. 一旦初始化,容量固定,容量不能扩容或缩减。
  3. 数组中的元素与元素之间的内存地址是连续的。

注意: Object类型的数组可以存储任意类型的数据。

Object[] arr = new Object[10];
arr[1] = "abc";
arr[2] = 'a';
arr[3] = 12;

基于数组的仅能存储同一种类型和容量不可变化的劣势下,诞生的集合!

集合的定义: 是存储任何对象数据的集合容器。在同一个容器中,可以同时存储int型和double型,甚至是自定义类型。

//试一试
import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        ArrayList array = new ArrayList();
        array.add("我是字符串");
        array.add(134);
        array.add(3.14534);

        for(int i=0; i<array.size(); i++) {
            System.out.println(array.get(i));
        }
    }
}
/*
输出:
我是字符串
134
3.14534

 */

集合比数组的优势:
1. 集合可以存储任意类型的对象数据,数组只能存储同一种数据类型的数据。
2. 集合的长度是允许发生变化的,数组的长度是固定的。


集合(Collection)接口

Collection的体系结构:
在这里插入图片描述

方法

看别人的技术文章和看API(源码)的根本区别就是,技术文章会把最重要、最核心的方法先罗列出来,还会把方法进行分门别类,这样学习才能事半功倍!

方法:(增、删、查、判断、遍历/迭代)
增加

  • boolean add(E e) 添加成功返回true,添加失败返回false.
  • boolean addAll(Collection c) 把一个集合的元素添加到另外一个集合中去。

删除

  • void clear() //移除所有元素
  • boolean remove(Object o) //指定集合中的元素删除,删除成功返回true,删除失败返回false.
  • boolean removeAll(Collection c) //删除交集元素
  • boolean retainAll(Collection c) //保留交集元素,其他元素删除;retain[保持、记住]

查看

  • int size()` //返回元素的个数

判断:Boolean类型;

  • boolean isEmpty()
  • boolean contains(Object o) //判断集合中是否存在参数的元素;
  • boolean containsAll(Collection c) //判断调用集合是否包含参数集合的全部元素,即判断集合是否完全相等;
//试一试
import java.util.ArrayList;
import java.util.Collection;

public class Main {
    public static void main(String[] args) {
        Collection col = new ArrayList();

        col.add("por*hub"); //增
        col.add("xvideo*");

        col.remove("xvideo*"); //删

        System.out.println("数量:"+col.size()); //查
        System.out.println("含por*hub?"+col.contains("por*hub"));

        System.out.println("空?"+col.isEmpty());//判断
    }
}
/*
输出:
  数量:1
  含por*hub?true
  空?false
*/

迭代(遍历方法)

  • Object[] toArray() //返回一个Object的数组,把集合中的元素全部存储到一个Object的数组中返回;
  • Iterator<E> iterator() //迭代器遍历集合中的元素
  • 增强for循环、forEach:始于JDK1.8(Lambda表达式)

两种迭代方法示例:

//迭代:toArray()方法
import java.util.ArrayList;
import java.util.Collection;

public class Main {
	public static void main(String[] args) {
		Collection<String> coll = new ArrayList<String>();
		coll.add("abc1");
		coll.add("abc2");
		coll.add("abc3");
		coll.add("abc4");
		
		Object[] arr = coll.toArray(); //将ArrayList集合转换为Object数组
		
		for(Object i:arr) { //遍历Object数组
			System.out.println(i);
		}
	}
}
//迭代:迭代器方法
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class Main {
	public static void main(String[] args) {
		Collection<String> coll = new ArrayList<String>();
		coll.add("abc1");
		coll.add("abc2");
		coll.add("abc3");
		coll.add("abc4");

		// 获取迭代器对象
		Iterator it = coll.iterator();
		while (it.hasNext()) {
			System.out.println(it.next());
		}

	}
}
//增强for
import java.util.ArrayList;
import java.util.Collection;

public class Main {
    public static void main(String[] args) {
        Collection<String> coll = new ArrayList<String>();
        coll.add("abc1");
        coll.add("abc2");
        coll.add("abc3");
        coll.add("abc4");

        //增强for
        for(String ele:coll) {
            System.out.println(ele);
        }

        //forEach
        coll.forEach(ele -> System.out.println(ele));
    }
}

迭代器(Iterator)接口

迭代器诞生的背景:

  1. java中提供了很多个集合,它们在存储元素时,采用的存储方式不同。
  2. 对于众多的集合,取出集合中的元素,每个集合都需要不同的操作。
  3. 由此就会有很多的取出集合操作的类。
  4. 为了减少操作类,Java使用一个专业术语“迭代器iterator”来统称所有集合的取出操作。

迭代器功能:
取出任意种类的集合中的元素。

方法概览:

  1. 判定集合中是否还有元素可以迭代:boolean hasNext();
  2. 返回迭代的下一个元素:E next();
  3. 移除迭代器最后一次返回的元素,即删除该指针现在指向元素:void remove();

迭代器的应用的底层原理:

  1. 因为Iterator是接口,就需要找实现类。
  2. 在Collection接口中,有一个方法:Iterator iterator();
  3. 在Collection接口中的实现类中(如ArrayList、HashSet),都会重写iterator方法,返回Iterator接口的实现类对象。
  4. 在具体集合遍历时,就会调用相应的方法。

快速理解迭代器的使用:

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class Main {
	public static void main(String[] args) {
		Collection<String> coll = new ArrayList<String>();
		coll.add("abc1");
		coll.add("abc2");
		coll.add("abc3");
		coll.add("abc4");
		//迭代器,对集合ArrayList中的元素进行取出
		
		//调用集合的方法iterator()获取出,Iterator接口的实现类的对象
		Iterator<String> it = coll.iterator();
		//接口实现类对象,调用方法hasNext()判断集合中是否有元素,集合中没元素, hasNext()返回了false
		//接口的实现类对象,调用方法next()取出集合中的元素
		while(it.hasNext()){
			String s = it.next();
			System.out.println(s);
		}
	}
}

List接口

实现类:

  1. ArrayList
  2. LinkedList
  3. Vector(已淘汰)

特点:

  1. 有序(即添加的顺序与出来的顺序一致);
  2. 索引;
  3. 可重复;

(除了Collection)特有方法:(增、删、获取、改、迭代)

添加

  • boolean add(int index, E element) //把元素添加到指定的索引值里
  • void add(int index, E element) //把元素插入指定位置
  • boolean addAll(int index, Collection<? extends E> c) //把指定的集合添加到指定的索引里;

删除

  • E remove(int index) //移除指定索引上的元素,返回被删除的元素

获取:

  • E get(int index) //根据索引值,返回值
  • int indexOf(Object o) //找元素第一次出现的索引值
  • int lastIndexOf(Object o) //找元素最后一次出现的索引值
  • E subList(int fromIndex, int toIndex) //截取元素:从fromIndex到toIndex【注意:顾头不顾尾】;

修改:

  • E set(int index, E element) //使用指定的元素替换该索引值的内容,返回修改之前的元素

总结:

  • List接口中的特有方法特点:操作的方法都与索引值有关。
  • 只有List接口下的集合类才具备索引值的方法,其他的都没有!
  • 因为List的本质是Object数组;

List接口专属迭代器:ListIterator

特性:

  1. ListIterator的超接口是Iterator;并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引,等等。
  2. ListIterator只能用来遍历List,不能遍历其他。
  3. ListIterator既可以前向也可以后向遍历,而Iterator只能是前向遍历。因为List接口的本质是个双向链表。
  4. 可以一边遍历、一边修改List元素。

方法:(ListIterator接口除了Iterator接口的额外方法有)

增加操作:void add(E e);
删除操作:void remove();
修改操作:void set(E e);
取出操作(查询)

  1. boolean hasNext() 向前判断是否有元素
  2. E next() 先取出当前指针指向的元素,然后指针向下移动一个单位。
  3. int nextIndex() 返回调用next()的元素索引
  4. boolean hasPrevious() 判断是否存在上一个元素。
  5. E previous() 当前指针先向上移动一个单位,然后再取出当前指针指向的元素。
//实例
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

public class Main {
    public static void main(String[] args) {
        List col = new ArrayList();
        col.add("thepor*tube com");
        col.add("por**hub com");
        col.add("javbus com");
        col.add("3atv com");

        ListIterator it = col.listIterator(); //获取到List的迭代器
        System.out.println("向前迭代:");
        while(it.hasNext()) {
            System.out.println(it.next());
        }
        System.out.println("向后迭代:");
        while(it.hasPrevious()) {
            System.out.println(it.previous());
        }
    }
}
/*
输出:
    向前迭代:
    thepor*tube com
    por**hub com
    javbus com
    3atv com
    向后迭代:
    3atv com
    javbus com
    por**hub com
    thepor*tube com
*/

使用迭代器的注意事项

  1. 在迭代过程使用add函数,它不会立即添加到本个循环的指针所在位置;而是等待本个迭代器完成后再添加入;
  2. 在迭代器迭代元素过程中,不允许使用集合对象的方法改变集合中元素个数;
  3. 只有在迭代器方法使用完毕后,才能使用集合的操作方法,即在使用迭代器进行迭代的时候,不能改变集合的元素个数;

在迭代器里next()和add():

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

public class Main {
	public static void main(String[] args) {
		List list = new ArrayList();
		
		list.add("AA");
		list.add("BB");
		list.add("CC");
		
		ListIterator it = list.listIterator();	//获取到迭代器
		while(it.hasNext()){
			System.out.print(it.next()+",");
			it.add("additional"); // 把元素添加到当前指针指向位置;它不会立即添加到本个循环的指针所在位置;而是等待本个迭代器完成后再添加入;
		}
		System.out.print("\n集合list的内容是:"+list);
	}
}
/* 输出:
	AA,BB,CC,
	集合list的内容是:[AA, additional, BB, additional, CC, additional]
*/

迭代时不能改变个数:

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

public class Main {
	public static void main(String[] args) {
		List list = new ArrayList();

		list.add("AA");
		list.add("BB");
		list.add("CC");

		ListIterator it = list.listIterator(); // 获取到迭代器
		while (it.hasNext()) {
			System.out.println(it.next());
			/*
			 如果使用集合对象的方法操作集合元素个数,则有异常抛出: ConcurrentModificationException
			list.add("aa"); // add方法是把元素添加到集合的末尾处的。相当于改变集合个数
			list.remove("BB"); //remove方法是删除集合中的指定元素。相当于改变集合个数
			*/
		}
	}
}
/* 输出:
	AA
	BB
	CC
 */

只有在迭代器使用完毕后,才能改变集合个数:

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

public class Main {
	public static void main(String[] args) {
		List list = new ArrayList();
		
		list.add("AA");
		list.add("BB");
		list.add("CC");
		
		ListIterator it = list.listIterator();	//获取到迭代器
		while(it.hasNext()){
			System.out.print(it.next()+",");
		}
		
		list.add("aa"); //改变了集合元素个数的后面不能出现迭代器的使用,只能在迭代器使用完毕后才能使用集合对象方法来操作集合
		//it.next(); 异常抛出!
		
		System.out.print("\n集合中的元素"+list);
	}
}
/* 输出:
	AA,BB,CC,
	集合中的元素[AA, BB, CC, aa]
 */

几种遍历方式

三种遍历集合的方式:
第一种: 使用get方法遍历。
第二种: 使用迭代器正序遍历。
第三种: 使用迭代器逆序遍历。

import java.util.ArrayList;
import java.util.Collection;
import java.util.ListIterator;
import java.util.List;

public class TestMain {

	public static void main(String[] args) {
		List list = new ArrayList();
		
		list.add("AA");
		list.add("BB");
		list.add("CC");
		
		//get遍历
		for(int i=0;i<list.size();i++) {
			System.out.println(list.get(i));
		}
		
		//迭代器正序遍历
		ListIterator it = list.listIterator();
		while(it.hasNext()) {
			System.out.println(it.next());
		}
		
		//迭代器逆序遍历
		while(it.hasPrevious()) {
			System.out.println(it.previous());
		}
	}
}

再次提醒:只能在List接口的实例中使用ListIterator!!!

ArrayList类

学习目标:
由于该类的绝大部分方法都是其父接口中继承而来,故需要学习的是该类的实现原理与特点!

实现原理:
底层是维护一个Object数组来实现;

使用指导: (什么时候使用ArrayList?)
如果目前的数据是查询比较多,增删比较少的时候,那么就使用ArrayList存储这批数据。 比如:高校的图书馆。

特点:

  1. 查询速度快(像数组一样以连续的内存空间存储元素),增删慢(检查容量、拓展容量、把旧数组的内容拷贝到新数组中,见ArrayList源代码中的具体方法)
  2. ArrayList底层维护了一个Object数组实现 的,使用无参构造函数时,Object数组默认的容量是10,当长度不够时,自动增长0.5倍。
  3. 为追求效率,ArrayList 没有实现同步(synchronized),如果需要多个线程并发访问,用户可以手动同步,也可使用 Vector 替代。

特有方法:

  • void ensureCapacity(int minCapacity) //自己制定ArrayList数组的初始容量,亦可用构造方法来实现;
  • trimToSize() //为当前ArrayList实例根据已经存储的内容个数量调整容量

核心方法

增:

  • public boolean add(E e):添加元素
  • public void add(int index, E element):在指定的索引处添加一个元素

删:

  • public boolean remove(Object o):删除指定的元素,返回删除是否成功
  • public E remove(int index):删除指定索引处的元素,返回被删除的元素

改:

  • public E set(int index,E element):修改指定索引处的元素,返回被修改的元素
    查:
  • public E get(int index):返回指定索引处的元素
  • public int size():返回集合中的元素的个数
增方法详解
import java.util.ArrayList;

public class Main {
	public static void main(String[] args) {
		ArrayList<String> array = new ArrayList<String>();
		array.add("hello");
		array.add("kyle");
		array.add("Java");
		
		// add(int index,E element):在指定的索引处添加一个元素
		array.add(1, "android");
		System.out.println("ArrayList目前的状态是:" + array); //ArrayList目前的状态是:[hello, android, kyle, Java]
	}
}
删方法详解
import java.util.ArrayList;

public class Main {
	public static void main(String[] args) {
		ArrayList<String> array = new ArrayList<String>();
		array.add("hello");
		array.add("kyle");
		array.add("Java");
		
		array.remove("hello"); //根据元素名删除
		System.out.println("删除某元素后ArrayList目前的状态是:" + array); //删除某元素后ArrayList目前的状态是:[kyle, Java]

		String s = array.remove(0); //根据下标删除
		System.out.println("删除第一个元素后的ArrayList目前的状态是:" + array); //删除第一个元素后的ArrayList目前的状态是:[Java]
	}
}
改方法详解
import java.util.ArrayList;

public class Main {
	public static void main(String[] args) {
		ArrayList<String> array = new ArrayList<String>();
		array.add("hello");
		array.add("kyle");
		array.add("Java");
		
		array.set(0, "olleh");
		System.out.println("ArrayList目前的状态是:" + array); //输出:ArrayList目前的状态是:[olleh, kyle, Java]
	}
}
查方法详解
import java.util.ArrayList;

public class Main {
	public static void main(String[] args) {
		ArrayList<String> array = new ArrayList<String>();
		array.add("hello");
		array.add("kyle");
		array.add("Java");
		
		//public E get(int index):返回指定索引处的元素
		System.out.println("获取的元素是:" + array.get(0));

		//public int size():返回集合中的元素的个数
		System.out.println("元素的个数:" + array.size());
	}
}

遍历方式

遍历方法:

  1. 类似数组的遍历方法,通过size()和get()配合实现的
  2. 通过迭代器遍历
  3. 增强for循环
  4. forEach,源于JDK1.8的Lambda表达式
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

public class Main {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        list.add("hello");
        list.add("world");
        list.add("java");

        //索引方式
        for(int i=0; i<list.size();i++) {
            System.out.print(list.get(i) + " ");
        }
        System.out.println();

        //Collection迭代器
        Iterator<String> it = list.iterator();
        while(it.hasNext()) {
            System.out.print(it.next() + " ");
        }
        System.out.println();

        //List专属迭代器
        ListIterator li = list.listIterator();
        while(li.hasNext()) {
            System.out.print(li.next() + " ");
        }
        System.out.println();
        while(li.hasPrevious()) {
            System.out.print(li.previous() + " ");
        }
        System.out.println();

        //增强for
        for(String ele:list) {
            System.out.print(ele + " ");
        }
        System.out.println();

        //forEach
        list.forEach(ele -> System.out.print(ele + " "));
    }
}

LinkedList类

特点:

  1. 链表实现;
  2. 增删快;
  3. 查找慢

实现原理:
LinkedList 同时实现了 List 接口和 Deque 接口,也就是说它既可以看作一个顺序容器,又可以看作一个队列(Queue),同时又可以看作一个栈(Stack)。

底层原理:

  • 由于LinkedList在内存中的地址不连续,需要让上一个元素记住下一个元素。所以每个元素中保存的有下一个元素的位置。虽然也有角标,但是查找的时候,需要从头往下找,显然是没有数组查找快的。但是,链表在插入新元素的时候,只需要让前一个元素记住新元素,让新元素记住下一个元素就可以了。所以插入很快。
  • 由于链表实现, 增加时只要让前一个元素记住自己就可以,删除时让前一个元素记住后一个元素,后一个元素记住前一个元素。这样的增删效率较高。

核心方法

增:

  • addFirst(E e) //把元素添加到集合的首位置上。
  • addLast(E e) //把元素添加到集合的末尾处。

删:

  • removeFirst() //删除集合中的首位置元素,并返回
  • removeLast() //

查:

  • getFirst() //获取集合中的首位置元素
  • getLast() //获取集合中末尾的元素

方法的使用方式类似于ArrayList,遍历的方式和ArrayList一样!
由于Vector已经被时间所淘汰,这里将不再详解。待后面Java集合进阶(研究源码和线程安全)时再做分析!


Set接口

Set的特点:

  1. 无序(存储和读取的顺序可能不一样)
  2. 不允许重复,仅针对于系统提供的数据类型;如果是自定义的类型/类,则允许重复
  3. 没有整数索引

方法

从其父接口(Collection)那里继承了绝大部分方法;

增:

  1. boolean add(E e);
  2. boolean addAll(Collection<? extends E> c);

删:

  1. void clear();
  2. boolean remove(Object obj);
  3. boolean reomveAll(Collection<?> c);

迭代:

  1. Object[] toArray();
  2. Iterator iterator();

查:

  1. int size();
  2. boolean isEmpty();
  3. boolean contains(Collection c);

特有方法(相对于List接口):

  1. boolean equals(Object o);
  2. int hashCode();
//试一试
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class Main {
    public static void main(String[] args) {
        Set ss = new HashSet();
        ss.add("Aaa"); //增
        ss.add("Bbb");
        ss.add(1234);
        ss.add(8848);
        ss.add(3.1415926);

        ss.remove(8848); //删

        System.out.println("长度:"+ss.size()); //获取
        System.out.println("空?"+ss.isEmpty());
        System.out.println("含1234?"+ss.contains(1234));

        //迭代
        System.out.println("内容迭代:");
        Iterator it = ss.iterator();
        while(it.hasNext()) {
            System.out.print(it.next() + " ");
        }
    }
}
/*
输出:
    长度:4
    空?false
    含1234?true
    内容迭代:
    Aaa 1234 Bbb 3.1415926 
 */

遍历迭代

Set接口的三种遍历方式:

  1. 转换为Object;
  2. 迭代器
  3. 增强for循环
遍历Set
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class Main {
	public static void main(String[] args) {
		//创建集合对象
		//HashSet<String> hs = new HashSet<String>();
		Set<String> set = new HashSet<String>(); //父接口引用指向子类对象,无法使用子类对象中特有的成员
		
		//添加元素
		set.add("hello"); //实现了Collection接口的add方法
		set.add("world");
		set.add("java");
		
		//遍历集合的三种方式
		//第一种:转Object数组
		Object[] objs = set.toArray();
		for(int i=0;i<objs.length;i++) {
			System.out.println(objs[i]);
		}
		
		//第二种:迭代器
		Iterator<String> it = set.iterator();
		while(it.hasNext()) {
			String s = it.next();
			System.out.println(s);
		}
		
		//第三种:增强for
		for(String s:set) {
			System.out.println(s);
		}
	}
}

HashSet类

HashSet的核心功能就是自动去重,保证整个容器中的每个元素都是唯一的。
在这里插入图片描述

特性:

  1. 底层的数据结构是哈希表(hash table);
  2. 存储,取出的速度都比较快;
  3. 线程不安全(也叫实现不同步);
//第一个HashSet程序
import java.util.Set;
import java.util.HashSet;

public class Main {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        set.add("前田可奈子");
        set.add("桃乃木香奈");
        set.add("深田咏美");
        System.out.println(set); //输出:[桃乃木香奈, 深田咏美, 前田可奈子]
    }
}

Set去重复的原理?

  • 对于基本数据类型,直接判断值大小去重复,并不需要开发者过多干预;
  • 对于引用类型,通过比较哈希值(hashCode)进行比较(equals),所以需要重写hashCode()和equals方法;

HashSet如何检查重复(add方法的执行流程):

  1. HashSet的add()方法,首先会使用当前集合中的每一个元素和新添加的元素进行hash值比较,
  2. 如果hash值不一样,则直接添加新的元素
  3. 如果hash值一样,比较地址值或者使用equals方法进行比较
  4. 比较结果一样,则认为是重复不添加
  5. 所有的比较结果都不一样则添加

为什么要调用equals方法?

  1. hashCode()与equals()的相关规定:
    a) 如果两个对象相等,则hashcode一定也是相同的
    b) 两个对象相等,对两个equals方法返回true
    c) 两个对象有相同的hashcode值,它们也不一定是相等的
  2. 综上,equals方法被覆盖过,则hashCode方法也必须被覆盖。hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)。
//HashSet保证元素唯一,自动去重
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class Main {
    public static void main(String[] args) {
        Set ss = new HashSet();
        ss.add("cc");
        ss.add("oo");
        ss.add("bd");
        ss.add("ba");
        ss.add("ba");
        ss.add("oo");
        ss.add("cc");

        Iterator it = ss.iterator();
        while(it.hasNext()) {
            System.out.print(it.next() + " ");
        }
    }
}
//输出:cc oo bd ba

无序的原理

Set容器的元素无序的根本原因是底层采用了哈希表存储元素。

  • JDK 1.8之前:哈希表 = 数组 + 链表 + (哈希算法)
  • JDK 1.8及之后:哈希表 = 数组 + 链表 + 红黑树(据哈希值排序) + (哈希算法)
    在这里插入图片描述

自定义对象如何实现自动去重、保证唯一?

HashSet集合添加的元素是不重复的,是如何去重复的?

  • 对于基本数据类型,直接判断值大小去重复,并不需要开发者过多干预;
  • 对于引用类型,通过比较哈希值(hashCode)进行比较(equals),所以需要重写hashCode()和equals方法;
//自定义类在Set中去重
import java.util.Set;
import java.util.HashSet;
import java.util.Objects;

public class Main {
    public static void main(String[] args) {
        Set<Apple> apples = new HashSet<>();
        Apple a1 = new Apple("红富士",59.9 ,"红色");
        Apple a2 = new Apple("阿克苏",39.9 ,"青红色");
        Apple a3 = new Apple("阿克苏",39.9 ,"青红色");
        apples.add(a1);
        apples.add(a2);
        apples.add(a3);
        System.out.println(apples);
    }
}

class Apple {
    private String name;
    private double price;
    private String color;

    public Apple() {
    }

    public Apple(String name, double price, String color) {
        this.name = name;
        this.price = price;
        this.color = color;
    }

    //IDEA自动重写出的equals、hashCode方法
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Apple apple = (Apple) o;
        return Double.compare(apple.price, price) == 0 && Objects.equals(name, apple.name) && Objects.equals(color, apple.color);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, price, color);
    }

    @Override
    public String toString() {
        return "Apple{" +
                "name='" + name + '\'' +
                ", price=" + price +
                ", color='" + color + '\'' +
                '}';
    }
}

LinkedHashSet类

LinkedHashSet架构:
在这里插入图片描述
特性:

  1. 基于链表的哈希表实现;

  2. 继承自HashSet;

  3. 维护着一个双重链表;

  4. 具有顺序,存储和取出的顺序相同

  5. 不允许有重复的元素

  6. 线程不安全,运行速度快


TreeSet类

特性:

  • 不重复,无索引,按照大小默认升序排序

排序特性:

  • 元素是基本数据类型:直接按大小,进行升序排序;进行排序的底层实现是:红-黑树;
  • 元素是字符串类型:按照字典顺序(即ASCII码表)逐个排序;进行排序的底层实现是:红-黑树;
  • 元素是自定义类:TreeSet无法进行默认排序,需要定义排序规则;

TreeSet类的架构:
在这里插入图片描述

//第一个TreeSet程序
import java.util.Set;
import java.util.TreeSet;

public class Main {
    public static void main(String[] args) {
        Set<Double> set01 = new TreeSet<Double>();
        set01.add(3.14159);
        set01.add(0.618);
        set01.add(8488.888);
        System.out.println(set01); //输出:[0.618, 3.14159, 8488.888]

        Set<String> set = new TreeSet<>();
        set.add("China");
        set.add("china");
        set.add("USA");
        System.out.println(set); //输出:[China, USA, china]
    }
}

自动排序的核心原理

自定义类设置自动排序规则:

  • 方案一:直接在类上实现Comparable接口,重写比较方法;优先级较高。
  • 方案二:写一个专门的比较器类(实现Comparator接口的类,并重写比较方法),需要比较时将比较器传入;
//Comparable实例
import java.util.Set;
import java.util.TreeSet;

public class Main {
    public static void main(String[] args) {
        Set<Employee> set01 = new TreeSet<>();
        set01.add(new Employee("前田可奈子",22,8000.00D));
        set01.add(new Employee("神田咏梅",28,9000.3D));
        set01.add(new Employee("桃乃木香奈", 25,9999.3D));
        System.out.println(set01);
    }
}
//输出:[Employee{name='前田可奈子', age=22, salary=8000.0}, Employee{name='桃乃木香奈', age=25, salary=9999.3}, Employee{name='神田咏梅', age=28, salary=9000.3}]

class Employee implements Comparable<Employee> {
    private String name;
    private Integer age;
    private Double salary;

    public Employee(String name, Integer age, Double salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    @Override
    public int compareTo(Employee o) {
        return this.age - o.age; //升序
        //return 0.age - this.age; //降序
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                '}';
    }
}
//Comparator实例
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

public class Main {
    public static void main(String[] args) {
        EmployeeComparator employeeComparator = new EmployeeComparator();
        Set<Employee> set01 = new TreeSet<>(employeeComparator); //传入比较器
        set01.add(new Employee("前田可奈子",22,8000.00D));
        set01.add(new Employee("神田咏梅",28,9000.3D));
        set01.add(new Employee("桃乃木香奈", 25,9999.3D));
        System.out.println(set01);
    }
}
//输出:[Employee{name='前田可奈子', age=22, salary=8000.0}, Employee{name='神田咏梅', age=28, salary=9000.3}, Employee{name='桃乃木香奈', age=25, salary=9999.3}]

class Employee {
    private String name;
    private Integer age;
    private Double salary;

    public Employee(String name, Integer age, Double salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    public Double getSalary() {
        return salary;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                '}';
    }
}

class EmployeeComparator implements Comparator<Employee> {
    @Override
    public int compare(Employee obj01, Employee obj02) {
        //return (int)(obj01.getSalary() - obj02.getSalary());当两数相减值小于1,则始终返回0
        return Double.compare(obj01.getSalary(), obj02.getSalary());
     }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值