collection类继承体系图
- collection是接口,不能直接new,可以new的是class必须实现全部具体的方法
Collection体系简介
-
Collection的体系
- Collection的体系结构
- List/Set约定
-
Collection体系提供的常⽤方法:
- new: new ArrayList(Collection), new ArrayList()
collection<Integer> c = new LinkedHashSet();
// IntegerList
List<Integer> List = new ArrayList<>(c);
// 等价于
List<Integer> list2 = new ArrayList<>();
list2.addAll(c)
// 也等价于
List<Integer> list3 = new ArrayList<>();
for(Integer i : c) {
list3.add(i);
}
复制代码
- 补充:file structure查看文件结构
- R: size()/isEmpty()/contains()/for()/stream()
- C/U: add()/addAll()/retainAll(),retainAll只保留当前collection有的东西,类似数学的取交集的概念
collection<Integer> c = new LinkedHashSet();
c.add(1);
c.add(2);
List<Integer> List = new ArrayList<>();
list.add(2);
list.add(3);
list.retainAll(c) // 只保留c中有的元素, list只保留2
复制代码
- D: clear()/remove()/removeAll(),clear删除全部,remove删除某一个
List
- 最常用的ArrayList
- 本质上就是⼀一个数组
- 面试题:动态扩容的实现
- 创建⼀个更大的空间,然后把原先的所有元素拷贝过去
- add()⽅方法
- 面试题:list怎么实现动态扩容
- 甚至可以说我记不清了,但是我可以照着源代码跟你讲
- 什么是好代码
- 不需要注释,直接能看懂的代码
- 重构的原则,每当你想要写注释时,请先尝试重构,使得所有的注释都显得多余
Set
不允许有重复元素的集合。
- set去重
list.add(2);
list.add(3);
list.add(3);
list.add(3);
Set<Integer> set = new HashSet<>(list);
复制代码
- 那么如果是对象重复了呢,判断重复:equals⽅法
- 如果你要实现⼀一个Set,你会如何实现?
class MySet {
List<Object> elements;
void add(Object object) {
if (!elements.contains(object)) {
elements.add(object);
}
}
}
复制代码
- 优化成百家姓查找
- 对象哈希桶
- 上面这种容易想到的⽅法比较低效
- Java世界里第⼆重要的约定:hashCode,张三和张三返回张
- 同⼀一个对象必须始终返回相同的hashCode,张三和张三返回张
- 两个对象的equals返回true,必须返回相同的hashCode 两个对象不等,也可能返回相同的hashCode,张三和张三峰返回张
哈希算法
- 哈希就是⼀一个单向的映射
- 例子:从姓名到姓到哈希运算
- 从任意对象到一个整数的hashCode,整数正负21亿,但是对象是无限的
HashSet
- 最常⽤,最⾼效的Set实现
- 实战:HashSet的高效性
- 实战:使⽤HashSet对ArrayList去重
- HashSet是无序的!如果有需要可以使⽤LinkedHashSet
Map与常用实现
Map<String, String> map = new HashMap<>();
map.put("PATH", "1")
map.put("AAA", "2")
map.get("AAA")
复制代码
- Map介绍
- C/U:put()/putAll()
- R:
- get()/size()
- containsKey()/containsValue()
- keySet()/values()/entrySet()
- D: remove()/clear()
Map和KeySet的坑
- 改了一个会影响另一个
entrySet的使用
Map<String, String> map = new HashMap<>();
map.put("PATH", "1")
map.put("AAA", "2")
map.get("AAA")
for (Map.Entry<String, String> entry: map.entrySet()) {
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
复制代码
- 面试题:hashmap
- Hash Map和Hash Set的区别
- 没区别,HashMap的Key的Set就是Hash Set
- hashMap肚子里包了一个hashSet
面试题
- hashMap扩容过程
- 创建一个更大的hashMap将原先的东西拷贝过来,照着源码去讲
- 线程不安全,hashmap死循环问题,扩容的时候hashmap可能会变成死循环的链表
- 多线程的时候用concurrentHashMap
- 恶意攻击,hashCode退化成链表,JDK7之后处理同一个hash碰撞的时候,变成红黑树,以提高性能
- hashMap和hashSet本质是一个东西
几种set,hashSet,LinkedHashSet,TreeSet的区别
- TreeSet/TreeMap使⽤Comparable约定,认为排序相等的元素相等
List<Integer> list = Arrays.asList(100000, 196, -2, -33423423, 342342, 15);
Set set1 = new HashSet(list);
Set set2 = new LinkedHashSet(list);
Set set3 = new TreeSet(list);
set1.forEach(System.out::println); // 无序
system.out.print("------")
set2.forEach(System.out::println); // 按插入有序
system.out.print("------")
set3.forEach(System.out::println); //从小大
system.out.print("------")
复制代码
- TreeSet 就是红黑树(一种二叉树)
- 右孩子大,左孩子小
- 把算法复杂度从线性时间,下降到log时间
- 提高查找性能
- 插入的时候会发生特殊的变化
java中约定
- 工具方法就是加s,Set的工具方法就是Sets, Collection就是Collections
Collections⼯具方法集合
- emptySet(): 等返回一个⽅便的空集合
- synchronizedCollection: 将一个集合变成线程安全的
- unmodifiableCollection: 将一个集合变成不可变的(也可以 使⽤用Guava的Immutable)
Collection的其他实现
- Queue/Deque
- Vector/Stack,Vector已经被弃用,被ArrayList替代,stack也已经被弃用,被Deque替代
- LinkedList
- ConcurrentHashMap
- PriorityQueue
Guava
- 遇到collections不满足要求的时候,想到Guava
- 不要重复发明轮子!尽量使⽤经过实战检验的类库
- Lists/Sets/Maps
- ImmutableMap/ImmutableSet
- Multiset,会告诉你这个元素存了几次/Multimap
- BiMap,双向mac可有更具value去查找key
课后练习题一
- 练习题1
package com.github.hcsp.collection;
import java.util.*;
public class Main {
// 请编写一个方法,对传入的List<User>进行如下处理:
// 返回一个从部门名到这个部门的所有用户的映射。同一个部门的用户按照年龄进行从小到大排序。
// 例如,传入的users是[{name=张三, department=技术部, age=40 }, {name=李四, department=技术部, age=30 },
// {name=王五, department=市场部, age=40 }]
// 返回如下映射:
// 技术部 -> [{name=李四, department=技术部, age=30 }, {name=张三, department=技术部, age=40 }]
// 市场部 -> [{name=王五, department=市场部, age=40 }]
public static Map<Object, List<User>> collect(List<User> users) {
// 先遍历整个列表,挑选出所有的部门列表
HashSet set = new HashSet();
users.forEach(user -> {
set.add(user.getDepartment());
});
Map<Object, List<User>> map = new HashMap<>();
// 遍历所有的部门,再遍历所有对象将是这个的对象加到这个部门
set.forEach(department -> {
ArrayList list = new ArrayList();
users.forEach(user -> {
if (user.getDepartment() == department) {
list.add(user);
}
});
// 将每个部门的对象的年龄按照从小到大的顺序排序
Collections.sort(list, new SortByUserId());
map.put(department, list);
});
return map;
}
public static void main(String[] args) {
System.out.println(
collect(
Arrays.asList(
new User(1, "张三", 40, "技术部"),
new User(2, "李四", 30, "技术部"),
new User(3, "王五", 40, "市场部"))));
}
}
复制代码
package com.github.hcsp.collection;
import java.util.Comparator;
public class SortByUserId implements Comparator<User> {
@Override
public int compare(User u1, User u2) {
if (u1.getAge() > u2.getAge()) {
return 1;
}
return -1;
}
}
复制代码
- 第二题
package com.github.hcsp.collection;
import java.util.*;
public class Main {
// 请编写一个方法,获得a和b集合中的公共元素。
public static Set<Person> commonElementsIn(List<Person> a, List<Person> b) {
Set mySet = new HashSet(a);
mySet.retainAll(b);
return mySet;
}
// Person类,如果两个Person对象的name相等,则认为这两个对象相等。
public static class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Person person = (Person) o;
return Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
public static void main(String[] args) {
List<Person> list1 = Arrays.asList(new Person("张学友"), new Person("周杰伦"));
List<Person> list2 = Arrays.asList(new Person("周润发"), new Person("周杰伦"));
System.out.println(commonElementsIn(list1, list2));
}
}
复制代码
- 第三题
package com.github.hcsp.collection;
public class Person {
/** 身份证号 */
private final String id;
/** 姓名 */
private String name;
/** 年龄 */
private int age;
public Person(String id) {
this.id = id;
}
public Person(String id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
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 boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Person person = (Person) o;
return id.equals(person.id);
}
@Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
// result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + age;
return result;
}
}
复制代码
第四题
package com.github.hcsp.collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class CharCount {
/**
* 保存字符到其出现次数的映射。例如,aabbc这个字符串中,这个Map的值就是
*
* <pre>
* a -> 2
* b -> 2
* c -> 1
* </pre>
*/
private final Map<Character, Integer> charCount = new HashMap<>();
public CharCount(String s) {
for (int i = 0; i < s.length(); ++i) {
char ch = s.charAt(i);
if (charCount.containsKey(ch)) {
charCount.put(ch, charCount.get(ch) + 1);
} else {
charCount.put(ch, 1);
}
}
}
public int count(char ch) {
return charCount.getOrDefault(ch, 0);
}
/**
* 我到底包含哪些字符?
*
* @return 包含的所有字符集合
*/
public Set<Character> chars() {
return new HashSet<>(charCount.keySet());
}
// 我和另外一个CharCount有多少个公共字符? 例如,aabbcc和abcdef有3个公共字符: a/b/c,因此返回3
public int howManyCharsInCommon(CharCount anotherCharCount) {
Set<Character> myChars = chars();
Set<Character> theirChars = anotherCharCount.chars();
theirChars.retainAll(myChars);
return theirChars.size();
}
}
复制代码
- 第五题
package com.github.hcsp.collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
public class RemoveDuplicateCharsInString {
// 修改这个方法使得它能够输出正确结果:
// 例如,输入aabbcc返回abc
// 输入ccbbaa返回cba
// 输入apple返回aple
public static String removeDuplicateCharsInString(String s) {
HashSet<Character> charSet = new LinkedHashSet<>();
for (int i = 0; i < s.length(); i++) {
charSet.add(s.charAt(i));
}
String result = "";
for (Character ch : charSet) {
result += ch;
}
return result;
}
public static void main(String[] args) {
System.out.println("removeDuplicateCharsInString(\"aabbcc\")");
System.out.println(removeDuplicateCharsInString("aabbcc"));
System.out.println(removeDuplicateCharsInString("ccbbaa"));
System.out.println(removeDuplicateCharsInString("apple"));
}
}
复制代码