分类 java 下的文章

反转字符串意味着按相反的顺序排列字符串的字符,也就是说,将最后一个字符放在第一位,将第二个最后一个字符放在第二位,以此类推。
本文将通过程序示例讨论在java中反转字符串的不同方法。

StringBuffer

java.lang.StringBuffer类有一个反转方法,该方法反转提供给它的字符串。

public class StringReverseDemo {
    public static void main(String[] args) {
        String str = "linuxcoming";
        // create an object of stringbuffer
        StringBuffer buffer = new StringBuffer(str);
        System.out.println("Original string: " + str);
        System.out.println("Reversed string: " + buffer.reverse());
   }
}

输出:

Original string: linuxcoming
Reversed string: gnimocxunil

- 阅读剩余部分 -

当您想要生成一个唯一的事务id或作为一个随机临时密码生成器,用户首次在网站上注册或创建防止自动输入的验证码时,通常需要生成随机的字符序列。

Java提供了许多不同的方法来编写随机字符串生成器应用程序,下面介绍几种方式。

UUID

UUID是由一组32位数的16进制数字所构成,故UUID理论上的总数为1632=2128,约等于3.4 x 1038。也就是说若每纳秒产生1兆个UUID,要花100亿年才会将所有UUID用完。

java.util.UUID 类可用于生成UUID, 它的static randomUUID方法返回一个32个字符的字符串。

import java.util.UUID;

public class RandomStringGenerator {

    public static void main(String[] args) {
        String randomString = usingUUID();
        System.out.println("Random string is: " + randomString);
        System.out.println("Random string of 8 characters is: "
            + randomString.substring(0, 8));
    }

    static String usingUUID() {
        UUID randomUUID = UUID.randomUUID();
        return randomUUID.toString().replaceAll("-", "");
    }
}
注意randomUUID方法生成的字符串包含“-”。上面的示例通过使用空字符串替换了。

以上程序输出为:

Random string is: 923ed6ec4d04452eaf258ec8a4391a0f
Random string of 8 characters is: 923ed6ec

- 阅读剩余部分 -

概览

在这个快速教程中,我们将展示如何在Maven中设置Java版本。

在继续之前,我们可以检查Maven的默认JDK版本。运行mvn -v命令将显示Maven运行的Java版本。

$ mvn -v
Apache Maven 3.6.1 (d66c9c0b3152b2e69ee9bac180bb8fcc8e6af555; 2019-04-05T03:00:29+08:00)
Maven home: /opt/apache-maven-3.6.1
Java version: 1.8.0_212, vendor: Oracle Corporation, runtime: /Library/Java/JavaVirtualMachines/jdk1.8.0_212.jdk/Contents/Home/jre
Default locale: zh_CN, platform encoding: UTF-8
OS name: "mac os x", version: "10.14.6", arch: "x86_64", family: "mac"

使用Compiler插件

我们可以在编译器插件中指定所需的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

- 阅读剩余部分 -

概述

在本文中,我们将探讨来自Java集合框架Map接口的TreeMap实现。

TreeMap它根据键的自然顺序对其元素进行排序,也可以根据用户提供了比较器排序。

在此之前,我们已经介绍了HashMap的实现,我们将认识到关于这些类如何工作的相当多的信息是类似的。

在继续阅读本文之前,强烈推荐阅读上面提到的文章。

默认排序

默认情况下,TreeMap根据其自然顺序对所有元素进行排序。对于整数,这意味着升序,对于字符串,这意味着字母顺序。

让我们看看测试中的自然顺序:

@Test
public void givenTreeMap_whenOrdersEntriesNaturally_thenCorrect() {
    TreeMap<Integer, String> map = new TreeMap<>();
    map.put(3, "val");
    map.put(2, "val");
    map.put(1, "val");
    map.put(5, "val");
    map.put(4, "val");

    assertEquals("[1, 2, 3, 4, 5]", map.keySet().toString());
}

注意,我们以非有序的方式放置了整数键,但是在检索键集时,我们确认它们确实是按升序维护的。这是整数的自然顺序。

同样的,当我们使用字符串时,它们会按自然顺序排序,即按字母顺序:

@Test
public void givenTreeMap_whenOrdersEntriesNaturally_thenCorrect2() {
    TreeMap<String, String> map = new TreeMap<>();
    map.put("c", "val");
    map.put("b", "val");
    map.put("a", "val");
    map.put("e", "val");
    map.put("d", "val");

    assertEquals("[a, b, c, d, e]", map.keySet().toString());
}

自定义排序

如果我们对TreeMap的自然顺序不满意,我们还可以在构建时通过比较器定义自己的排序规则。

在下面的例子中,我们希望整数键按降序排列:

@Test
public void givenTreeMap_whenOrdersEntriesByComparator_thenCorrect() {
    TreeMap<Integer, String> map = 
      new TreeMap<>(Comparator.reverseOrder());
    map.put(3, "val");
    map.put(2, "val");
    map.put(1, "val");
    map.put(5, "val");
    map.put(4, "val");

    assertEquals("[5, 4, 3, 2, 1]", map.keySet().toString());
}

HashMap不保证存储的键的顺序,也不保证这个顺序在一段时间内保持不变,但是TreeMap保证键总是按照指定的顺序排序。

排序的重要性

我们现在知道TreeMap以排序的顺序存储它的所有元素。我们可以执行以下查询;查找“最大”,查找“最小”,查找所有小于或大于某个值的键,等等。

以下守则只涵盖其中一小部分个案:

@Test
public void givenTreeMap_whenPerformsQueries_thenCorrect() {
    TreeMap<Integer, String> map = new TreeMap<>();
    map.put(3, "val");
    map.put(2, "val");
    map.put(1, "val");
    map.put(5, "val");
    map.put(4, "val");

    Integer highestKey = map.lastKey();
    Integer lowestKey = map.firstKey();
    Set<Integer> keysLessThan3 = map.headMap(3).keySet();
    Set<Integer> keysGreaterThanEqTo3 = map.tailMap(3).keySet();

    assertEquals(new Integer(5), highestKey);
    assertEquals(new Integer(1), lowestKey);
    assertEquals("[1, 2]", keysLessThan3.toString());
    assertEquals("[3, 4, 5]", keysGreaterThanEqTo3.toString());
}

TreeMap的内部实现

TreeMap实现了NavigableMap接口,内部基于红黑树:

public class TreeMap<K,V> extends AbstractMap<K,V>
  implements NavigableMap<K,V>, Cloneable, java.io.Serializable

红黑树的原则超出了本文的范围,但是,为了理解它们如何适应TreeMap,需要记住一些关键的事情。

首先,红黑树是由节点组成的数据结构;想象一棵倒立的芒果树,它的根在天空中,树枝向下生长。根将包含添加到树中的第一个元素。

规则是,从根节点开始,任何节点的左分支中的任何元素总是小于节点本身中的元素。右边的总是更大。如前所述,定义大于或小于的是由元素的自然顺序或构造时定义的比较器决定的。

这个规则保证了TreeMap的元素始终是有序和可预测的。

其次,红黑树是一种自平衡二叉搜索树。该属性和上述属性保证了搜索、获取、放置和删除等基本操作花费对数时间O(log n)。

自我平衡是关键。当我们不断地插入和删除条目时,想象一下这棵树一边长了,另一边短了。

这意味着一个操作在较短的分支上花费的时间较短而在离根最远的分支上花费的时间较长,这是我们不希望发生的。

因此,在红黑树的设计中要注意这一点。对于每一次插入和删除操作,树在任何边的最大高度都保持在O(log n)处,即树本身保持连续平衡。

总结

在本文中,我们探讨了Java TreeMap类的使用及其内部实现。

概述

在本文中,我们将探讨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实现。