linuxcoming 发布的文章

某些Http Proxy或者Http Server不支持Patch操作,这时我们就可以使用Http Post操作来模拟Http Patch操作,仅仅需要在Http Header中添加 X-HTTP-Method-Override Header来指定目标Http Method即可, 例如:

使用jQuery通过Post模拟Patch接口:

$.ajax({
  headers: {
    'X-HTTP-Method-Override': 'Patch'
  },
  method: 'Post'
  // more parameters...
});

- 阅读剩余部分 -

最近开始准备整理一份完整的Linux/FreeBSD的PXE安装教程,先从基础设施VirtualBox安装开始,便于我们模拟在真实场景下远程装机的功能.

1.下载软件

建议直接从官网下载软件,下载软件的同时可以关注下官方一些文档,以及最近的版本情况,甚至你还可以关注下目前版本对PXE的支持情况

(1).Windows下载地址

https://download.virtualbox.org/virtualbox/6.0.4/VirtualBox-6.0.4-128413-Win.exe

安装过程没太多需要注意的,一步一步确认就好了.

VirtualBox安装

(2).Linux下载地址

https://www.virtualbox.org/wiki/Linux_Downloads

- 阅读剩余部分 -

我个人电脑使用的是Gentoo Linux, 其他Linux发行版本同样适用下面的方法.

在我们开始备份之前,我们需要对备份的环境做一些说明,开源社区一般都建议将Linux系统做如下分区:

  • /boot 使用独立分区也可以不分区(分区的好处是根分区有问题的话, 内核相关的启动代码不会丢失)
  • / 根分区使用独立分区
  • /opt 使用独立分区
  • /home 使用独立分区
  • /var使用独立分区

使用社区建议能最大限度保存你环境, 做好备份的话, 能很快恢复系统, 即使硬盘有坏道的情况下.

- 阅读剩余部分 -

很长时间没有升级Gentoo Linux了, 手痒执行了一次全系统更新, 发现比以前的升级编译的时间长了非常多.

看了下log, 花时间最多的地方在gcc/mesa/llvm这三个包, 顺带研究了下gcc/llvm的关系,又使用equery 检查了下llvm包的依赖关系, 整个系统只有mesa一个包直接依赖llvm,对于个人用户, 建议还是果断将 -llvm use 标记添加到/etc/portage/make.conf, 减少升级编译的时间.除非你的工作确实需要llvm.以下是我优化后的mesa包的use标记列表:

下面是我的/etc/portage/make.conf中的USE变量配置的内容,我比较喜欢mate的简单,所以qt/kde等等都被移除了.

USE="${USE} systemd -samba ipv6 -cups -qt4 -ldap -gnome -kde jpeg jpeg2k tk -llvm"

- 阅读剩余部分 -

概述

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

概述

java.util.concurrent包提供了创建并发应用程序的工具。

java.util.concurrent并发包含太多的特性,不可能在一篇文章中讨论。在本文中,我们将主要关注这个包中一些最有用的实用工具,如:

  • Executor
  • ExecutorService
  • ScheduledExecutorService
  • Future
  • CountDownLatch
  • CyclicBarrier
  • Semaphore
  • ThreadFactory
  • BlockingQueue
  • DelayQueue
  • Locks
  • Phaser

- 阅读剩余部分 -

本地修改了一堆文件,没有git add到暂存区时, 如果需要撤销修改, 可以参考下述操作:

单个文件/文件夹

$ git checkout -- filename

所有文件/文件夹

$ git checkout .

本地新增了一堆文件,没有git add到暂存区, 如果需要去掉新增文件:

单个文件/文件夹

$ rm filename / rm dir -rf

所有文件/文件夹

$ git clean -xdf

- 阅读剩余部分 -