21中级 - Collection体系原理与实战

collection类继承体系图

1.png

  • 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);
    }
  }
}
复制代码

2.png

  • 优化成百家姓查找

3.png

  • 对象哈希桶

4.png

  • 上面这种容易想到的⽅法比较低效
    • 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的坑

  • 改了一个会影响另一个

6.png

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

7.png

  • Hash Map和Hash Set的区别
    • 没区别,HashMap的Key的Set就是Hash Set

8.png

  • hashMap肚子里包了一个hashSet

面试题

  • hashMap扩容过程
    • 创建一个更大的hashMap将原先的东西拷贝过来,照着源码去讲
  • 线程不安全,hashmap死循环问题,扩容的时候hashmap可能会变成死循环的链表
  • 多线程的时候用concurrentHashMap
  • 恶意攻击,hashCode退化成链表,JDK7之后处理同一个hash碰撞的时候,变成红黑树,以提高性能

9.png

扫描二维码关注公众号,回复: 13167812 查看本文章
  • hashMap和hashSet本质是一个东西

几种set,hashSet,LinkedHashSet,TreeSet的区别

10.png

  • 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时间
    • 提高查找性能

11.png

  • 插入的时候会发生特殊的变化

12.png

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

13.png

14.png

  • 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"));
    }
}
复制代码

猜你喜欢

转载自juejin.im/post/7018765919107153928