基础知识

打乱List集合元素顺序

       List<Integer> list = new ArrayList<Integer>();
        list.add(1);
        list.add(2);
        list.add(3);
        Collections.shuffle(list);

classpath

在这里插入图片描述

public static void main(String[] args) {
//getResource方法中的参数不以’/'开头时,是从ClassPath根下获取;
	URL resource =  DispatcherServlet.class.getClassLoader().getResource("");
	System.out.println(resource);
}
输出:
file:/D:/my-project-demo/springmvc-ljw/mvc-demo-ljw/target/classes/

Properties

在这里插入图片描述

private final String handlerAdapter = "spring.bean.handlerAdapter";
Properties  prop = new Properties();
InputStream is = DispatcherServlet.class.getResourceAsStream("/application.properties");
prop.load(is);
 //从字节输入流中读取键值对
String className = prop.getProperty(handlerAdapter);

1. Class.getResourceAsStream(String path):
path 不以’/'开头时默认是从此类所在的包下取资源,以’/'开头则是从ClassPath根下
获取。其只是通过path构造一个绝对路径,最终还是由ClassLoader获取资源。

2. Class.getClassLoader.getResourceAsStream(String path):
默认则是从ClassPath根下获取,path不能以’/'开头,最终是由ClassLoader获取资源。

3. ServletContext. getResourceAsStream(String path):
默认从WebAPP根目录下取资源,Tomcat下path是否以’/'开头无所谓,当然这和具体的容器实现有关。

静态代码块

  类的Static代码块,也就是静态代码块,只会执行一次,是在类被加载的时候执行。因
为每个类只会被加载一次,所以静态代码块也只会被执行一次。而构造方法每次生成一
个对象的时候都会调用类的构造方法,所以new一次就会调用构造方法一次。静态代码
块的作用也是完成一些初始化工作。首先执行静态代码块,然后执行构造方法。静态代
码块在类被加载时候执行,而构造方法是在生成对象时候执行;要想调用某个类来生成
对象,首先需要将类加载到Java虚拟机上(JVM),然后由JVM加载这个类来生成对象。

  如果继承体系中既有构造方法,又有静态代码块,那么首先执行最顶层的类的静态代码
块,一直执行到最底层的类的静态代码块。然后再去执行最顶层的类的构造方法,一直执
行到最底层的类的构造方法。注意:静态代码块只会执行一次。

 不能在静态方法中访问非静态成员变量;可以在静态方法中访问静态成员变量。可以在
非静态方法中访问静态的成员变量。不能在静态方法中使用this关键字。

静态变量

静态变量  被赋值后   保存全局,   任何地方都可以被使用.
静态变量  可以  被修改.
静态变量线程不安全.

一种是被static关键字修饰的变量,叫类变量或者静态变量
类的静态变量在内存中只有一个,java虚拟机在加载类的过程中为静态变量分配内存,静
态变量位于方法区,被类的所有实例共享。静态变量可以直接通过类名进行访问,其生命
周期取决于类的生命周期。
   而实例变量取决于类的实例。每创建一个实例,java虚拟机就会为实例变量分配一次
内存,实例变量位于堆区中,其生命周期取决于实例的生命周期。

JAVA中初始化的顺序:
        加载类;
        静态变量初始化;
        静态块;【其只能调度静态的,不能调度非静态的】
        成员变量;
        构造方法; 

public class ServerBean {
    private String name;
    private int port;
    public void injectionConf(){
        new ServerConf(this.name,this.port);
    }
set和get
}

public class ServerConf {
    private static ServerConf serverConf;

    private String serverName;
    private int port;

    public ServerConf(String serverName, int port) {
        this.serverName = serverName;
        this.port = port;
        serverConf = this;
    }
    public static ServerConf getServerConf() {
        return serverConf;
    }
}

public class ServerInitializer {
    private Map<String, Object> rpcServiceMap;

    public ServerInitializer(Map<String, Object> rpcServiceMap) {
        this.rpcServiceMap = rpcServiceMap;
    }

    public void init() throws Exception {
        ServerConf serverConf = ServerConf.getServerConf();//获取静态变量
        System.out.println("-------"+ JSON.toJSONString(serverConf));
    }
}

@Test
public void test() throws Exception {
    ServerBean serverBean = new ServerBean();
    serverBean.setName("uiuiu");
    serverBean.setPort(1235);

    serverBean.injectionConf();//给静态变量赋值

    new ServerInitializer(new HashMap<String, Object>()).init();
}

内部类(内部类最大的优点就在于它能够非常好的解决多重继承的问题)

为什么使用内部类?
使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无
论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
  在我们程序设计中有时候会存在一些使用接口很难解决的问题,这个时候我们可以利用
内部类提供的、可以继承多个具体的或者抽象的类的能力来解决这些程序设计问题。可
以这样说,接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整。
特性:
   1、内部类可以用多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立。
    2、在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承同一个类。
    3、创建内部类对象的时刻并不依赖于外围类对象的创建。
    4、内部类是一个独立的实体。
    5、内部类提供了更好的封装,除了该外围类,其他类都不能访问。

(1).静态内部类(也是一种懒加载方式)
使用static修饰的内部类我们称之为静态内部类,不过我们更喜欢称之为嵌套内部类。静
态内部类与非静态内部类之间存在一个最大的区别,我们知道非静态内部类在编译完成之
后会隐含地保存着一个引用,该引用是指向创建它的外围类,但是静态内部类却没有。没
有这个引用就意味着:
      1 它的创建是不需要依赖于外围类的。
      2 它不能使用任何外围类的非static成员变量和方法。
•只有真正调用getInstance()才会加载静态内部类。加载类时是线程安全的。instance
是static final 型(final可加可不加),保证了只有一个实例存在,而且只能被赋值
一次,从而保证了线程安全。
•兼备了并发高效调用和延迟加载的优势!

public class StaticInnerSingleton {
    public StaticInnerSingleton() {}
    public static  StaticInnerSingleton getInstance() {
        return SingletonClassInstance.instance;
    }
      private static class SingletonClassInstance{
        static {
            System.out.println("先执行?");
        }
        private static final StaticInnerSingleton instance = new StaticInnerSingleton();
    }
}

在这里插入图片描述

(2).接口中使用内部类
public interface Encoder {
  Type MAP_STRING_WILDCARD = Util.MAP_STRING_WILDCARD;
  void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException;

  class Default implements Encoder {
    @Override
    public void encode(Object object, Type bodyType, RequestTemplate template) {
      if (bodyType == String.class) {
        template.body(object.toString());
      } else if (bodyType == byte[].class) {
        template.body((byte[]) object, null);
      } else if (object != null) {
        throw new EncodeException(
            format("%s is not a type supported by this encoder.", object.getClass()));
      }
    }
  }
}
//使用
Encoder encoder = new Encoder.Default();

创建对象的五种方式

1、使用new关键字
这是最常见也是最简单的创建对象的方式了。通过这种方式,我们可以调用任意的构造函
(无参的和带参数的)

在这里插入图片描述

2、使用Class类的newInstance方法
我们也可以使用Class类的newInstance方法创建对象。这个newInstance方法调用无参的构造函数创建对象。
我们可以通过下面方式调用newInstance方法创建对象:

在这里插入图片描述

3、使用Constructor类的newInstance方法
和Class类的newInstance方法很像, java.lang.reflect.Constructor类里也有一个newInstance方法可以创建对象。我们可以通过这个newInstance方法调用有参数的和私有的构造函数。

在这里插入图片描述

4、使用clone方法
无论何时我们调用一个对象的clone方法,jvm就会创建一个新的对象,将前面对象的内容全部拷贝进去。用clone方法创建对象并不会调用任何构造函数。
要使用clone方法,我们需要先实现Cloneable接口并实现其定义的clone方法。

在这里插入图片描述

5、使用反序列化
当我们序列化和反序列化一个对象,jvm会给我们创建一个单独的对象。在反序列化时,jvm创建对象并不会调用任何构造函数。
为了反序列化一个对象,我们需要让我们的类实现Serializable接口。

在这里插入图片描述
我们从上面的字节码片段可以看到,除了第1个方法,其他4个方法全都转变为invokevirtual(创建对象的直接方法),第一个方法转变为两个调用,new和invokespecial(构造函数调用)。

File和路径的获取

String name = JavaPage.class.getPackage().getName();   // com.ljw09

InputStream inputStream = new FileInputStream(File file);
InputSource inputSource = new InputSource(file.toURI().toURL().toString());
inputSource.getSystemId() // file:/D:/源码/tomcat/apache-tomcat-8.5.39-src/catalina-home/conf/server.xml

xml解析(JDK原生方式)

org.xml.sax.XMLReader  

com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl.JAXPSAXParser#parse(org.xml.sax.InputSource)
com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser#parse(org.xml.sax.InputSource)

在这里插入图片描述

XMLInputSource xmlInputSource =new XMLInputSource(inputSource.getPublicId(),inputSource.getSystemId(),null);
xmlInputSource.setByteStream(inputSource.getByteStream());
xmlInputSource.setCharacterStream(inputSource.getCharacterStream());
xmlInputSource.setEncoding(inputSource.getEncoding());
parse(xmlInputSource);

com.sun.org.apache.xerces.internal.parsers.XML11Configuration#parse(com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource)

Resource资源文件路径的加载问题

(1)	可以通过类的class文件路径获知当前项目或者编译文件的路径

在这里插入图片描述

System.out.println(MemberLotteryController.class.getResource(""));
file:/D:/ideaSpase/julu-other/applet/applet-serviceimpl/target/classes/com/bizvane/appletserviceimpl/controllers/

System.out.println(MemberLotteryController.class.getResource("/"));
file:/D:/ideaSpase/julu-other/applet/applet-serviceimpl/target/classes/

System.out.println(MemberLotteryController.class.getResource("/bootstrap.properties"));
file:/D:/ideaSpase/julu-other/applet/applet-serviceimpl/target/classes/bootstrap.properties

在Class类里面有这样一个方法getResource(String name) 查找带有给定名称的资源获取一个URL
public static void main(String[] args) throws IOException {
        //获取当前类class所在的resource路径
        System.out.println(TestFilePath.class.getResource("/").getPath());
        //获取当前类class所在的路径:
        System.out.println(TestFilePath.class.getResource("").getPath());
    }
/E:/develop/workspace/selfproject/nutrition/target/test-classes/
/E:/develop/workspace/selfproject/nutrition/target/test-classes/cn/test/
获取这个路径后还要直接拼接资源文件路径,可以直接通过TestFilePath.class.getResource("/request-mapping.properties").getPath()方式获取
(2)、通过new File("")来确定工程目录(非编译运行文件的目录)
  public static void main(String[] args) throws IOException {
        File f = new File("");
        System.out.println("空字符file的Path : " + f.getPath());
        System.out.println("空字符file的标准路径CanonicalPath : " + f.getCanonicalPath());
        System.out.println("空字符file的绝对路径AbsolutePath : " + f.getAbsolutePath());
    }
代码输出结果如下,可以根据资源文件在工程目录对应的路径进行资源路径的拼接,从而完成资源的定位(同时也可以通过直接new File(path)的方式获取文件,其中path是相对于工程目录的路径)
空字符file的Path : 
空字符file的正则路径CanonicalPath : E:\develop\workspace\selfproject\nutrition
空字符file的绝对路径AbsolutePath : E:\develop\workspace\selfproject\nutrition
(3)、通过类加载的路径来获取对应的路径信息
System.out.println(MemberLotteryController.class.getClassLoader().getResource(""));
file:/D:/ideaSpase/julu-other/applet/applet-serviceimpl/target/classes/
System.out.println(MemberLotteryController.class.getClassLoader().getResource("/"));
null
System.out.println(MemberLotteryController.class.getClassLoader().getResource("/bootstrap.properties"));
null
 
    public static void main(String[] args) throws IOException {
        URL xmlpath = new TestFilePath().getClass().getClassLoader().getResource("/"); 
        URL xmlpath1 = new TestFilePath().getClass().getClassLoader().getResource("");
        System.out.println("获取当前类被加载的工程路径:" + xmlpath);
        System.out.println("获取当前类被加载的路径:" + xmlpath1);
    }
代码输出结果如下,结果说明类加载获取resource时只能用getResource(""),这是因为加载类的路径就默认是在根目录下的,加斜杠反而无法获取路径(将斜杠当成资源名称了)
获取当前类被加载的工程路径:null
获取当前类被加载的路径:file:/E:/develop/workspace/selfproject/nutrition/target/test-classes/
(4)、直接通过java的系统System类获取项目路径
  public static void main(String[] args) throws IOException {
        System.out.println(System.getProperty("user.dir"));
    }
代码输出结果如下,获取的是项目的根目录
E:\develop\workspace\selfproject\nutrition
(5).java获取resource文件
	war包

Thread.currentThread().getContextClassLoader()  
URL l1 =   
Thread.currentThread().getContextClassLoader().getResource("readFile/test1.xml");  
System.out.println(l1);  
  
URL l2 =   
Thread.currentThread().getContextClassLoader().getResource("collection/test2.xml");  
System.out.println(l2);  
  
URL l3 = Thread.currentThread().getContextClassLoader().getResource("test3.xml");  
String l4=l3.getPath();//加上getPath()则去掉前面的file:  

	jar包
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("conf/job_two_one_mapping.txt");  
try(Scanner scanner = new Scanner(is)) {  
    while (scanner.hasNextLine()) {  
        System.out.println(scanner.nextLine());  
    }  
} catch (Exception e) {  
    log.error("读取文件数据异常" ,e);  
}  

Java中的基点有哪些呢?大致总结一下有以下几种:
1)classpath
如果你要找的资源在classpath下,那么通过classpath这个基点是比较合适的,而取得
这个基点方式主要是通过ClassLoader来,具体方法就是 ClassLoader.getResource(String name),而取得ClassLoader的方式很多,比如:
	1.Thread.currentThread().getContextClassLoader()
	2.clazz.getClassLoader()
	3.ClassLoader. getSystemClassLoader()
   ClassLoader找resource的实现原理就是先递归在parent classLoader中从所在
   classpath里加载resource(最终如何加载JDK未开源),如果所有层级的
   classLoader都未找到,则调用findResource方法来找,而这个方法是暴露给自制
   classLoader来现实的,因此给了在classpath之外加载resource的机会。
2 当前用户目录
就是相对于System.getProperty("user.dir" )返回的路径, 对于一般项目,这是项目的根路径。对于JavaEE服务器,这可能是服务器的某个路径。这个并没有统一的规范! 然而, 默认情况下,java.io 包中的类总是根据当前用户目录来分析相对路径名,如new File("xxx"),就是在 System.getProperty("user.dir" )路径下找xxx文件。因此,通过这种方式来定位文件可能会出现移植问题。
3 Web应用程序的根目录
在Web应用程序中,我们一般通过ServletContext.getRealPath("/" )方法得到Web应用程序的根目录的绝对路径。
(6). getResourceAsStream
MemberLotteryController.class.getResourceAsStream("")//获取类所在包的绝对路径
D:\ideaSpase\julu-other\applet\applet-serviceimpl\target\classes\com\bizvane\appletserviceimpl\controllers 
MemberLotteryController.class.getResourceAsStream("/")
            D:\ideaSpase\julu-other\applet\applet-serviceimpl\target\classes
MemberLotteryController.class.getResourceAsStream("/bootstrap.properties")
            D:\ideaSpase\julu-other\applet\applet-serviceimpl\target\classes\bootstrap.properties

什么是桥接方法

什么是桥接方法
桥接方法是 JDK 1.5 引入泛型后,为了使Java的泛型方法生成的字节码和 1.5 版本前的字节码相兼容,由编译器自动生成的方法。
我们可以通过Method.isBridge()方法来判断一个方法是否是桥接方法,在字节码中桥接方法会被标记为ACC_BRIDGE和ACC_SYNTHETIC,其中ACC_BRIDGE用于说明这个方法是由编译生成的桥接方法,ACC_SYNTHETIC说明这个方法是由编译器生成,并且不会在源代码中出现。

什么时候会生成桥接方法
就是说一个子类在继承(或实现)一个父类(或接口)的泛型方法时,在子类中明确指定了泛型类型,那么在编译时编译器会自动生成桥接方法(当然还有其他情况会生成桥接方法,这里只是列举了其中一种情况)。如下所示:

在这里插入图片描述
在这里插入图片描述

SubClass只声明了一个方法,而从字节码可以看到有三个方法,第一个是无参的构造方法(代码中虽然没有明确声明,但是编译器会自动生成),第二个是我们实现的接口中的方法,第三个就是编译器自动生成的桥接方法。桥接方法实际是是调用了实际的泛型方法。

SubClass只声明了一个方法,而从字节码可以看到有三个方法,第一个是无参的构造方法(代码中虽然没有明确声明,但是编译器会自动生成),第二个是我们实现的接口中的方法,第三个就是编译器自动生成的桥接方法。可以看到flags包括了ACC_BRIDGE和ACC_SYNTHETIC,表示是编译器自动生成的方法,参数类型和返回值类型都是Object。再看这个方法的字节码,它把Object类型的参数强制转换成了String类型,再调用在SubClass类中声明的方法,转换过来其实就是:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Method.getDeclaring Class()类的Class对象

java.lang.reflect.Method.getDeclaringClass()方法  //返回表示声明由此Method对象表示的方法的类的Class对象。
public static void main(String[] args) { 
Method[] methods = SampleClass.class.getMethods(); 
Class declaringClass = methods[0].getDeclaringClass(); 
System.out.println(declaringClass.getName()); 
} 

.Java 8 的 default 方法特性,Java 8 对 Map 增加了不少实用的默认方法

putIfAbsent 方法
其实简单的说:
       传统的put方法,只要key存在,value值就会被覆盖,注意put方法返回的是put之前的值,如果无put之前的值返回null
       putIfAbsent方法,只有在key不存在或者key为null的时候,value值才会被覆盖  .
putIfAbsent(K key, V value): 根据key匹配Node,如果匹配不到则增加key-value,返回null,如果匹配到Node,如果oldValue不等于null则不进行value覆盖
,返回oldValue

在这里插入图片描述
在这里插入图片描述

Map<Integer,String> map = new HashMap<Integer,String>();

for(int i=0; i<6; i++){
    map.put(i,"val_"+i);
}

map.put(10,null);

 //3:V putIfAbsent(K key, V value):根据key匹配Node,如果匹配不到则增加key-value,返回null,如果匹配到Node,如果oldValue不等于null则不进行value覆盖,返回oldValue
System.out.println(map.putIfAbsent(3,"val_66"));//val_3
System.out.println(map.putIfAbsent(10,"val_66"));//null
System.out.println(map.putIfAbsent(11,"val_66"));//null
System.out.println(map.get(3)+"--"+map.get(10)+"--"+map.get(11));//val_3--val_66--val_66
getOrDefault
//1:遍历
map.forEach((key,value) -> System.out.println(key+":"+value));

//2:V getOrDefault(key,defaultValue):获取key值,如果key不存在则用defaultValue
System.out.println("3-->"+map.getOrDefault(3,"val_66"));//3-->val_3
System.out.println("10-->"+map.getOrDefault(10,"val_66"));//10-->null
System.out.println("11-->"+map.getOrDefault(11,"val_66"));//11-->val_66


computeIfAbsent 方法
根据key匹配,参数为key,存在且value不为null,不做修改,为null用返回值作为value,不存在则新增
/** 9:
 * computeIfAbsent(K key,Functionsuper K, ? extends V> mappingFunction):
 * 根据key做匹配Node,(匹配不到则新建然后重排)
 * 如果Node有value,则直接返回oldValue,
 * 如果没有value则根据Function接口的apply方法获取value,返回value。
 * Function接口的apply的入参为key,调用computeIfAbsent时重写Function接口可以根据key进行逻辑处理,
 * apply的返回值即为要存储的value。
 */
System.out.println("----------------------computeIfAbsent------------------------");
map.put(8,null);
System.out.println(map.toString());//{0=val_0, 1=val_1, 2=val_2, 3=val_3, 4=val_4, 5=val_5, 8=null, 10=700000}
System.out.println(map.computeIfAbsent(0,key -> key+"000"));//val_0  -》key值存在,直接返回oldValue
System.out.println(map.computeIfAbsent(7,key -> "value_"+key));//value_7 -》key匹配不到,直接新增,返回值为value
System.out.println(map.computeIfAbsent(8,key -> "88"));//88 -》key匹配到了,value为null,返回值作为value
System.out.println(map.toString());//{0=val_0, 1=val_1, 2=val_2, 3=val_3, 4=val_4, 5=val_5, 7=value_7, 8=88, 10=700000}

computeIfPresent 方法
key,value作为参数,存在,原来的值为null不做操作,否则返回值作为新的value覆盖原来;不存在,不做操作;返回值为null删除该节点

/** 10:
 * V computeIfPresent(K key,BiFunction remappingFunction)
 * 根据key做匹配,如果匹配不上则返回null,匹配上根据BiFunction的apply方法获取value,返回value。
 * BiFunction接口的apply的入参为key、oldValue,调用computeIfPresent时重写Function接口
 * 可以根据key和oldValue进行逻辑处理,apply的返回值如果为null则删除该节点,否则即为要存储的value。
 */
map.remove(7);
map.remove(8);
map.replace(10,null);
map.remove(0,"val_0");//value匹配到了删除
map.remove(1,"val_0");//value匹配失败,不会删除
System.out.println(map.toString());//{1=val_1, 2=val_2, 3=val_3, 4=val_4, 5=val_5, 10=null}
System.out.println(map.computeIfPresent(3,(key,value) -> key+":"+value));//3:val_3 -》key存在,根据返回值修改value
System.out.println(map.computeIfPresent(0,(key, value) -> "0000"));//null -》key不存在,返回null,不做任何操作
System.out.println(map.computeIfPresent(1,(key, value) -> null));//null -》key存在,根据返回值修改value
System.out.println(map.computeIfPresent(10,(key,value) -> "val_10"));//null -》oldValue值为null,删除节点
System.out.println(map.toString());//{2=val_2, 3=3:val_3, 4=val_4, 5=val_5, 10=null}

replace
//5:boolean replace(K key, V oldValue, V newValue):根据key匹配node,如果value也相同则使用newValue覆盖返回true,否则返回false
map.put(11,null);
map.replace(3,"3","33");
map.replace(10,"val_66","val_666666");
map.replace(11,null,"val_11");
map.replace(11,null,"val_11");
System.out.println(map.toString());//{0=val_0, 1=val_1, 2=val_2, 3=val_3, 4=val_4, 5=val_5, 10=val_666666, 11=val_11}

replaceAll
/** 6:
 * void replaceAll(BiFunction function):调用此方法时重写BiFunction的Object apply(Object o, Object o2)方法,
 * 其中o为key,o2为value,根据重写方法逻辑进行重新赋值。
 */
map.replaceAll((key,value) -> {
    if(key == 2){
        return value+"222";
    }
    return value;
});
System.out.println(map.toString());//{0=val_0, 1=val_1, 2=val_2222, 3=val_3, 4=val_4, 5=val_5, 10=val_666666, 11=val_11}

compute
根据key做匹配,key,value为参数,匹配到Node做value替换,匹配不到新增node。apply的返回值为null则删除该节点。

/** 7:
 * V compute(K key,BiFunction remappingFunction):根据key做匹配,根据BiFunction的apply返回做存储的value。
 * 匹配到Node做value替换,匹配不到新增node。apply的返回值如果为null则删除该节点,否则即为要存储的value。
 */
System.out.println("---------------------- compute -----------------------");
System.out.println(map.compute(3,new BiFunction() {
    @Override
    public Object apply(Object key, Object value) {
        return key+":"+value;
    }
}));//3:val_3 -》用返回值覆盖原来的值,这里用了java7的编码方式,以下均采用java8的lanbda表达式
System.out.println(map.compute(10,(key,value) -> {return value.split("_")[1];}));//666666 -》用返回值覆盖原来的值
System.out.println(map.compute(6,(key,value) ->  null));//null -》返回值为null,则删除该key值
System.out.println(map.toString());//{0=val_0, 1=val_1, 2=val_2, 3=3:val_3, 4=val_4, 5=val_5, 10=666666, 11=val_11}


merge
ldValue,newValue作为为参数,其它功能于compute类似

/** 8:
 * merge(K key, V value,BiFunctionsuper V, ? super V, ? extends V> remappingFunction):
 * 功能大部分与compute相同,不同之处在于BiFunction中apply的参数,入参为oldValue、value,
 * 调用merge时根据两个value进行逻辑处理并返回value。
 */
System.out.println(map.merge(3,"val_3",(value,newValue) -> newValue));//val_3  --》返回值覆盖原来的value
System.out.println(map.merge(10,"33334",(a,b) -> (Integer.valueOf(a)+Integer.valueOf(b))+""));//700000
System.out.println(map.merge(8,"88",(oldValue,newValue) -> oldValue+newValue));//88 -》key不存在则新增
System.out.println(map.merge(11,"11",(old,newValue) -> null));//null -》返回值为null,删除该节点
System.out.println(map.toString());//{0=val_0, 1=val_1, 2=val_2, 3=val_3, 4=val_4, 5=val_5, 8=88, 10=700000}


remove
//4:boolean remove(Object key, Object value):根据key匹配node,如果value也相同则删除
System.out.println(map.size());//8
map.remove(10,"66");
map.remove(11,"val_66");
System.out.println(map.size());//7
System.out.println(map.toString());//{0=val_0, 1=val_1, 2=val_2, 3=val_3, 4=val_4, 5=val_5, 10=val_66}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值