Set 一个不包含重复元素的 collection。
实现类:HashSet类、LinkedHashSet类
Set 集合中的元素是无序的(LinkedHashSet除外),Set集合中是不存在下标的概念,所以肯定没有get(下标)
方法,里面所有的元素都是不重复的。如果有重复的元素,如果此 Set 已包含该元素,则该调用不更改 Set 并返回 false
方法摘要 boolean add(E e) 如果 set 中尚未存在指定的元素,则添加此元素(可选操作)。 boolean addAll(Collection<? extends E> c) 如果 set 中没有指定 collection 中的所有元素,则将其添加到此 set 中(可选操作)。 void clear() 移除此 set 中的所有元素(可选操作)。 boolean contains(Object o) 如果 set 包含指定的元素,则返回 true。 boolean containsAll(Collection<?> c) 如果此 set 包含指定 collection 的所有元素,则返回 true。 boolean equals(Object o) 比较指定对象与此 set 的相等性。 int hashCode() 返回 set 的哈希码值。 boolean isEmpty() 如果 set 不包含元素,则返回 true。 Iterator<E> iterator() 返回在此 set 中的元素上进行迭代的迭代器。 boolean remove(Object o) 如果 set 中存在指定的元素,则将其移除(可选操作)。 boolean removeAll(Collection<?> c) 移除 set 中那些包含在指定 collection 中的元素(可选操作)。 boolean retainAll(Collection<?> c) 仅保留 set 中那些包含在指定 collection 中的元素(可选操作)。 int size() 返回 set 中的元素数(其容量)。 Object[] toArray() 返回一个包含 set 中所有元素的数组。 <T> T[] toArray(T[] a) 返回一个包含此 set 中所有元素的数组;返回数组的运行时类型是指定数组的类型。
案例:
在HashSet中存储3个员工,每个员工要保存的信息是员工编号,员工姓名,员工年龄。并演示3种方式进行迭代,输出员工的信息。
在上题中添加一个重复的元素,并进行迭代,输出元素。查看
元素个数。 4个,要重写 equals 方法,就不会加入重复的元素了。
尝试添加null对象,是否可以添加? 可以添加 null 对象
import java.util.Objects; public class Emp { private String empId; private String empName; private int empAge; public String getEmpId() { return empId; } public void setEmpId(String empId) { this.empId = empId; } public String getEmpName() { return empName; } public void setEmpName(String empName) { this.empName = empName; } public int getEmpAge() { return empAge; } @Override public String toString() { return empId + ", " + empName + ", " + empAge; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Emp emp = (Emp) o; return empAge == emp.empAge && Objects.equals(empId, emp.empId) && Objects.equals(empName, emp.empName); } @Override public int hashCode() { return Objects.hash(empId, empName, empAge); } public void setEmpAge(int empAge) { this.empAge = empAge; } public Emp(String empId, String empName, int empAge) { this.empId = empId; this.empName = empName; this.empAge = empAge; } } import java.util.HashSet; import java.util.Iterator; public class HashSetTest02 { public static void main(String[] args) { HashSet<Emp> empHashSet = new HashSet<>(); empHashSet.add(new Emp("e001","小黄",20)); empHashSet.add(new Emp("e002","中黄",22)); empHashSet.add(new Emp("e003","大黄",28)); empHashSet.add(new Emp("e003","大黄",28)); //两个emp 地址值不同,集合中会重复打印 //需要重写 Emp 的 equals方法,避免重复打印 //增强 for 循环 for (Emp emp : empHashSet) { System.out.println(emp); } System.out.println("============================"); //迭代器 Iterator<Emp> iterator = empHashSet.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); } } }
需求:Scanner接收用户输入一个字符串。“aaabbbccdd",过滤
重复字符。
import java.util.HashSet; import java.util.Scanner; public class test01 { public static void main(String[] args) { Scanner input = new Scanner(System.in); System.out.print("输入字符串:"); String str = input.next(); //“aaabbbccdd" char[] chars = str.toCharArray(); HashSet<Character> hashSet = new HashSet<>(); for (char ch : chars) { hashSet.add(ch); } for (Character chh : hashSet) { System.out.print(chh); } //abcd } }
关于 Set接口,建议观看韩顺平的B站视频学习
https://www.bilibili.com/video/BV1fh411y7R8?p=518&share_source=copy_web&vd_source=0e856227873a5eed50c1cf7df4a1b5a4
LinkedHashSet 是 HashSet 的子类
LinkedHashSet 底层是一个 LinkedHashMap ,底层维护了一个 数组+双向链表
LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,同时使用链表维护元素的次序,这使得元素看起来是以插入顺序保存的
LinkedHashSet 无重复元素
1.如果是自定义对象类型:
对象默认的equals比较的是地址值
如果想不比较地址,比较属性,就重写equals方法
2.如果是String,Integer等(包装类的 equals 方法已经修改过)。
比较内容
将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。
1)Map 与 Collection 并列存在。用于保存具有映射关系的数据 :Key - Value
2)Map 中的 key 和 value 可以是任何引用类型的数据,会封装到HashMap$Node 对象中
3)Map 中的 key 不允许重复。keySet
4)Map 中的 value 可以重复
5)Map 的 key 可以为 null,value 也可以为null,注意 key 为 null,只能有一个,value 为 null,可以多个
6)key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到对应的 value
1.HashMap的扩容机制和底层跟HashSet基本一致,数组+单向链
表,HashMap使用了key和value来保存数据,而HashSet只使用了
key来保存数据。
2.HashMap无序的,只能保存一个null的key,如果key一样,就是
覆盖操作。
void clear() 从此映射中移除所有映射关系(可选操作)。 boolean containsKey(Object key) 如果此映射包含指定键的映射关系,则返回 true。 boolean containsValue(Object value) 如果此映射将一个或多个键映射到指定值,则返回 true。 Set<Map.Entry<K,V>> entrySet() 返回此映射中包含的映射关系的 Set 视图。 boolean equals(Object o) 比较指定的对象与此映射是否相等。 V get(Object key) 返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。 int hashCode() 返回此映射的哈希码值。 boolean isEmpty() 如果此映射未包含键-值映射关系,则返回 true。 Set<K> keySet() 返回此映射中包含的键的 Set 视图。 V put(K key, V value) 将指定的值与此映射中的指定键关联(可选操作)。 void putAll(Map<? extends K,? extends V> m) 从指定映射中将所有映射关系复制到此映射中(可选操作)。 V remove(Object key) 如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。 int size() 返回此映射中的键-值映射关系数。 Collection<V> values() 返回此映射中包含的值的 Collection 视图。
建议观看韩顺平B站上的视频学习:
https://www.bilibili.com/video/BV1fh411y7R8?p=537&share_source=copy_web&vd_source=0e856227873a5eed50c1cf7df4a1b5a4
HashMap 小结
HashMap 底层机制及源码剖析:
Hashtable 和 HashMap 对比:
遍历方式一:
遍历方式二:
Set<Map.Entry<k,v>> entrySet() 返回此映射所包含的映射关系的 Set 视图
k getKey() 返回与此项对应的键 v getValue() 返回与此项对应的值 public interface Map<K,V> { //内部接口 interface Entry<K,V> { K getKey(); V getValue(); } }
需求:Scanner接收用户输入一个字符串。“aaabbbccdd",
过滤重复字符并统计重复次数。
public static void main(String[] args) { Scanner input = new Scanner(System.in); System.out.print("请输入一个字符串:"); //aaabbbccdd String str = input.next(); //1.先把字符串转换为字符数组 char[] chars = str.toCharArray(); //2.创建HashMap集合 HashMap<Character, Integer> map = new HashMap<>(); //key - 字符 value - 次数 for (char ch : chars) { //定义一个变量:保存次数 int count = 1; //如果key存在 if(map.containsKey(ch)){ //2-1: 先从a中获取次数 count = map.get(ch); //2-2:然后在基础上+1 count++; } //2-3:把次数重新放入集合中 map.put(ch,count); } //遍历 Set<Character> keySet = map.keySet(); for (Character key : keySet) { System.out.println(key + ":" + map.get(key)); } }
集合中放入集合:
如:
List<Map<String,对象类型>> list = new ArrayList();
Map<Integer,Map<String,对象类型>> map2 = new HashMap<>();
案例:
public class Student { private String name; private int age; private double score; public Student(String name, int age, double score) { this.name = name; this.age = age; this.score = score; } 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; } public double getScore() { return score; } public void setScore(double score) { this.score = score; } } public class TestStu { public static void main(String[] args) { Map<String, Student> map = new HashMap<>(); map.put("A001",new Student("小强",21,39.5)); map.put("A002",new Student("赵四",22,59.5)); map.put("A003",new Student("张三",21,69.5)); System.out.println("学号\t\t姓名\t\t年龄\t\t成绩"); //遍历集合 Set<String> keySet = map.keySet(); Iterator<String> iterator = keySet.iterator(); while (iterator.hasNext()){ String stuId = iterator.next(); Student student = map.get(stuId); System.out.println(stuId+"\t\t"+student.getName()+"\t\t"+student.getAge()+"\t\t"+student.getScore()); } //嵌套集合 List<Map<String,Student>> list = new ArrayList<>(); Map<Integer,Map<String,Student>> map2 = new HashMap<>(); } }
作用:是集合的工具类,方便集合的操作。
常用的方法:
void sort(List<T> list) 根据元素的自然顺序 对指定列表按升序进行排序。
@Test public void test01(){ //根据元素的自然顺序 对指定列表按升序进行排序。 List<Integer> list = Arrays.asList(34,56,14,68,45,89); Collections.sort(list); System.out.println("list = " + list); }
排序方式扩展:= 了解
需求:想对对象中的属性进行排序???
实现步骤:
1)实现 Comparable 接口
2)实现compareTo()方法
比较的规则:比较此对象与指定对象的顺序。如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。
如果满足该规则,升序排序。
如果规则相反,降序 排序。
此对象:当前创建的对象 : this
指定对象:传入的对象Object o
int compareTo(T o) 比较此对象与指定对象的顺序。
注意:因为String类默认重写了compareTo方法,所有可以直接比较。(比较的规律不确定)
案例:
需求:
在集合中存储学生对象,然后对学生的年龄进行升序排序。
如果年龄相同的情况下,想对分数进行升序排序。
如果年龄相同的情况下,想对姓名进行升序排序。
o: 传入的对象
this: 当前对象
规则:当前对象和传入的对象的age属性进行相减对比,
分别返回负数,零,正数。
升序排序
总结:
当前对象.属性 - 传入的对象.属性 - 升序
传入的对象.属性 - 当前对象.属性 - 降序
字符串的比较:直接调用compareTo方法
@Test public void test02(){ List<Student> list = Arrays.asList( new Student("柳岩",21,39.5), new Student("张三",22,59.5), new Student("李四",22,25.5), new Student("周富强",19,89.5), new Student("徐志胜",22,78.5) ); /** * 需求: * 在集合中存储学生对象,然后对学生的年龄进行升序排序。 * 如果年龄相同的情况下,想对分数进行升序排序。 * 如果年龄相同的情况下,想对姓名进行升序排序。 */ //排序 Collections.sort(list); System.out.println(list); } public class Student implements Comparable<Student>{ private String name; private int age; private double score; public Student(String name, int age, double score) { this.name = name; this.age = age; this.score = score; } 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; } public double getScore() { return score; } public void setScore(double score) { this.score = score; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", score=" + score + "}\n"; } //如果要实现对象的比较,那么必须实现compareTo方法 /* o: 传入的对象 this: 当前对象 规则:当前对象和传入的对象的age属性进行相减对比,分别返回负数,零,正数。 升序排序 总结: 当前对象.属性 - 传入的对象.属性 - 升序 传入的对象.属性 - 当前对象.属性 - 降序 */ @Override public int compareTo(Student o) { /* if(this.getAge() - o.getAge() < 0){ return -1; }else if(this.getScore() - o.getScore() > 0){ return 1; } return 0;*/ //优化 /* return (this.getAge() - o.getAge())==0? (int)(this.getScore()-o.getScore()):(this.getAge() - o.getAge());*/ //满足年龄的情况下,对字符串排序 return (o.getAge() - this.getAge())==0? (this.getName().compareTo(o.getName())):(o.getAge() - this.getAge()); //如果一定要按中文比较 //Comparator<Object> comparator = Collator.getInstance(Locale.CHINA); //return (o.getAge() - this.getAge()) == 0 ? //(comparator.compare(o.getName(),this.getName())) : (o.getAge() - this.getAge()) } }
static <T> int binarySearch(List list, T key) 使用二分搜索法搜索指定列表,以获得指定对象。
static void reverse(List<?> list) 反转指定列表中元素的顺序。
static <T> void fill(List<? super T> list, T obj) 3 使用指定元素替换指定列表中的所有元素。
static void shuffle(List<?> list) 使用默认随机源对指定列表进行置换。