javase笔记
一、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.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: 底层是二叉树;键是唯一的;可以对键定制顺序。