集合:Collection、List、Set

Java集合的继承体系:
在这里插入图片描述

Collection

从体系图中可以看出,Collection集合中有两大分类,List集和Set集,它们都继承了Collection接口。

Collection的底层是 Object[] 数组,可以保存任何类型元素,在保存基本数据类型时,先把基本类型转换为对应的包装类类型, 因为包装类是Object的子类,可以进行存储。

Collection集合常用方法:

  • boolean add() : 添加元素
  • void clear() : 清空集合
  • boolean remove(): 删除某个元素
  • boolean isEmpty() :判断集合中是否为空
  • boolean contains() : 是否包含某个元素
  • int size() :返回集合中元素个数

迭代器(Iterator):

迭代器是一种模式,它可以使序列类型的数据结构的遍历行为和被遍历的对象分离,我们无需关心该序列底层数据结构,只要拿到这个对象,使用迭代器就可以遍历这个对象的内部数据

(1) 创建迭代器对象

  • Iterator it = 对象.iterator();

(2) Iterator方法

  • boolean hasNext() : 判断光标下一位是否还有元素,有就返回true,没有就返回false,生成迭代器的时候,光标不是指向第一个元素的,而是在最顶端,没有指向任何元素,光标不会自动复位,使用完之后,必须重新生成

  • next() : 把光标向下移动一位,并返回该位上的数据

  • remove():删除当前光标指向的元素

(3) 注意事项

迭代器一旦创建,如果集合中添加或者删除元素,迭代器必须重新生成,否则会报异常·java.util.ConcurrentModificationException

使用迭代器进行遍历时,如果需要删除元素,只能使用迭代器的remove方法,不能使用集合的remove方法

List

List集合存储的元素 有序,可重复。有序指得是存取顺序一致,并且有指定的下标可以表示数据的唯一性,所以可以存在重复数据。

List接口有两个实现类,ArrayList 和 LinkedList。

  • ArrayList : 底层是数组,查询效率较高,添加和删除效率较低
  • LinkedList : 底层是双向链表,查询效率较低,添加和删除效率较高

ArrayList 底层是索引数组,下标0开始,初始化容量为10 ,如果存满了,会进行扩容,容量为原始容量的1.5倍,非线程安全,效率高。

LIst 常用方法:

  • add(E e) : 将指定元素添加到尾部
  • add(int index,E e) : 将指定元素添加到指定位置,原位置内容统一向后移动
  • set(int index,E e) : 更改指定位置上的元素值
  • get(int index) : 获取指定索引上的元素
  • remove(int index) : 删除指定索引上的元素
  • remove(Object o) : 删除指定元素

LinkedList 常用方法:

  • add(E e) : 添加到尾部
  • push(E e) : 添加到头部
  • addFirst(E e) : 添加到头部
  • addLast(E e) : 添加到尾部
  • offerFirst(E e) 添加到头部,成功返回true
  • offerLast(E e) : 添加到尾部,成功返回true
  • get(int index) : 返回指定下标对应的数据(链表没有下标,只是模拟下标,方便我们查询使用)

Set

Set集合元素特点 : 无序,唯一

有 TreeSet 和 HashSet 实现类,TreeSet底层是红黑树,HashSet底层是散列表

(1) TreeSet

底层是红黑数, 元素按顺序保存

  • 数字 : 默认从小到大
  • 字符串 : 按位比较,默认比较每位ASCII码
  • 日期 : 默认比较自然日期 昨天-今天-明天
	TreeSet treeSet = new TreeSet();
	// 如果字符串有多个字符,先比较各自的第一位,谁小谁先输出
	// 如果第一位相同,再比较第二位,以此类推
	//所以先输出 101 ,后输出11
	treeSet.add("11");
	treeSet.add("101");
	for (Object object : treeSet) {
    
    
		System.out.print(object);
	}

比较器

比较器有两种 : 元素自身比较器 和 比较器类

为什么字符串,Integer,Date可以排序?

  • 因为都实现了Comparable接口

使用TreeSet在进行数据添加的时候,会自动调用该对象的compareTo()方法和集合内元素进行比较

如果我们想要存储自定义类型怎么办?

  • 实现Comparable接口,重写compareTo方法

(1)自定义数据类型,实现Comparable接口,重写compareTo方法

class User implements Comparable{
    
    
	private int age;

	public int getAge() {
    
    
		return age;
	}

	public void setAge(int age) {
    
    
		this.age = age;
	}

	public User(int age) {
    
    
		super();
		this.age = age;
	}

	@Override
	public String toString() {
    
    
		return "User [age=" + age + "]";
	}

	@Override
	public int compareTo(Object o) {
    
    
		// this 是当前对象
		// o 是集合内对象
		// 返回值为0 表示相等,不添加
		// 返回大于0 表示要添加的元素大,则放到后面
		// 返回小于0 表示要添加的元素小,则放到前面
		User user = (User) o;
		// 升序
		return this.age - user.age;
		// 降序
		// return this.age - user.age;
	}	
}

(2) 测试

public static void main(String[] args) {
    
    
	TreeSet treeSet = new TreeSet();
	
	//创建三个自定义类型对象
	User user1 = new User(11);
	User user2 = new User(13);
	User user3 = new User(6);
	
	//添加到TreeSet集合中
	treeSet.add(user1);
	treeSet.add(user2);
	treeSet.add(user3);
	
	for (Object object : treeSet) {
    
    
		System.out.println(object);
	}
}

结果:(根据重写的compareTo方法,对象按照年龄升序排列)

3
User [age=6]
User [age=11]
User [age=13]

treeSet添加的元素第二种排序方式:

使用 java.util.Comparator

  • 单独创建比较器类,实现Comparator接口,重写compare()方法
  • 传入匿名内部类

使用 Comparable 或 Comparator 进行比较的应用场景

  • 如果添加的元素的类是我们写的,我们应该使用 Comparable , 因为对扩展开发,其他人还可以使用 Comparator 实现新的排序功能
  • 如果添加的元素的类不是我们写的
  •  1 该类有排序(实现了Comparable) 比如Integer,但是默认排序结果不是我们想要的,那么我们可以使用Comparator进行调整排序,因为优先级高
    
  •  2 如果该类没有实现排序(没有实现Comparable), 这时候我们需要使用Comparator进行排序,因为我们不可能去改变人家类的源码
    

使用 Comparator 进行排序实例:
Integer 类实现了Comparable 接口,但默认排序方式是升序,我们可以使用 Comparator 进行降序排列。

public static void main(String[] args) {
    
    
	//参数传入内部类
	TreeSet treeSet = new TreeSet(new Comparator() {
    
    
		@Override
		public int compare(Object o1, Object o2) {
    
    
			Integer i1  = (Integer) o1;
			Integer i2  = (Integer) o2;
			return i2-i1;//降序
		}
	});
	//添加元素
	treeSet.add(1);
	treeSet.add(12);
	treeSet.add(11);
	treeSet.add(3);
	treeSet.add(5);
	//遍历输出
	for (Object object : treeSet) {
    
    
		System.out.println(object);
	}
}

向 ArrayList 中添加 Integer类型数据,并对其排序:

	public static void main(String[] args) {
    
    
		ArrayList arrayList = new ArrayList();
		arrayList.add(2);
		arrayList.add(22);
		arrayList.add(12);
		arrayList.add(5);
		Collections.sort(arrayList);
		System.out.println(arrayList);//[2, 5, 12, 22]
	}

因为 Integer 实现了 Comparable ,默认升序排列
改为降序:

	public static void main(String[] args) {
    
    
		ArrayList arrayList = new ArrayList();
		arrayList.add(2);
		arrayList.add(22);
		arrayList.add(12);
		arrayList.add(5);
		Collections.sort(arrayList,new Comparator() {
    
    
			@Override
			public int compare(Object o1, Object o2) {
    
    
				Integer i1  = (Integer) o1;
				Integer i2  = (Integer) o2;
				return i2-i1;
			}
		});
		System.out.println(arrayList);//[22, 12, 5, 2]

	}

散列表:

(1)散列表是一种数据结构,不过java中屏蔽了,直接以封装的形式封装到了HashSet, HashMap和HashTable中,其中hashTable已经过时

(2)结构:
数组中保存链表(单向链表),并且链表节点内有四个属性

  • key
  • value:自身保存的数据
  • next :下一个节点的地址
  • hash

HashSet 和 HashMap

  • HashSet 就是HashMap的封装,本质就是一个HashMap
  • 默认初始化容量都是 16
  • 封装之后HashSet把value值屏蔽了,只能操作key,所以在使用set添加的时候,只需要传入key即可

散列表需要使用hashCode()和equals()来表示对象的唯一性
添加过程:

  • 使用添加的键值对中的key,调用key的hashCode方法,生成hash值,进行hash算法得到数组下标
  • 判断该下标上是否有元素,如果没有,把该键值对保存到该数组中即可
  • 如果该数组中有对象,则调用key的equals方法和数组中的元素进行比较,如果相等则key不添加,value值覆盖
  • 如果不相等,就把该对象添加到已有元素的next属性,形成链表
  • 如果添加的时候,就已经是链表了,则需要用key和链表中所有的元素key进行比较是否相等

猜你喜欢

转载自blog.csdn.net/qq_41504815/article/details/113002267