集合框架

接口:Collection接口、Map接口。Java集合框架图,如下:

java集合总结(源码分析) - 图1
Collections,提供了对集合进行排序、遍历等多种算法实现;
Arrays,提供了大量的静态方法,用来实现数组常见的操作;
自定义的集合类型,那么类需要有Comparable或者Comparator接口的支持;
Iterator(迭代器)不是一个集合,它是一种用于访问集合的方法;
ListIterator 是 Collection API 中的接口, 它扩展了 Iterator 接口;

函数式接口

https://www.jianshu.com/p/8005f32caf3d

什么是函数式接口?

(1)函数式接口在java中是指:有且仅有一个抽象方法的接口。

  • 函数式接口,即适用于函数式编程场景的接口。而java中的函数式编程体现就是Lambda,所以函数式接口就是可以适用于Lambda使用的接口。只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导。

(2)你可以通过Lambda表达式来创建该接口的对象。(若Lambda表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明)。
(3)我们可以在任意函数式接口上使用@FunctionalInterface注解,这样做可以检查它是否是一个函数式接口(一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。),同时javadoc也会包含一条声明,说明这个接口是一个函数式接口。需要注意的是,即使不使用该注解,只要满足函数式接口的定义,这仍然是一个函数式接口,使用起来都一样。(该接口是一个标记接口)

备注:“语法糖”是指使用更加方便,但是原理不变的代码语法。例如在遍历集合时使用的for-each语法,其实底层的实现原理仍然是迭代器,这便是“语法糖”。从应用层面来讲,Java中的Lambda可以被当做是匿名内部类的“语法糖”,但是二者在原理上是不同的。

自定义函数

public abstract 可以不写,编译器自动加上

  1. @FunctionalInterface
  2. public interface MyNumber{
  3. double getValue();
  4. }

函数式接口中使用泛型

  1. @FunctionalInterface
  2. public interface MyFunc<T>{
  3. T getValue(T t);
  4. }

函数式编程

lambda的延迟执行

有些场景的代码执行后,结果不一定会被使用,从而造成性能浪费。而Lambda表达式是延迟执行的,这正好可以作为解决方案,提升性能。

性能浪费的日志案例

  1. 一种典型的场景就是对参数进行有条件使用,

例如对日志消息进行拼接后,在满足条件的情况下进行打印输出:

  1. public class Demo01Logger {
  2. private static void log(int level, String msg) {
  3. if (level == 1) {
  4. System.out.println(msg);
  5. }
  6. }
  7. public static void main(String[] args) {
  8. String msgA = "Hello";
  9. String msgB = "World";
  10. String msgC = "Java";
  11. log(1, msgA + msgB + msgC);//级别1 不一定能够满足 但是 字符串连接操作还是执行了 那么字符串的拼接操作就白做了,存在性能浪费
  12. }
  13. }
  1. SLF4J是应用非常广泛的日志框架,它在记录日志时为了解决这种性能浪费的问题,并不推荐首先进行字符串的拼接,而是将字符串的若干部分作为可变参数(包装为数组)传入方法中,

仅在日志级别满足要求的情况下才会进行字符串拼接。

  1. LOGGER.debug("变量{}的取值为{}。", "os", "macOS")

其中的大括号 {} 为占位符。如果满足日志级别要求,则会将“os”和“macOS”两个字符串依次拼接到大括号的位置;否则不会进行字符串拼接。

  1. Lambda可以做到更好。

    1. @FunctionalInterface
    2. public interface MessageBuilder {
    3. String buildMessage();
    4. }
    5. public class Demo02LoggerLambda {
    6. private static void log(int level, MessageBuilder builder) {
    7. if (level == 1) {
    8. System.out.println(builder.buildMessage());// 实际上利用内部类 延迟的原理,代码不相关 无需进入到启动代理执行
    9. }
    10. }
    11. public static void main(String[] args) {
    12. String msgA = "Hello";
    13. String msgB = "World";
    14. String msgC = "Java";
    15. log(2,()->{
    16. System.out.println("lambda 是否执行了");
    17. return msgA + msgB + msgC;
    18. });
    19. }
    20. }

    使用Lambda作为参数和返回值

    假设有一个 方法使用该函数式接口作为参数,那么就可以使用Lambda进行传参.如线程中的Runable接口

    1. public class Runnable {
    2. private static void startThread(Runnable task) { // 为了将Lambda表达式作为参数传递,接收Lambda表达式的参数类型必须是与该Lambda表达式兼容的函数式接口的类型。
    3. new Thread(task).start();
    4. }
    5. public static void main(String[] args) {
    6. startThread(() ‐> System.out.println("线程任务执行!"));
    7. }
    8. }

    如果一个方法的返回值类型是一个函数式接口,那么就可以直接返回一个Lambda表达式(类始于new 内部类的实现方式 方法中可以实现构造内部类)

    1. public class lambda_Comparator {
    2. //下面给出 lambda 以及实际替代的内部类写法
    3. private static Comparator<String> newComparator(){
    4. return (a,b)->b.length()-a.length();
    5. }
    6. private static Comparator<String> newComparator1(){
    7. return new Comparator<String>() {
    8. @Override
    9. public int compare(String a, String b) {
    10. return b.length()-a.length();
    11. }
    12. };
    13. }
    14. public static void main(String[] args) {
    15. String[] array={"abc","ab","abcd"};
    16. System.out.println(Arrays.toString(array));
    17. Arrays.sort(array, newComparator1()); // 方式一
    18. Arrays.sort(array,(a,b)->b.length()-a.length());//更简单的方式
    19. System.out.println(Arrays.toString(array));
    20. }
    21. }

    Java内置四大核心函数式接口

    要使用lambda表达式,就要创建一个函数式接口。
    那每次用lambda表达式的时候岂不是很麻烦,这时候,java给我们内置了四大核心函数式接口。
    包:java.util.function

    Supplier接口(供应接口)

    1. @FunctionalInterface
    2. public interface Supplier<T> {
    3. /**
    4. * Gets a result.
    5. *
    6. * @return a result
    7. */
    8. T get();
    9. }

    用来获取一个泛型参数指定类型的对象数据。

    练习

    1. public class Test_Supplier {
    2. private static String test_Supplier(Supplier<String> suply) {
    3. return suply.get(); //供应者接口
    4. }
    5. public static void main(String[] args) {
    6. // 产生的数据作为 sout 作为输出
    7. System.out.println(test_Supplier(()->"产生数据"));
    8. System.out.println(String.valueOf(new Supplier<String>() {
    9. @Override
    10. public String get() {
    11. return "产生数据";
    12. }
    13. }));
    14. }
    15. }
    1. public class use_Supplier_Max_Value {
    2. private static int getMax(Supplier<Integer> suply) {
    3. return suply.get();
    4. }
    5. public static void main(String[] args) {
    6. Integer [] data=new Integer[] {6,5,4,3,2,1};
    7. int reslut=getMax(()->{
    8. int max=0;
    9. for (int i = 0; i < data.length; i++) {
    10. max=Math.max(max, data[i]);
    11. }
    12. return max;
    13. });
    14. System.out.println(reslut);
    15. }
    16. }

    Consumer接口

    1. @FunctionalInterface
    2. public interface Consumer<T> {
    3. /**
    4. * Performs this operation on the given argument.
    5. *
    6. * @param t the input argument
    7. */
    8. void accept(T t);
    9. /**
    10. * Returns a composed {@code Consumer} that performs, in sequence, this
    11. * operation followed by the {@code after} operation. If performing either
    12. * operation throws an exception, it is relayed to the caller of the
    13. * composed operation. If performing this operation throws an exception,
    14. * the {@code after} operation will not be performed.
    15. *
    16. * @param after the operation to perform after this operation
    17. * @return a composed {@code Consumer} that performs in sequence this
    18. * operation followed by the {@code after} operation
    19. * @throws NullPointerException if {@code after} is null
    20. */
    21. default Consumer<T> andThen(Consumer<? super T> after) {
    22. Objects.requireNonNull(after);
    23. return (T t) -> { accept(t); after.accept(t); };
    24. }
    25. }

    void accept(T t),消费一个数据,其数据类型由泛型决定
    默认方法:andThen,消费数据的时候,首先做一个操作,然后再做一个操作,实现组合

练习

  1. public class Test_Comsumer {
  2. public static void generateX(Consumer<String> consumer) {
  3. consumer.accept("hello consumer");
  4. }
  5. public static void main(String[] args) {
  6. generateX(s->System.out.println(s));
  7. }
  8. }
  1. public class use_Consumer_FormattorName {
  2. public static void formattorPersonMsg(Consumer<String[]> con1, Consumer<String[]> con2) {
  3. // con1.accept(new String[]{ "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男" });
  4. // con2.accept(new String[]{ "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男" });
  5. // 一句代码搞定
  6. con1.andThen(con2).accept(new String[] { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男" });
  7. }
  8. public static void main(String[] args) {
  9. formattorPersonMsg((s1) -> {
  10. for (int i = 0; i < s1.length; i++) {
  11. System.out.print(s1[i].split("\\,")[0] + " ");
  12. }
  13. }, (s2) -> {
  14. System.out.println();
  15. for (int i = 0; i < s2.length; i++) {
  16. System.out.print(s2[i].split("\\,")[1] + " ");
  17. }
  18. });
  19. System.out.println();
  20. printInfo(s->System.out.print(s.split("\\,")[0]),
  21. s->System.out.println(","+s.split("\\,")[1]),datas);
  22. }
  23. // 自身自销 有意思
  24. private static void printInfo(Consumer<String> one, Consumer<String> two, String[] array) {
  25. for (String info : array) { // 这里每次产生 {迪丽热巴。性别:女 } String 数据 逻辑那边顺序处理就行
  26. one.andThen(two).accept(info); // 姓名:迪丽热巴。性别:女。 } }
  27. }
  28. }
  29. }

Predicate接口

  1. @FunctionalInterface
  2. public interface Predicate<T> {
  3. boolean test(T var1);
  4. default Predicate<T> and(Predicate<? super T> var1) {
  5. Objects.requireNonNull(var1);
  6. return (var2) -> {
  7. return this.test(var2) && var1.test(var2);
  8. };
  9. }
  10. default Predicate<T> negate() {
  11. return (var1) -> {
  12. return !this.test(var1);
  13. };
  14. }
  15. default Predicate<T> or(Predicate<? super T> var1) {
  16. Objects.requireNonNull(var1);
  17. return (var2) -> {
  18. return this.test(var2) || var1.test(var2);
  19. };
  20. }
  21. static <T> Predicate<T> isEqual(Object var0) {
  22. return null == var0 ? Objects::isNull : (var1) -> {
  23. return var0.equals(var1);
  24. };
  25. }
  26. // 源码中没有
  27. static <T> Predicate<T> not(Predicate<? super T> target) {
  28. Objects.requireNonNull(target);
  29. return (Predicate<T>)target.negate();
  30. }
  31. }

boolean test(T t) 。用于条件判断的场景
默认方法:and(与) or(或) nagte (取反)
静态方法:not isEquals

练习

  1. /**
  2. * 1. 必须为女生;
  3. * 2. 姓名为4个字。
  4. */
  5. public static void main(String[] args) {
  6. String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "赵丽颖,女" };
  7. getFemaleAndname((s) -> s.split("\\,")[0].length() == 4,
  8. (s) -> s.split("\\,")[1].equals("女"), array);
  9. }
  10. private static void getFemaleAndname(Predicate<String> one,
  11. Predicate<String> two, String[] arr) {
  12. for (String string : arr) {
  13. if (one.and(two).test(string)) {
  14. System.out.println(string);
  15. }
  16. }
  17. }

Function接口

  1. @FunctionalInterface
  2. public interface Function<T, R> {
  3. /**
  4. * Applies this function to the given argument.
  5. *
  6. * @param t the function argument
  7. * @return the function result
  8. */
  9. R apply(T t);
  10. /**
  11. * Returns a composed function that first applies the {@code before}
  12. * function to its input, and then applies this function to the result.
  13. * If evaluation of either function throws an exception, it is relayed to
  14. * the caller of the composed function.
  15. *
  16. * @param <V> the type of input to the {@code before} function, and to the
  17. * composed function
  18. * @param before the function to apply before this function is applied
  19. * @return a composed function that first applies the {@code before}
  20. * function and then applies this function
  21. * @throws NullPointerException if before is null
  22. *
  23. * @see #andThen(Function)
  24. */
  25. default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
  26. Objects.requireNonNull(before);
  27. return (V v) -> apply(before.apply(v));
  28. }
  29. /**
  30. * Returns a composed function that first applies this function to
  31. * its input, and then applies the {@code after} function to the result.
  32. * If evaluation of either function throws an exception, it is relayed to
  33. * the caller of the composed function.
  34. *
  35. * @param <V> the type of output of the {@code after} function, and of the
  36. * composed function
  37. * @param after the function to apply after this function is applied
  38. * @return a composed function that first applies this function and then
  39. * applies the {@code after} function
  40. * @throws NullPointerException if after is null
  41. *
  42. * @see #compose(Function)
  43. */
  44. default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
  45. Objects.requireNonNull(after);
  46. return (T t) -> after.apply(apply(t));
  47. }
  48. /**
  49. * Returns a function that always returns its input argument.
  50. *
  51. * @param <T> the type of the input and output objects to the function
  52. * @return a function that always returns its input argument
  53. */
  54. static <T> Function<T, T> identity() {
  55. return t -> t;
  56. }
  57. }

用来根据一个类型的数据得到另一个类型的数据
默认方法 andThen compose()
静态方法 identity:静态方法 identity

练习

  1. public static void main(String[] args) {
  2. String str = "赵丽颖,20";
  3. solotion(s->s.split("\\,")[1],s->Integer.parseInt(s),s->s+=100,str);
  4. }
  5. private static void solotion(Function<String, String> o1, Function<String,
  6. Integer> o2, Function<Integer, Integer> o3, String str) {
  7. Integer apply = o1.andThen(o2).andThen(o3).apply(str);
  8. System.out.println(apply);
  9. }

扩展函数接口

扩展函数接口太多了, 但是掌握了基本的五大函数接口

标识接口

Cloneable接口


(1)实现了Cloneable接口,以指示Object的clone()方法可以合法地对该类实例进行按字段复制;
(2)如果在没有实现Cloneable接口的实例上调用Object的clone()方法,则会导致抛出CloneNotSupporteddException;
(3)按照惯例,实现此接口的类应该使用公共方法重写Object的clone()方法,Object的clone()方法是一个受保护的方法;
Object的clone()方法创建并返回此对象的一个副本。对于任何对象x,表达式:
(1)x.clone() != x为true;
(2)x.clone().getClass() == x.getClass()为true;
(3)x.clone().equals(x)一般情况下为true,但这并不是必须要满足的要求;


Serializable接口


    为了能直接以 Java 对象的形式进行保存,然后再重新得到该 Java 对象,这就需要序列化能力。序列化其实可以看成是一种机制,按照一定的格式将 Java 对象的某状态转成介质可接受的形式,以方便存储或传输。
    Serializable接口是用于实现Java类的序列化操作而提供的一个语义级别的接口。
(1)提供一种简单又可扩展的对象保存恢复机制。
(2)对于远程调用,能方便对对象进行编码和解码,就像实现对象直接传输。
(3)可以将对象持久化到介质中,就像实现对象直接存储。
(4)允许对象自定义外部存储的格式。

RandomAccess接口


Map

HashMap
    

LinkedHashMap
    

ConcurrentHashMap
    

Hashtable
    

TreeMap

Collection接口

Collection接口是所有集合类的根节点,Collection表示一种规则,所有实现了Collection接口的类遵循这种规则。
AbstractCollection抽象类,Collection接口的骨架实现类,最小化实现了Collection接口所需要实现的工作量。
基本所有集合的实现类,同时实现了CloneableSerializable接口。

List


    List接口是一个元素有序(按照插入的顺序维护元素顺序)、可重复、可以为null的集合,中可以存放任意的数据。
    public interface List<E> extends Collection<E> {}
    Iterator接口实例化:Iterator<E> iterator();
    AbstractList抽象类, List接口的骨架实现类,最小化实现了List接口所需要实现的工作量。


LinkedList


    LinkedList 是基于链表实现的,所以它的插入和删除操作高效。但也是由于其为基于链表的,所以随机访问的效率差。
public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable {}
    AbstractSequentialList抽象类,继承自 AbstractList抽象类,是 List 接口 的简化版实现,只支持按次序访问
    Iterator接口实例化:
(1)public Iterator<E> iterator();
    public abstract ListIterator<E> listIterator(int index);
    Deque接口,定义了一个线性Collection,支持在两端插入和删除元素, Deque实际是“double ended queue(双端队列)”的简称,大多数Deque接口的实现都不会限制元素的数量,但是这个队列既支持有容量限制的实现,也支持没有容量限制的实现。比如,LinkedList就是有容量限制的实现,其最大的容量为Integer.MAX_VALUE

  1. //长度
  2. transient int size = 0;
  3. //指向头结点
  4. transient Node<E> first;
  5. //指向尾结点
  6. transient Node<E> last;

    Iterator接口实例化:
(1)public Iterator<E> descendingIterator();//return new DescendingIterator();
    private class DescendingIterator implements Iterator<E> {}
(2)public ListIterator<E> listIterator(int index);//return new ListItr(index);
    private class ListItr implements ListIterator<E> {}


ArrayList


    一个其容量能够动态增长的动态数组。可以随机访问元素,但在List中间插入和移除元素时较慢。同时,ArrayList的操作不是线程安全的,一般在单线程中才使用ArrayList。
public class ArrayList<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {}
    AbstractList抽象类,其内部只有一个抽象方法 get(),子类必须实现它,一般是作为获取元素的用途。除此之外,如果子类要操作元素,还需要重写 add(), set(), remove() 方法,因为 AbstractList 虽然定义了这几个方法,但默认是不支持的,直接是抛出UnsupportedOperationException 异常。
    Iterator接口实例化:
(1)private class Itr implements Iterator<E> {}
(2)private class ListItr extends Itr implements ListIterator<E> {}
    RandomAccess接口是 List 实现所使用的标记接口,用来表明其支持快速(通常是固定时间)随机访问。此接口的主要目的是允许一般的算法更改其行为,从而在将其应用到随机或连续访问列表时能提供良好的性能。

  1. // 内部实现为Object数组
  2. transient Object[] elementData;
  3. // 实际元素的数量
  4. private int size;

    Iterator接口实例化:
(1)public Iterator<E> iterator();//return new Itr();
(2)public ListIterator<E> listIterator();//return new ListItr(0);
    指定位置迭代:public ListIterator<E> listIterator(int index);


Vector(放弃)


    一个可增长的数组(和 ArrayList 类似),能够用索引直接找到元素,Vector 的容量可增可减。Vector 是线程安全的,它所有的方法都加上了 synchronized 关键字(放弃的原因)。由于进行同步等的操作,Vector的效率理论上会比其他几个List的操作低。
public class Vector<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {}

  1. // Vector 的操作就是基于这个数组来实现的:
  2. protected Object[] elementData;
  3. // Vector 中的元素数量
  4. protected int elementCount;
  5. // Vector 的增量,用它来判断需要扩容多少
  6. protected int capacityIncrement;

    Iterator接口实例化:
(1)public synchronized Iterator<E> iterator();//return new Itr();
    private class Itr implements Iterator<E> {}
(2)public synchronized ListIterator<E> listIterator();//return new ListItr(0);
    指定位置迭代:final class ListItr extends Itr implements ListIterator<E>;


Stack(放弃)


    一个标准的后进先出的栈。堆栈只定义了默认构造函数,用来创建一个空栈。 堆栈除了包括由Vector定义的所有方法,也定义了自己的一些方法。depue(双端队列)比起stack具有更好的完整性和一致性,应该被优先使用。
    public class Stack<E> extends Vector<E>
    Stack本身通过扩展Vector而来,而Vector本身是一个可增长的对象数组,那么这个数组的哪里作为Stack的栈顶,哪里作为Stack的栈底?
答案从源代码中寻找,jdk:addElement(item),the last item of the Vector object。


CopyOnWriteArrayList


    ReentrantReadWriteLock 读写锁的思想,读读共享、写写互斥、读写互斥、写读互斥
    CopyOnWrite思想,就是对一块内存进行修改时,不直接在原有内存块中进行写操作,而是将内存拷贝一份,在新的内存中进行写操作,写完之后,再将原来指向的内存指针指到新的内存,原来的内存就可以被回收。
    CopyOnWriteArrayList读取是完全不用加锁的,并且更厉害的是:写入也不会阻塞读取操作,只有写入和写入之间需要进行同步等待,读操作的性能得到大幅度提升。所有可变操作(add,set等等)都是通过创建底层数组的新副本来实现的。其中,写入操作 add() 方法在添加集合的时候加了锁,保证同步,避免多线程写的时候会 copy 出多个副本。
public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {}

  1. /** The lock protecting all mutators */
  2. final transient ReentrantLock lock = new ReentrantLock();
  3. /** The array, accessed only via getArray/setArray. */
  4. private transient volatile Object[] array;

    Iterator接口实例化:
(1)public Iterator<E> iterator(); //return new COWIterator<E>(getArray(), 0);
    static final class COWIterator<E> implements ListIterator<E>
(2)public ListIterator<E> listIterator(int index);

Set


    public interface Set<E> extends Collection<E>
    Set接口仅仅对Collection所有方法进行继承而已,而自己没有扩展任何方法,Set接口与Collection接口一样,都是15个方法。Set不允许包含相同的元素,这里说的相同元素指的是用 equals() 方法比价后返回 true.


HashSet


HashSet 实现类与 Set 接口在方法上的唯一区别就是 HashSet 多了一个clone()方法。
hashSet 有以下特点:
· 不能保证元素的排列顺序,顺序有可能发生变化
· 不是同步的
· 集合元素可以是 null,但只能放入一个 null
一般操作 HashSet 还是调用 Collection 的 add / remove 等方法进行操作
public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable

  1. private transient HashMap<E,Object> map;
  2. transient Set<K> keySet;
  3. transient Collection<V> values;

    Iterator接口实例化:
(1)public Iterator iterator();// return map.keySet().iterator();
HashSet底层通过包装HashMap来实现


LinkedHashSet

TreeSet

Queue