Java基础进阶篇-第二天

79 阅读6分钟

Set容器

Set容器中的方法与Collection方法基本一致

Set集合去重复只需要重写集合存储类的equals/hashCode方法

HashSet集合

底层基于哈希表实现

哈希值:一个int类型的随机值,java中的每个对象都有一个哈希值(通过object类的hashCode方法获取值)

对象哈希值的特点

  • 同一个对象多次调用hashCode方法返回的哈希值是相同的。
  • 不同的对象,它们的哈希值大概率不相等,但也有可能会相等(哈希碰撞)。

哈希碰撞:int类型范围是-21亿~21亿,当超出这个范围哈希值就会发生重复即为碰撞。

哈希表

  • jdk8之前,哈希表=数组+链表实现,jdk8之后,哈希=数组+链表+红黑树
  • 哈希表是一种增删改查数据,性能都较好的数据结构

哈希表存储过程

  1. 创建一个默认长度16的数组,默认加载因子为0.75,数组名table

  2. 使用元素的哈希值对数组的长度做运算计算出应存入的位置

  3. 判断当前位置是否为null,如果是null直接存入

  4. 如果不为null,表示有元素,则调用equals方法比较相等,则不存;不相等,则存入数组

    • JDK 8之前,新元素存入数组,占老元素位置,老元素挂下面

    • JDK 8开始之后,新元素直接挂在老元素下面

哈希表的扩容变化

  • 数组长度*加载因子=扩容长度;当数组长度达到扩容长度则会进行两倍扩容
  • 当链表长度超过8,且数组长度>=64时,自动将链表转成红黑树(JDK8开始之后)
LinkedHashSet集合

底层基于哈希表(数组+链表+红黑树)实现的。

每个元素都额外的多了一个双链表的机制记录它前后元素的位置。

TreeSet集合

基于红黑树实现(通过指定的比较器Comparator来判断元素是否重复)

// 子类特定方法
public boolean addAll(Collection<? extends E> c); // 添加集合中的所有元素
public E first() // 返回第一个元素
public E last() // 返回最后一个元素

默认从小到大进行元素排序,若存储自定义对象则需要给对象制定比较规则。

  • 方案1:让对象的类实现Comparable接口,重写compareTo方法,声明大小规则。
  • 方案2:TreeSet集合自带抽象比较器对象,通过有参构造器实现容器中的比较器,来制定比较规则。
// 方案1
public class Student implements Comparable<Student> {
    private String name;
    private Integer age;
    private double score;
    private String address;

    // 实现比较器
    @Override
    public int compareTo(Student o) {
        // 从小到大
        return Double.compare(this.getScore(), o.getScore());
    }
}
// 方案2 匿名函数,实现TreeSet内部的抽象比较函数
Set<User> users = new TreeSet<>(new Comparator<User>() {
    @Override
    public int compare(User o1, User o2) {
        return Double.compare(o1.getScore(), o2.getScore());
        /*if(o1.getScore() > o2.getScore()){
            return 1;
        }else if (o1.getScore() < o2.getScore()){
            return -1;
        }
        return 0;*/
    }
});

Map集合的体系

Map集合体系

Set集合中的HashSet/LinkedHashSet/TreeSet等集合底层都是Map集合中对应的容器实现。

Set集合只是使用了Map集合中的Key存储数据。

TreeMap集合(通过指定的比较器Comparator来判断元素是否重复)

常用方法

方法名称说明
public V put(K key,V value)添加元素
public int size()获取集合的大小
public void clear()清空集合
public boolean isEmpty()判断集合是否为空,为空返回true , 反之
public V get(Object key)根据键获取对应值
public V remove(Object key)根据键删除整个元素
public boolean containsKey(Object key)判断是否包含某个键
public boolean containsValue(Object value)判断是否包含某个值
public Set keySet()获取全部键的集合
public Collection values()获取Map集合的全部值

遍历方式

Map<Integer, String> map = new HashMap<>();
map.put(1, "张三");
map.put(2, "李四");
map.put(3, "王五");
map.put(4, "赵六");

// 方法一
for (Integer i : map.keySet()) {
    System.out.print(map.get(i)+" ");
}
System.out.println();

// 方法二
for (Map.Entry<Integer, String> item : map.entrySet()) {
    System.out.print(item.getKey()+" ");
    System.out.print(item.getValue()+"\t");
}
System.out.println();

// 方法三 内部通过的方法二中的entrySet()实现
map.forEach((k,v)->{
    System.out.print(k+" "+v+"\t");
});

Stream流(JDK8之后)

获取Stream流

List<Student> list = new ArrayList<>();
Stream<Student> stream = list.stream(); // Collection集合含有stream方法

String[] arr = {"张三", "李四", "王五", "赵六"};
Stream<String> s1 = Arrays.stream(arr);
Stream<String> s2 = Stream.of(arr); //  Stream.of 为静态方法,of参数为数组数据。

处理Stream流(常用方法)

Stream提供的常用中间方法说明
Stream filter(Predicate<? super T> predicate)用于对流中的数据进行过滤。
Stream sorted()对元素进行升序排序
Stream sorted(Comparator<? super T> comparator)按照指定规则排序
Stream limit(long maxSize)获取前几个元素
Stream skip(long n)跳过前几个元素
Stream distinct()去除流中重复的元素。
Stream map(Function<? super T,? extends R> mapper)对元素进行加工,并返回对应的新流
static Stream concat(Stream a, Stream b)合并a和b两个流为一个流
List<Student> list = new ArrayList<>();
list.add(new Student("张三", 18, 100, "北京"));
list.add(new Student("李四", 19, 90, "上海"));
list.add(new Student("李四", 42, 90, "上海"));
list.add(new Student("王五", 20, 80, "广州"));
list.add(new Student("赵六", 45, 70, "深圳"));
list.add(new Student("赵六", 45, 70, "深圳"));

list.stream().filter(student -> student.getAge() > 18).forEach(System.out::println);

// 调用Student中的比较方法,类需要实现Comparable接口中的compareTo方法
list.stream().sorted().forEach(System.out::println); 

list.stream().sorted(Comparator.comparingInt(Student::getAge)).forEach(System.out::println);

list.stream().limit(2).forEach(System.out::println);

list.stream().skip(2).forEach(System.out::println);

list.stream().distinct().forEach(System.out::println);

list.stream().map(Student::getName).forEach(System.out::println);

Stream<String> s1 = list.stream().map(Student::getName);
Stream<Student> s2 = list.stream().filter(student -> student.getScore() > 60);
Stream<Object> o = Stream.concat(s1,s2);
o.forEach(System.out::println);

收集Stream流

Stream提供的常用终结方法说明
void forEach(Consumer action)对此流运算后的元素执行遍历
long count()统计此流运算后的元素个数
Optional max(Comparator<? super T> comparator)获取此流运算后的最大值元素
Optional min(Comparator<? super T> comparator)获取此流运算后的最小值元素
R collect(Collector collector)把流处理后的结果收集到一个指定的集合中去
Object[] toArray()把流处理后的结果收集到一个数组中去
Collectors工具类提供了具体的收集方式说明
public static Collector toList()把元素收集到List集合中
public static Collector toSet()把元素收集到Set集合中
public static Collector toMap(Function keyMapper , Function valueMapper)把元素收集到Map集合中
List<Student> list = new ArrayList<>();
list.add(new Student("张三", 18, 100, "北京"));
list.add(new Student("李四", 19, 90, "上海"));
list.add(new Student("李四", 42, 50, "上海"));
list.add(new Student("王五", 20, 80, "广州"));
list.add(new Student("赵六", 45, 70, "深圳"));

list.stream().map((s) -> s.getScore()).forEach(x -> System.out.print(x+" "));

long l = list.stream().map((s) -> s.getScore()).count();
System.out.println(l);

Optional<Student> min = list.stream().min((s1, s2) -> s1.getScore() - s2.getScore());
System.out.println(min.get());

Optional<Student> max = list.stream().max((o1, o2) -> o1.getAge() - o2.getAge());
System.out.println(max.get());

list.stream().filter(s -> s.getName().startsWith("李")).collect(Collectors.toList());
list.stream().filter(s -> s.getName().startsWith("李")).collect(Collectors.toSet());

Object[] ss = list.stream().filter(s -> s.getName().startsWith("李")).toArray();
System.out.println(Arrays.toString(ss));

Map<Integer,String> map = list.stream().filter(s -> s.getName().startsWith("李")).collect(Collectors.toMap(Student::getAge,Student::getName));
map.forEach((k,v) -> System.out.println(k+" "+v));

可变参数

格式:数据类型...参数名称

可变参数的特点和好处

  • 特点
    • 可以不传数据给它
    • 可以传一个或者同时传多个数据给它
    • 也可以传一个数组给它
  • 好处:常常用来灵活的接收数据

可变参数的注意事项

  • 可变参数在方法内部就是一个数组
  • 一个形参列表中可变参数只能有一个
  • 可变参数必须放在形参列表的最后面
public void sum(int age,int...num){
    System.out.println(age);
    System.out.println(Arrays.toString(num));
}

Collections工具类

方法名称说明
public static boolean addAll(Collection<? super T> c, T... elements)给集合批量添加元素
public static void shuffle(List<?> list)打乱List集合中的元素顺序
public static void sort(List list)对List集合中的元素进行升序排序
public static void sort(List list,Comparator<? super T> c)对List集合中元素,按照比较器对象指定的规则进行排序
Collections.addAll(list,new Student("1",2,3,"4"));
Collections.shuffle(list);
Collections.sort(list); // 默认升序
Collections.sort(list, (s1,s2) -> s1.getAge() - s2.getAge()); // 自定义排序