标签 java 下的文章

示例

LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
       .maximumSize(1000)
       .expireAfterWrite(10, TimeUnit.MINUTES)
       .removalListener(MY_LISTENER)
       .build(
           new CacheLoader<Key, Graph>() {
             public Graph load(Key key) throws AnyException {
               return createExpensiveGraph(key);
             }
           });

适用性

缓存在很多场景下都是相当有用的。例如,计算或检索一个值的代价很高,并且对同样的输入需要不止一次获取值的时候,就应当考虑使用缓存。

- 阅读剩余部分 -

简介

Guava是Google开源的核心Java库,Google Guava源于2007年的"Google Collections Library"。这个库是为了方便编码,并减少编码错误。主要功能包括集合 [collections] 、缓存 [caching] 、原生类型支持 [primitives support] 、并发库 [concurrency libraries] 、通用注解 [common annotations] 、字符串处理 [string processing] 、I/O 等等。这些每天都在被Google的工程师应用在产品服务中。

优点

  • 标准化 - Guava库是由谷歌托管。
  • 高效 - 可靠,快速和有效的扩展JAVA标准库
  • 优化 -Guava库经过高度的优化。

函数式编程:增加Java功能和处理能力。

实用程序:提供经常需要在应用程序中开发的许多实用程序类。

验证:提供标准的故障安全验证机制。

最佳实践:强调最佳的做法。

总之,它是一个提高代码质量、简化工作,促使代码更有弹性、更加简洁的工具。

- 阅读剩余部分 -

介绍

构建软件项目通常包括以下任务:下载依赖项、在类路径上放置额外的JAR、将源代码编译成二进制代码、运行测试、将编译后的代码打包成可部署的构件(如JAR、WAR和ZIP文件),并将这些构件部署到应用服务器或仓库。

Apache Maven将这些任务自动化,在手工构建软件并将编译和打包代码的工作与代码构建工作分离开来的同时,将人类出错的风险降到最低。

在本教程中,我们将探索这个功能强大的工具,它使用用XML编写的中心信息—项目对象模型(Project Object Model, POM)—来描述、构建和管理Java软件项目。

为什么使用Maven

Maven的主要特性:

  • 遵循最佳实践的简单项目设置: Maven通过提供项目模板(命名原型),尽量避免配置
  • 依赖项管理: 它包括自动更新、下载和验证兼容性,以及报告依赖项闭包(也称为传递依赖项)
  • 项目依赖项和插件之间的隔离: Maven从依赖项仓库检索项目依赖项,而任何插件的依赖项都从插件仓库检索,从而在插件开始下载附加依赖项时减少冲突
  • 中央仓库系统: 可以从本地文件系统或公共仓库(如Maven central)加载项目依赖项

- 阅读剩余部分 -

概述

Lombok是一个库,它简化了许多繁琐的任务,并减少了Java源代码的冗长。当然,我们通常希望能够在IDE中使用库,这需要额外的设置。在本教程中,我们将讨论如何在两种最流行的Java ide (IntelliJ IDEA和Eclipse)中配置它。

IntelliJ IDEA

启用注释处理

Lombok通过APT使用注释处理,因此,当编译器调用它时,库根据原始文件中的注释生成新的源文件。

不过,默认情况下不启用注释处理。

因此,我们要做的第一件事是在项目中启用注释处理。

打开 Preferences -> Build, Execution, Deployment -> Compiler -> Annotation Processors 勾选以下:

* Enable annotation processing

* Obtain processors from project classpath

- 阅读剩余部分 -

JVM概述

JVM是Java Virtual Machine(Java 虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

Java语言的一个非常重要的特点就是平台无关性。而使用Java虚拟机是实现这一特点的关键。一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码。而引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。这就是Java的能够“一次编译,到处运行”的原因。

JVM总体上是由类装载子系统(ClassLoader)、运行时数据区、执行引擎、垃圾收集这四个部分组成。其中我们最为关注的运行时数据区,也就是JVM的内存部分则是由方法区(Method Area)、JAVA堆(Java Heap)、虚拟机栈(JVM Stack)、程序计数器、本地方法栈(Native Method Stack)这几部分组成。

JVM体系结构

jvm

- 阅读剩余部分 -

经常编写markdown需要引入一些代码块,需要定义Markdown支持的语言列表,这里列一下,需要的时候方便查找

NameMarkdown Label
.NET Consoledotnetcli
ASP.NET (C#)aspx-csharp
ASP.NET (VB)aspx-vb
AzCopyazcopy
Azure CLIazurecli
Azure PowerShellazurepowershell
C++cpp
C++/CXcppcx
C++/WinRTcppwinrt
C#csharp
C# in browsercsharp-interactive
Consoleconsole
CSHTMLcshtml
DAXdax
Dockerdockerfile
F#fsharp
Gogo
HTMLhtml
HTTPhttp
Javajava
JavaScriptjavascript
JSONjson
Kusto Query Languagekusto
Markdownmd
Objective-Cobjc
ODataodata
PHPphp
PowerApps (dot decimal separator)powerapps-dot
PowerApps (comma decimal separator)powerapps-comma
PowerShellpowershell
Pythonpython
Q#qsharp
Rr
Rubyruby
SQLsql
Swiftswift
TypeScripttypescript
VBvb
XAMLxaml
XMLxml

- 阅读剩余部分 -

概述

在本文中,我们将探讨LinkedHashMap类的内部实现。LinkedHashMap是Map接口的一种常见实现。

这个特定的实现是HashMap的子类,因此共享HashMap实现的核心构建块。因此,强烈建议您在阅读本文之前复习一下这方面的知识。

LinkedHashMap vs HashMap

LinkedHashMap类在大多数方面与HashMap非常相似。但是,LinkedHashMap同时基于哈希表和链表,以增强哈希映射的功能。

它维护一个双链表,除了默认大小为16的底层数组之外,还运行所有条目。

为了维护元素的顺序,LinkedHashMap通过修改HashMap的Map.Entry添加指向下一个和前一个条目的指针:

static class Entry<K,V> extends HashMap.Node<K,V> {
    Entry<K,V> before, after;
    Entry(int hash, K key, V value, Node<K,V> next) {
        super(hash, key, value, next);
    }
}

注意,Entry类只添加了两个指针;在此之前和之后,它可以将自己挂接到链表上。除此之外,它还使用HashMap的入口类实现。

最后,请记住这个链表定义了迭代的顺序,默认情况下是元素插入的顺序(插入顺序)。

插入顺序

让我们来看看一个LinkedHashMap实例,它根据条目如何插入到映射中来对其排序。它还保证在实例的整个生命周期内维持这一秩序:

@Test
public void givenLinkedHashMap_whenGetsOrderedKeyset_thenCorrect() {
    LinkedHashMap<Integer, String> map = new LinkedHashMap<>();
    map.put(1, null);
    map.put(2, null);
    map.put(3, null);
    map.put(4, null);
    map.put(5, null);

    Set<Integer> keys = map.keySet();
    Integer[] arr = keys.toArray(new Integer[0]);

    for (int i = 0; i < arr.length; i++) {
        assertEquals(new Integer(i + 1), arr[i]);
    }
}

在这里,我们只是对LinkedHashMap中的元素排序进行了初步的、非结论性的测试。

我们可以保证这个测试将始终通过,因为插入顺序将始终保持不变。但HashMap则不行。

如果客户需要在调用API之前以相同的方式对返回的map进行排序,那么可以使用LinkedHashMap。

如果一个键被重新插入到map中,则插入顺序不受影响。

若不想使用默认顺序

LinkedHashMap提供了一个特殊的构造函数,使我们能够在自定义负载因子(LF)和初始容量之间指定一种称为访问顺序的不同排序机制/策略:

LinkedHashMap<Integer, String> map = new LinkedHashMap<>(16, .75f, true);
第一个参数是初始容量,然后是负载系数,最后一个参数是排序模式。所以,通过传入true,我们得到了访问顺序,而默认的是插入顺序。

此机制确保元素的迭代顺序是上次访问元素的顺序,从最近最少访问到最近最多访问。

因此,构建一个最近最少使用的(LRU)缓存是非常简单和实用:

@Test
public void givenLinkedHashMap_whenAccessOrderWorks_thenCorrect() {
    LinkedHashMap<Integer, String> map 
      = new LinkedHashMap<>(16, .75f, true);
    map.put(1, null);
    map.put(2, null);
    map.put(3, null);
    map.put(4, null);
    map.put(5, null);

    Set<Integer> keys = map.keySet();
    assertEquals("[1, 2, 3, 4, 5]", keys.toString());
  
    map.get(4);
    assertEquals("[1, 2, 3, 5, 4]", keys.toString());
  
    map.get(1);
    assertEquals("[2, 3, 5, 4, 1]", keys.toString());
  
    map.get(3);
    assertEquals("[2, 5, 4, 1, 3]", keys.toString());
}

请注意,当我们在map上执行访问操作时,如何转换map中元素的顺序, map上的任何访问操作,则被访问的元素将最后出现。

迭代map不会影响顺序;只有访问操作才会影响顺序。

LinkedHashMap还提供了一种机制,用于维护固定数量的元素,并在需要添加新元素时不断删除最旧的元素。

可以重写removeeldestentry方法以强制此策略自动删除过时的元素。

为了在实践中看到这一点,让我们创建自己的LinkedHashMap类,通过扩展LinkedHashMap强制删除过时的元素:

public class MyLinkedHashMap<K, V> extends LinkedHashMap<K, V> {

    private static final int MAX_ENTRIES = 5;

    public MyLinkedHashMap(
      int initialCapacity, float loadFactor, boolean accessOrder) {
        super(initialCapacity, loadFactor, accessOrder);
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry eldest) {
        return size() > MAX_ENTRIES;
    }

}

上面的覆盖将允许map增长到最大的5个条目。当大小超过这个值时,每个新元素将被插入,代价是丢失map中最老的元素,即最后访问时间先于所有其他元素的元素:

@Test
public void givenLinkedHashMap_whenRemovesEldestEntry_thenCorrect() {
    LinkedHashMap<Integer, String> map
      = new MyLinkedHashMap<>(16, .75f, true);
    map.put(1, null);
    map.put(2, null);
    map.put(3, null);
    map.put(4, null);
    map.put(5, null);
    Set<Integer> keys = map.keySet();
    assertEquals("[1, 2, 3, 4, 5]", keys.toString());
  
    map.put(6, null);
    assertEquals("[2, 3, 4, 5, 6]", keys.toString());
  
    map.put(7, null);
    assertEquals("[3, 4, 5, 6, 7]", keys.toString());
  
    map.put(8, null);
    assertEquals("[4, 5, 6, 7, 8]", keys.toString());
}

性能

与HashMap一样,只要哈希函数良好,LinkedHashMap就可以在恒定的时间内执行添加、删除和包含基本操作。它还接受空键和空值。

但是,LinkedHashMap的这种常量时间性能可能比HashMap的常量时间性能稍差,这是由于维护一个双链表增加了开销。

对LinkedHashMap集合视图的迭代也花费与HashMap类似的线性时间O(n)。另一方面,LinkedHashMap在迭代期间的线性时间性能优于HashMap的线性时间。

这是因为,对于LinkedHashMap, O(n)中的n只是映射中条目的数量,而与容量无关。而对于HashMap, n是容量,大小相加为O(大小+容量)。

负载因子和初始容量被精确地定义为HashMap。但是,请注意,对于初始容量选择过高的惩罚对于LinkedHashMap来说没有HashMap那么严重,因为该类的迭代时间不受容量的影响。

并发

与HashMap一样LinkedHashMap也是不同步的。因此,如果您要从多个线程访问它,并且其中至少有一个线程可能会从结构上更改它,那么它必须是外部同步的。

最好在创建时这样做:

Map m = Collections.synchronizedMap(new LinkedHashMap());
与HashMap的区别在LinkedHashMap仅仅调用get就会导致结构修改。除此之外,还有put和remove之类的操作。

总结

在本文中,我们探讨了Java LinkedHashMap类作为Map接口的最重要实现之一的用法。我们还探讨了它与HashMap的区别,HashMap是它的超类。

希望在阅读完这篇文章后,您可以做出更明智和有效的决定,在您的用例中使用哪种map实现。