第十七章 集合下(接口和实现类)

一、Set 接口

Set接口: 存储的数据是唯一的,不能重复,没有顺序。(是Collection的子接口)

1.HashSet 实现类

import java.util.HashSet;
import java.util.Set;
public class TestHashSet {
	public static void main(String[] args) {
		/**	HashSet:是Set接口的实现类;数据是唯一的
		 * 			底层数据结构式哈希表;
		 * 	哈希表:将一组关键字(哈希值)映射到地址集上
		 *  哈希表原码存储方式:利用一个数组进行存储的(初始大小为16:下标为0-15);
		 *  			根据对象相应的特征,调用hashCode()方法,根据一些算法,获得哈希值;
		 *  			数组中的某一个元素位置被称为 桶:将哈希值%16,为null的话将对象放入数组中;
		 *  			哈希值冲突,会调用a.equals()方法,判断两个对象是否相同,相同不会存储,不相同可以链式存储
		 *  			如果链式存储的元素达到了8个,采取二叉树的方式存储
		 */
		Set<String> set = new HashSet<>();
		set.add("ab");
		set.add("ac");
		set.add("ab");//数据是唯一的
		set.add("ba");
		System.out.println(set);//[ab, ac, ba]
	}
}

HashSet 去重:

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
class Student{
	private int no;
	private String name;
	public Student(int no, String name) {
		this.no = no;
		this.name = name;
	}
	public int getNo() {
		return no;
	}
	public void setNo(int no) {
		this.no = no;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public String toString() {
		return "Student [no=" + no + ", name=" + name + "]";
	}
	@Override//用系统自动生成的
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		result = prime * result + no;
		return result;
	}
	@Override//用系统自动生成的
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Student other = (Student) obj;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		if (no != other.no)
			return false;
		return true;
	}	
}
public class TestHashSet1 {
	public static void main(String[] args) {
		/**	HashSet去重:数据是唯一的,本身没有顺序,底层是哈希表,先用hashCode比较哈希值,
		 *				再equals判断两个对象是否相等
		 *				两个对象一样,系统会自动去重,两个对象的属性一样不会去重,也不会报错
		 *				这时可以按照自己的方式去重,也可以用系统生成的
		 */
		Set<Student> set = new HashSet<>();
		Student guojing = new Student(1,"郭靖");
		Student yangkang = new Student(1,"郭靖");
		Student huangrong = new Student(3,"黄蓉");
		Collections.addAll(set, guojing,yangkang,huangrong);
		set.forEach(System.out::println);//唯一,无序
	}
}

在这里插入图片描述

import java.util.HashSet;
import java.util.Random;
import java.util.Set;
public class TestNumber {
	public static void main(String[] args) {
		Set<Integer> set = new HashSet<>();
		int a;
		while (true) {
			Random r = new Random();
			a = (1+r.nextInt(30));
			set.add(a);
			if (set.size()==7) {
				break;
			}
		}
		System.out.println(set);
	}
}

2.TreeSet 实现类

TreeSet的基本使用

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
class Student implements Comparable<Student>{
	private int no;
	private String name;
	private int age;
	public Student(int no, String name, int age) {
		super();
		this.no = no;
		this.name = name;
		this.age = age;
	}
	public int getNo() {
		return no;
	}
	public void setNo(int no) {
		this.no = no;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "Student [no=" + no + ", name=" + name + ", age=" + age + "]";
	}
	@Override
	public int compareTo(Student o) {
		//年龄重复了比较编号(可以比较所有的属性)
		int n = this.age-o.age;
		if (n==0) {
			return this.no-o.no;
		}
		return n;
	}
}
public class Demo1 {
	public static void main(String[] args) {
		Set<Integer> set1 = new TreeSet<>((n1,n2)->{return n2-n1;});//可以自己定制顺序
		set1.add(50);
		set1.add(45);
		set1.add(55);
		System.out.println(set1);//[45, 50, 55]
		/**	TreeSet:数据是唯一的,有顺序(默认按照自然升序排序),也可以自己定制顺序排序
		 * 			底层是二叉树,添加对象的时候是要维护次序的(要比较大小),引用数据类型不能直接比较,不然会报错
		 * 			要实现Comparable接口,让它具备排序的能力。
		 * 			树:由分支关系组成的层次结构。
		 * 			二叉树:树形结构任意节点不能超过两个。
		 * 		          遍历:中序(左根右)
		 */
		Set<Student> set = new TreeSet<>();
		Student guojing = new Student(1,"郭靖",19);
		Student yangkang = new Student(3,"杨康",19);
		Student huangrong = new Student(2,"黄蓉",19);
		Collections.addAll(set, guojing,yangkang,huangrong);
		set.forEach(System.out::println);
	}
}

3.HashSet、LinkedHashSet、TreeSet 的区别

HashSet: 底层数据结构是哈希表,数据是唯一的,没有顺序,效率高。
LinkedHashSet: 底层数据结构是链表,数据是唯一的,有顺序(元素添加的顺序维护),效率较HashSet要低一些。
TreeSet: 底层数据结构是二叉树,数据是唯一的,有顺序的(可以自己定制顺序),效率低。
效率: HashSet>>>LinkedHashSet>>>TreeSet

import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;
public class TsetSet {
	public static void main(String[] args) {
		//1.HashSet实现类:数据是唯一的,没有顺序,效率比较高
		Set<String> set = new HashSet<>();//多态的形式
		set.add("hello");
		set.add("aa");
		set.add("bb");
		//它其实是没有顺序的(因为哈希值有一定的顺序,可能会感觉有顺序)
		System.out.println(set);//[aa, bb, hello]
		/**	2.LinkedHashSet实现类:它是HashSet的子类(底层是链表);
		 * 	数据是唯一的,有一定的次序(按照元素添加的顺序来进行维护的)
		 */
		set = new LinkedHashSet<>();
		set.add("hello");
		set.add("aa");
		set.add("bb");
		//按照元素添加的顺序来进行维护的
		System.out.println(set);//[hello, aa, bb]
		//3.数据是唯一的,自己可以定制顺序
		TreeSet<String> set1 = new TreeSet<>((n1,n2)->n2.compareTo(n1));
		set.add("hello");
		set.add("aa");
		set.add("bb");
		System.out.println(set);//[hello, aa, bb]
	}
}

二、队列

队列是一种数据结构:

  1. 访问受限的线性表;
  2. 添加元素从队尾添加,删除元素从对头删除;
  3. 先进先出的数据结构;
    在这里插入图片描述

1.Queue 接口(单端队列)

列队不允许添加null值(因为nul值已经l作为了offer\poll\peek操作失败的返回值);
但是有一个特殊:LinkedList特殊(因为设计的比较早,后来没有改代码,一直沿用了下来,尽量不要往里面添加null值)
队列的基本操作:

import java.util.LinkedList;
import java.util.Queue;
public class TestQueue {
	public static void main(String[] args) {
		//建了一个队列(集合)
		Queue<String> q = new LinkedList<>();
//入队(添加):向队尾添加元素
		//添加成功返回true,失败异常。
		q.add("aa");
		//添加成功返回true,失败返回false。
		q.offer("bb");
		q.offer("cc");
		System.out.println(q);//[aa, bb]
//出队(删除)
		//返回删除的元素,删除失败引发异常。
		System.out.println(q.remove());;//aa
		//返回删除的元素,删除失败返回null。
		System.out.println(q.poll());//bb
		System.out.println(q.poll());//cc
		System.out.println(q.size());//0
		
//获取列表头元素,获取失败引发异常。
		//System.out.println(q.element());//aa
//获取列表头元素,获取失败返回null。
		//System.out.println(q.element());//aa
		System.out.println("--------------");
//循环出队
		while (q.size()>0) {
			System.out.println(q.poll());
		}
	}
}

2.Deque 接口(双端队列)

Deque :可以对列头和列尾操作。

三、Map (双列存储)

1.Map的基本使用

Map 接口: 是映射,它是双列存储的,一对信息(键值对,键是唯一的,值可以重复)。
常用方法:

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class TestMap1 {
//Map接口的常用方法
	public static void main(String[] args) {
		//HashMap:键是唯一的,没有顺序
		//创建(映射)集合:map
		Map<Integer,String> map = new HashMap<>();
		//添加键值对
		map.put(11, "aa");
		map.put(22, "bb");
		map.put(33, "cc");
		System.out.println(map);//{33=cc, 22=bb, 11=aa}
//1.返回集合中的键值对的数目
		System.out.println(map.size());//3
//2.集合是否为空
		System.out.println(map.isEmpty());//false
//3.指定的键在集合中是否包含
		System.out.println(map.containsKey(22));//true
//4.指定的值在集合中是否包含
		System.out.println(map.containsValue("cc"));//true
//5.删除指定的键在集合中的键值对信息
		map.remove(22);
		System.out.println(map);//{33=cc, 11=aa}
//6.键的集合Set
		System.out.println(map.keySet());//[33, 11]
//7.值的集合Collection
		System.out.println(map.values());//[cc, aa]
//8.清除
		map.clear();
		System.out.println(map.size());//0	
	}
}

需要注意的地方:

import java.util.HashMap;
import java.util.Map;
public class TestMap2 {
	public static void main(String[] args) {
		Map<Integer,String> map = new HashMap<>();
		map.put(11, "aa");
		map.put(22, "bb");
		map.put(33, "cc");
		//map.put(33, "cc");
		//System.out.println(map);//{33=cc, 22=bb, 11=aa}
		map.put(33, "abc");//新值覆盖
		System.out.println(map);//{33=abc, 22=bb, 11=aa}
	//修改键对应的值
		map.replace(11, "hello");
		System.out.println(map);//{33=abc, 22=bb, 11=hello}
	}
}

遍历的方式:

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.BiConsumer;
public class TestMap3 {
	public static void main(String[] args) {
		//遍历-------------
		Map<Integer,String> map = new HashMap<>();
		map.put(11, "aa");
		map.put(22, "bb");
		//----------遍历输出-----------------
		//1.集合的forEach方法
		map.keySet().forEach(System.out::println);//遍历键
		map.values().forEach(System.out::println);//遍历值
		map.forEach(new BiConsumer<Integer, String>() {//遍历键和值
			@Override
			public void accept(Integer t, String u) {
				System.out.println(t+","+u);
			}
		});
		map.forEach((m1,m2)->{System.out.println(m1+":"+m2);});//遍历键和值
		//2.迭代器
		map.keySet().iterator().forEachRemaining(System.out::println);//遍历键
		map.values().iterator().forEachRemaining(System.out::println);//遍历值
		map.entrySet().forEach(System.out::println);//遍历键和值
		//entry遍历
		map.entrySet().forEach((e)->{System.out.println(e.getKey()+","+e.getValue());});
		//键值对的集合
		Set<Entry<Integer,String>> set = map.entrySet();
		Iterator<Entry<Integer, String>> i = set.iterator();//获得set的迭代器
		while (i.hasNext()) {
			//System.out.println(i.next());//遍历键和值
			Entry<Integer,String> e = i.next();
			System.out.println("键:"+e.getKey());
			System.out.println("值:"+e.getValue());
		}
	}
}

2.和Map的区别

import java.util.HashMap;
import java.util.HashSet;
public class TestMap4 {
	public static void main(String[] args) {
		/**	区别:Set:Set的值相当于Map的键,值是Object对象
		 * 		 Map:键完全相同的情况下,新值会覆盖旧值
		 */
		HashSet<Integer> set = new HashSet<>();
		set.add(11);
		
		HashMap<Integer,String> map = new HashMap<>();
		map.put(11, "aa");
	}
}

3.Map总结

1.HashMap: 直接实现了Map接口;
        HashMap:底层是哈希表;键是唯一的;值可以重复;没有顺序;可以存储null键和null值。
        Hashtable:也实现了Map接口;线程安全的;性能低(弃用了)。
2.LinkedHashMap: 底层是链表;键是唯一的;有顺序(键会按照元素添加的顺序维护键值对);
3.TreeMap: 底层是二叉树;键是唯一的;可以对键定制顺序。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值