Java基础学习22Set集合

Java基础学习22Set集合

「这是我参与11月更文挑战的第22天,活动详情查看:2021最后一次更文挑战」。

关于作者

  • 作者介绍

🍓 博客主页:作者主页
🍓 简介:JAVA领域优质创作者🥇、一名在校大三学生🎓、在校期间参加各种省赛、国赛,斩获一系列荣誉🏆
🍓 关注我:关注我学习资料、文档下载统统都有,每日定时更新文章,励志做一名JAVA资深程序猿👨‍💻


Set接口简介

Set接口与List接口最大的区别就是内容是不允许重复的,通知Set和List最大的区别还有一个就是

set接口没有对Collection接口进行扩充,而List对Collection接口进行了扩充。由于jdk1.8的原因,所以在collection接口也提供有一些default方法,而这写方法并没有在Set接口里面出现。也就是说set接口里面不可能使用get()方法进行处理,而在Set子接口里面有我们常用的子类HashSet、TreeSet。

image-20210830135859831

散列存放的子类:HashSet

Hash(哈希)属于一种算法,这种算法的核心意义指的是找空保存算法,所以只要一看见Hash就是说没有顺序的保存。

观察Set接口的使用

package com.day17.demo;

import java.util.HashSet;
import java.util.Set;

public class HashSetDemo {
	public static void main(String[] args) {
		Set<String> all = new HashSet<>();
		all.add("Hello");
		all.add("zsr");
        all.add("zsr");
		all.add("Abc");
		System.out.println(all);
	}
}
复制代码

保存数据在输出之后发现,重复的数据没有了,并且其本身的保存也是没有任何顺序的。

排序存放的子类:TreeSet

如果现在希望Set集合之中保存的数据有顺序,那么就通过TreeSet进行Set接口的实例化。

使用TreeSet实例化接口

package com.day17.demo;

import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;

public class HashSetDemo {
	public static void main(String[] args) {
		Set<String> all = new TreeSet<>();
		all.add("C");
		all.add("C");
		all.add("A");
		all.add("B");
		all.add("D");
		System.out.println(all);
	}
}
复制代码

现在发现所有的保存的数据没有重复且有顺序。TreeSet使用的是一个升序排列的模式完成的。

关于TreeSet排序的说明

通过之前的程序可以发现,使用TreeSet实例化Set接口之中,所有保存的数据都是有序的,那么在这种情况下,那么如果说使用一个自定义的类呢?

那么这个时候如果这个类的对象要进行排序的话,则这个类必须实现Comparable接口,设置比较规则,但是在这种情况下有一点必须注意:一旦使用了Comparable的话,类之中所有的属性都必须写进排序规则。

自定义排序

package com.day17.demo;

import java.util.Set;
import java.util.TreeSet;
class Personn implements Comparable<Personn>{
	private String name;
	private Integer age;
	
	public Personn(String name, Integer age) {
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "person [name=" + this.name + ", age=" + this.age + "]\n";
	}
	@Override
	public int compareTo(Personn o) {
		// TODO Auto-generated method stub
		if(this.age > o.age){
			return 1;
		}else if (this.age < o.age){
			return 0;
		}else{
			return this.name.compareTo(o.name);
		}
		
	}
	
}
public class TreeSetDemo {
	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		Set<Personn> all=new TreeSet<Personn>();
		all.add(new Personn("张三",20));
		all.add(new Personn("张三",20));
		all.add(new Personn("李四",20));
		all.add(new Personn("王五",30));
		all.add(new Personn("赵六",40));
		System.out.println(all);
	}
}
复制代码

因为在实际开发之中TreeSet的使用实在是过于麻烦了,在项目开发的简单java类是根据数据库表的设计而来的,如果一张数据表的字段过多,你这个类得写死。

image-20210830180601214

TreeSet子类依靠Compara()方法的返回值是否为0来判断是否为重复元素.

关于重复元素的说明

在使用TreeSet子类进行数据保存的时候,重复元素的判断依靠的是Comparable接口完成的。这并不是全部Set接口判断重复元素的方式,因为如果使用的是HashSet子类,由于其Comparable没有任何关系,所以他判断重复重复元素的主要依靠的是两个方法:

  • hash码:public int hashCode();
  • 对象比较:public Boolean equals(Object obj)

在进行对象比较的过程之中,首先会使用hashCode()与保存在集合之中的对象的hashCode()进行比较,如果代码相同,则再使用equals()方法进行内容的比较,如果全部相同,则为相同元素。

package com.day17.demo;

import java.util.Set;
import java.util.TreeSet;
class Personn implements Comparable<Personn>{
	private String name;
	private Integer age;
	
	public Personn(String name, Integer age) {
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((age == null) ? 0 : age.hashCode());
		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;
		Personn other = (Personn) obj;
		if (age == null) {
			if (other.age != null)
				return false;
		} else if (!age.equals(other.age))
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
	@Override
	public String toString() {
		return "person [name=" + this.name + ", age=" + this.age + "]\n";
	}
	@Override
	public int compareTo(Personn o) {
		// TODO Auto-generated method stub
		if(this.age > o.age){
			return 1;
		}else if (this.age < o.age){
			return 0;
		}else{
			return this.name.compareTo(o.name);
		}
		
	}
	
}
public class TreeSetDemo {
	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		Set<Personn> all=new TreeSet<Personn>();
		all.add(new Personn("张三",20));
		all.add(new Personn("张三",20));
		all.add(new Personn("李四",20));
		all.add(new Personn("王五",30));
		all.add(new Personn("赵六",40));
		System.out.println(all);
	}
}
复制代码

​ 如果要想标识出对象的唯一性,一定需要hashCode()、equals()共同作用。

面试题:如果两个hashCode()相同、equals()不同结果是什么?不能消除

面试题:如果两个hashCode()不相同、equals()相同结果是什么?不能消除

​ 对象判断必须两个都要实现。

image-20210830200214061

集合的输出操作

在之前所介绍的都属于单值集合的基本操作,可是对于集合有一个最重要的问题就是如何进行集合内容的输出操作,而这个问题在Java的类集框架之中给出了四种输出方式:Iterator,ListIterator,Enumeration,foreach。

迭代输出:Iterator

image-20210830200741889

public boolean hasNext()判断是否有下一个元素
public E next()取得当前元素
public default void remove()删除元素

标准Iterator

package com.day17.demo;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

public class ArrayListDemo {
	public static void main(String[] args) {
		List<String> all = new ArrayList<>();//此时集合里面只适合保存String类型数据
		all.add("Hello");
		all.add("Hello");	//重复数据
		all.add("world~!");
		all.add("zsr~");
		Iterator<String> iter = all.iterator();//实例化Iterator
		while(iter.hasNext()){
			String str = iter.next();
			System.out.println(str);
		}
	}
}
复制代码

对于Iterator接口中提供的remove()方法主要解决的就是集合内元素删除的问题

remove操作

package com.day17.demo;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

public class ArrayListDemo {
	public static void main(String[] args) {
		List<String> all = new ArrayList<>();//此时集合里面只适合保存String类型数据
		all.add("Hello");
		all.add("Hello");	//重复数据
		all.add("a");
		all.add("world~!");
		Iterator<String> iter = all.iterator();//实例化Iterator
		while(iter.hasNext()){
			String str = iter.next();
			if("a".equals(str)){
				all.remove("a");//如果使用此操作后面的中断执行了
				//iter.remove();//如果不中断后续输出,则执行
				continue ;
			}
			System.out.println(str);
		}
	}
}
复制代码

以后只要是见到了集合的输出操作,永远都是用Iterator接口完成。

双向迭代输出:ListIterator

Iterator可以完成的是由前向后的单项输出操作,如果现在希望可以完成由前向后,由后向前的输出的话,那么就可以利用ListIterator接口完成,此接口是Iterator的子接口,在ListIterator接口主要使用一下两个扩充方法:

判断是否有前一个元素:public Boolean hasPrevious();

取出前一个元素:public E previous()。

但是如果要想取得ListIterator接口的实例化对象,Collection没有这样的方法支持,这个方法在List接口之中存在:

Public ListIterator listIterator()

执行双向迭代

package com.day17.demo;


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


public class ListIteratorDemo {
	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		List<String> all=new ArrayList<String>();
		all.add("hello");
		all.add("hello");
		all.add("world");
		ListIterator<String> ite=all.listIterator();
		System.out.println("由前向后");
		while(ite.hasNext()){
			String str=ite.next();
			System.out.print(str + "、");
		}
		System.out.println();
		System.out.println("由后向前");
		while(ite.hasPrevious()){
			String str=ite.previous();
			System.out.print(str + "、");
		}
	}
}
复制代码

但是对于由后向前的操作,在进行之前一定发生由前向后的输出。由于此输出接口只有List可以使用,所以在开发之中几乎不会出现。

废弃的接口:Enumeration

Enumeration是一个最早的输出接口,最早成为枚举输出,在JDK1.0的时候就已经推出了,并且在JDK1.5的时候将其功能进行扩充,主要就是增加了泛型,在Enumeration接口里只定义了两个方法:

判断是否有下一个元素:public Boolean hasMoreElements()

取得当前元素:public E nextElement()

不过要想取得Enumeration的实例化对象,不能依靠Collection接口了,之能够依靠Vector类完成,在Vector子类之中定义了一个方法:public Enumeration elements()。

使用Enumertaion进行输出

package com.day17.demo;

import java.util.Enumeration;
import java.util.Vector;

public class IteratorTest {
	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		Vector<String> all=new Vector<String>();
		all.add("hello");
		all.add("hello");
		all.add("world");
		Enumeration<String> ite=all.elements();
		while(ite.hasMoreElements()){
			String str=ite.nextElement();
			System.out.println(str);
		}
	}
}
复制代码

从开发而言,首先考虑的绝对不是Enumeration,考虑的肯定是Iterator,只有在必须使用的时候才用它。

JDK1.5的支持foreach

对于foreach输出除了可以进行数组的输出以外,还可以进行集合的输出。

使用foreach

package com.day17.demo;

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

public class IteratorTest {
	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		List<String> all=new ArrayList<String>();
		all.add("hello");
		all.add("hello");
		all.add("world");
		for(String x : all){
			System.out.println(x);
		}
	}
}
复制代码

使用foreach并不是一个被广泛认可的操作代码形式。

分类:
后端