java无回显的密码输入

在java6中有一个新类Console,能够实现无回显输入,用于安全输入!以下是代码样例:

import java.io.*;
import java.util.*;
public class ConsoleDemo {
    public static void main(String[] args) {
        Console console = System.console();
        if (console == null) {
            System.err.println("Failed to get console!");
            return;
        }
        String name = console.readLine("Please type your name:");
        char[] password1 = null;
        char[] password2 = null;
        console.printf("Your name is %s\n", name);
        password1 = console.readPassword("Please type your password:");
        password2 = console.readPassword("Please type your password again:");
        if (!Arrays.equals(password1, password2)) {
            System.err.println("Password doesn't match the confirmation.");
            return;
        }
        console.printf("Hi, %s, Your password is '%s'\n", name,  new String(password1));
        Arrays.fill(password1, '\0'); /* 安全清理 */
        Arrays.fill(password2, '\0');
    }
}
转载请注明:http://krystism.is-programmer.com/若有错误,请多多指正,谢谢!

调用Arrays.asList(arr).contains(value)判断数组是否存在某个元素?

这个问题很简单,就是在一个无序数组中判断某个元素是否存在,当然如果自己定义数据结构,可以使用trie,hash,如果不要求完全准确,允许误判,还能使用布隆!显然不能使用二分搜索,因为数组是无序的!当我们要经常搜索时,排序是必要的,或者使用其他数据结构。

这篇文章讨论了下http://www.programcreek.com/2014/04/check-if-array-contains-a-value-java/#more-12168,于是自己测试了下,下面是我的代码(必须使用java8版本以上):

import java.util.*;
interface StringPolicy {
	boolean contains(String[] arr, String value);
}
interface LongPolicy {
	boolean contains(long[] arr, long value);
}
class Policies {
	
    public static boolean useList(String[] arr, String value) {
        return Arrays.asList(arr).contains(value);
    }
    public static boolean useList(long[] arr, long value) {
    	return Arrays.asList(arr).contains(value);
    }
    public static boolean useSet(String[] arr, String value) {
        return new HashSet<>(Arrays.asList(arr)).contains(value);
    }
    public static boolean useSet(long[] arr, long value) {
        return new HashSet<>(Arrays.asList(arr)).contains(value);
    }
    public static boolean useLoop(String[] arr, String value) {
        for (String s : arr) {
            if (s.equals(value))
                return true;
        }
        return false;
    }
    public static boolean useLoop(long[] arr, long value) {
    	for (long l : arr) {
    		if (l == value)
    			return true;
    	}
    	return false;
    }
    public static boolean useStream(String[] arr, String value) {
    	return Arrays.stream(arr).anyMatch(value::equals);
    }
    public static boolean useStream(long[] arr, long value) {
    	return Arrays.stream(arr).anyMatch(l -> l == value);
    }
    public static boolean useParallelStream(String[] arr, String value) {
    	return Arrays.stream(arr).parallel().anyMatch(value::equals);
    }
    public static boolean useParallelStream(long[] arr, long value) {
    	return Arrays.stream(arr).parallel().anyMatch(l -> l == value);
    }
}
public class SearchDemo {
	public static void fill(String[] arr) {
        for(int i = 0; i < arr.length; ++i) {
            arr[i] = String.valueOf(i + 1);
        }
    }
	public static void fill(long[] arr) {
		for (int i = 0; i < arr.length; ++i) {
			arr[i] = i + 1;
		}
	}
	public static void fill(String[] arr, String value) {
		Arrays.fill(arr, value);
	}
	public static void fill(long[] arr, long value) {
		Arrays.fill(arr, value);
	}
	public static void test(String name, StringPolicy p, String[] arr, String value, int times) {
		long start = System.nanoTime();
		for (int i = 0; i < times; ++i) {
			p.contains(arr, value);
		}
		long end = System.nanoTime();
		long d = (end - start) / 1_000_000;
		System.out.printf("%s: %d\n", name, d);
	}
	public static void test(String name, LongPolicy p, long[] arr, long value, int times) {
		long start = System.nanoTime();
		for (int i = 0; i < times; ++i) {
			p.contains(arr, value);
		}
		long end = System.nanoTime();
		long d = (end - start) / 1_000_000;
		System.out.printf("%s: %d\n", name, d);
	}
	public static void main(String[] args) {
		String[] arr1 = new String[10_000];
		long[] arr2 = new long[10_000];
		fill(arr1);
		fill(arr2);
		System.out.println("******************* String **************************");
		test("useLoop", Policies::useLoop, arr1, "A", 10_000);
		test("useList", Policies::useList, arr1, "A", 10_000);
		test("useSet", Policies::useSet, arr1, "A", 10_000);
		test("useStream", Policies::useStream, arr1, "A", 10_000);
		test("useParallelStream", Policies::useParallelStream, arr1, "A", 10_000);
		System.out.println("******************* Long **************************");
		test("useLoop", Policies::useLoop, arr2, -1, 10_000);
		test("useList", Policies::useList, arr2, -1, 10_000);
		test("useSet", Policies::useSet, arr2, -1, 10_000);
		test("useStream", Policies::useStream, arr2, -1, 10_000);
		test("useParallelStream", Policies::useParallelStream, arr2, -1, 10_000);
	}

结果输出是:

数组大小为1k时的结果:
******************* String **************************
useLoop: 141
useList: 128
useSet: 859
useStream: 331
useParallelStream: 746
******************* Long **************************
useLoop: 87
useList: 7
useSet: 40
useStream: 311
useParallelStream: 835
数组大小为10k时的结果:
******************* String **************************
useLoop: 887
useList: 1005
useSet: 7889
useStream: 2489
useParallelStream: 2420
******************* Long **************************
useLoop: 644
useList: 10
useSet: 24
useStream: 2455
useParallelStream: 2748
当把数组大小提高到100k时,结果为:
******************* String **************************
useLoop: 10584
useList: 11597
useSet: 92295
useStream: 31249
useParallelStream: 17176
******************* Long **************************
useLoop: 6167
useList: 10
useSet: 33
useStream: 22633
useParallelStream: 21192
 
可以看出使用字符串时,使用循环的方式会比其他方式更快,但当判断基本数据类型时,使用List是最快的,往往使用Set都不是好方法,使用Stream也并没有提高速率。原文并没有说明!
 
转载请注明:http://krystism.is-programmer.com/若有错误,请多多指正,谢谢!

java中使用private构造方法不能实现单例模式

众所周知,一般使用java实现单例模式有两种方法,分别为急切式(饥饿式)和双重加锁式,急切式就是在声明时即创建,这样在类加载时就已经创建好了,即时我们可能并不不需要它,它的生命周期是永久的,造成内存泄漏的可能!第二种方式是lazy的,只有在使用时创建,实现了延迟加载。代码为

1.急切式

class Singleton {
    private final static Singleton instance = new Singleton();
    private Singleton(){
    }
    public static Singleton getInstance() {
        return instance;
    }
}

2. 双重加锁式

class Singleton {
    private volatile static Singleton instance = null;
    private Singleton(){

    }
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized(Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

还有一种方法是使用内部类的方法,这主要为克服方法1中的问题,既能实现延迟加载,又能保证线程安全,而且性能不错。代码为:

class Singleton {
    private Singleton() {

    }
    private static class Holder {
        private static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance() {
        return Holder.INSTANCE;
    }
}

以上方法都是通过使用private构造方法来阻止外部直接创建对象,但如果使用反射机制,则不能保证实例的唯一性了!!!

public class SingletonDemo {
    //@SuppressWarnings("unchecked")
    //public static <T> T newInstance(Class<?> cl) {
    //    T t = null;
    //    try {
    //        t = (T)cl.newInstance(); /* 只能调用public构造方法 */
    //    } catch(Exception e) {
    //        e.printStackTrace();
    //    }
    //    return t;
    //}
    @SuppressWarnings("unchecked")
    public static <T> T newInstance(Class<?> cl) {
        T t = null;
        try {
            Constructor<?> constructor = cl.getDeclaredConstructor();
            constructor.setAccessible(true);
            t = (T)constructor.newInstance();
        } catch(Exception e) {
            e.printStackTrace();
        }
        return t;
    }
    @SuppressWarnings("unchecked")
    public static <T> T newInstance(Class<?> cl, Class<?>...args) {
        T t = null;
        try {
            Constructor<?> constructor = cl.getDeclaredConstructor(args);
            constructor.setAccessible(true);
            t = (T)constructor.newInstance("name", 1);
        } catch(Exception e) {
            e.printStackTrace();
        }
        return t;
    }
    public static void main(String[] args) {
        Singleton instance = Single.INSTANCE;
        Singleton s1 = newInstance(Singleton.class, String.class, int.class);
        Singleton s2 = newInstance(Singleton.class, String.class, int.class);
        //Tmp t1 = newInstance(Tmp.class);
        //Tmp t2 = newInstance(Tmp.class);
        //System.out.println(s1 == s2);
        //System.out.println(t1 == t2);
    }
}

Joshua Bloch (Effect java 作者)提出一种新的方法实现单例模式,就是使用Enum(其实也是类,一种特殊的类,构造方法必须是private ,final的)!如下:

 

enum Singleton {
    INSTANCE;
    public void sayHi() {
        System.out.println("Hi");
    }
}

这样当试图通过反射机制创建对象时,会抛出异常!

转载请注明:http://krystism.is-programmer.com/若有错误,请多多指正,谢谢!

java同步工具Phaser

从java7开始在concurrent包中加入了Phaser类,它几乎可以取代CountDownLatch和CyclicBarrier, 其功能更灵活,更强大,支持动态调整需要控制的线程数。下面以一个具体实例说明这个Phaser类的用处,相信理解这个例子后,其功能不言而喻。

例子:有若干考生参加考试,考试科目是统一的,考试顺序为语文、数学、英语共三门,若其中一门挂科,则不能参加后面的考试。现在假设考试时间不受限制,开考时间以最后一名考完上一科时间为准,即先考完的考生并且成绩合格,需要参加下一门考试,则必须等待所有考生考完当科。

import java.util.*;
import java.util.concurrent.*;
class Examinee implements Runnable {
    public static Random random = new Random(new Date().getTime());
    private String name;
    private Phaser phaser;
    private int english;
    private int chinese;
    private int math;
    public Examinee(String name, Phaser phaser) {
        this.name = name;
        this.phaser = phaser;
    }
    public boolean english() {
        int time = random.nextInt(10);
        try {
            TimeUnit.SECONDS.sleep(time); /* 考试时间是一个随机数 */
        } catch (Exception e) {
            e.printStackTrace(System.err);
        }
        english = random.nextInt(101) ; /* 假定考试成绩是随机的 */
        if (english < 60) {
            System.out.printf("%s Failed to pass English!\n", name);
            phaser.arriveAndDeregister(); /* 考试不合格,取消考试资格 */
            return false;
        } else {
            phaser.arriveAndAwaitAdvance(); /* 通过考试,可以进行下一门考试 */
            return true;
        }
    }
    public boolean chinese() {
        int time = random.nextInt(10);
        try {
            TimeUnit.SECONDS.sleep(time);
        } catch (Exception e) {
            e.printStackTrace(System.err);
        }
        chinese = random.nextInt(101) ;
        if (chinese < 60) {
            System.out.printf("%s Failed to pass Chinese!\n", name);
            phaser.arriveAndDeregister();
            return false;
        } else {
            phaser.arriveAndAwaitAdvance();
            return true;
        }
    }
    public boolean math() {
        int time = random.nextInt(10);
        try {
            TimeUnit.SECONDS.sleep(time);
        } catch (Exception e) {
            e.printStackTrace(System.err);
        }
        math = random.nextInt(101) ;
        if (math < 60) {
            System.out.printf("%s Failed to pass Math!\n", name);
            phaser.arriveAndDeregister();
            return false;
        } else {
            phaser.arriveAndAwaitAdvance();
            return true;
        }
    }
    public void showInfo() {
        System.out.printf("%s\tChinese:%d\tEnglish:%d\tMath:%d\n", name, chinese, english, math);
        phaser.arriveAndDeregister();
    }
    @Override
    public void run() {
        phaser.arriveAndAwaitAdvance(); /* 等待考生做好准备,即在同一个起跑线上 */
        System.out.printf("%s is ready!\n", name);
        if (!chinese() || !english() || !math())
            return;
        showInfo(); /* 只显示所有科目均合格的考生分数 */
    }
}
public class PhaserDemo2 {
    public static void main(String[] args) {
        final String[] names = {"Jim", "Hary", "Mary", "Jhon", "Ali", "Lucy", "Wang", "Zhang", "Li", "Uhm", "Hey", "Gu", "Asu", "Who"};
        Phaser phaser = new Phaser(names.length);
        List<Thread> threads = new ArrayList<>(names.length);
        for (String name : names) {
            threads.add(new Thread(new Examinee(name, phaser), name)); /* 创建各个考生线程 */
        } 
        for (Thread t : threads) {
            t.start(); /* 开始考试 */
        }
        for (Thread t : threads) {
            try {
                t.join(); /* 等待考试结束 */
            } catch (InterruptedException e) {
                e.printStackTrace(System.err);
            }
        }
        System.out.printf("Terminal ? %s\n", phaser.isTerminated());
    }
}

由上面的例子,可以看出Phaser的功能。其中arrive方法很像CountDownLatch的countDown方法和CyclicBarrier的await方法,请自行比较之!Phaser类还有很多其他的功能,可以查看其API文档。

转载请注明:http://krystism.is-programmer.com/若有错误,请多多指正,谢谢!

java 使用Exchanger类实现两个线程交换数据

java.util.concurrent包中有许多涉及多线程并发的工具,使用这些现成工具可以大大简化编码,并且安全高效。

Exchanger提供了一个同步点,即调用exchange方法,当两个线程都到达这个同步点时,两个线程交换数据。

比如,有两个人,一个人需要买一本书,另一个人当然卖一本书,这两个人约定在某地交易,但这两个人距离约定地不同,到达目的地的时间也不同,先到的必须等待直到另一个人到达完成交易。

使用Exchanger类可以很容易实现!

import java.util.*;
import java.util.concurrent.*;
interface Exchangable {
}
class Money implements Exchangable {
    private int value;
    public Money(int value) {
        this.value = value;
    }
    public String toString() {
        return "$" + value;
    }
}
class Book implements Exchangable {
    private int value;
    private String name;
    public Book(String name, int value) {
        this.name = name;
        this.value = value;
    }
    public String toString() {
        String format = "Book: %s($%d)";
        return String.format(format, name, value);
    }
}
class Seller implements Runnable {
    Exchanger<Exchangable> exchanger;
    Exchangable book;
    public Seller(Exchanger<Exchangable> exchanger) {
        this.exchanger = exchanger;
        this.book = new Book("Love Java", 88);
    }
    public void run() {
        try {
            Random random = new Random(System.currentTimeMillis());
            int seconds = random.nextInt(10) + 1;
            System.out.println("Hi, I am Seller, I am going to sell a book,waiting " + seconds + "s");
            TimeUnit.SECONDS.sleep(seconds);
            System.out.println("Hi, I am Seller, I get " + exchanger.exchange(book));
        } catch (Exception e) {

        }
    }
}
class Buyer implements Runnable {
    Exchanger<Exchangable> exchanger;
    Exchangable money;
    public Buyer(Exchanger<Exchangable> exchanger) {
        this.exchanger = exchanger;
        this.money = new Money(88);
    }
    public void run() {
        try {
            Random random = new Random(System.currentTimeMillis() + 99);
            int seconds = random.nextInt(10) + 1;
            System.out.println("Hi, I am Buyer, I am going to buy a book,waiting " + seconds + "s");
            TimeUnit.SECONDS.sleep(seconds);
            System.out.println("Hi, I am Buyer, I get " + exchanger.exchange(money));
        } catch (Exception e) {

        }
    }
}
public class ExchangerDemo {
    public static void main(String[] args) {
        Exchanger<Exchangable> exchanger = new Exchanger<>();
        Seller seller = new Seller(exchanger);
        Buyer buyer = new Buyer(exchanger);
        new Thread(seller).start();
        new Thread(buyer).start();
    }
}

Exchanger类使用很简单,只有两个public方法,一个是exchange(V x)方法,死等待直到完成交易,另一个是exchange(V x, long time, TimeUnit timeunit),其中x是要交换的数据,time是时间,timeunit指定时间单位,合起来表示最长等待时间,比如exchange(x, 5, TimeUnit.SECONDS)表示最长等待5秒!

转载请注明:http://krystism.is-programmer.com/若有错误,请多多指正,谢谢!

ubuntu14.04 编译hadoop2.5

ubuntu13.10或以下版本也可以编译,只是源中maven和protobuf都版本太低,需要自己下源码重新编译安装,很繁琐。

hadoop2.5有编译好的发布包,但是默认是64位的,老是出现以下错误

OpenJDK Server VM warning: You have loaded library /hadoop/lib/native/libhadoop.so.1.0.0 which might have disabled stack guard. The VM will try to fix the stack guard now.
It's highly recommended that you fix the library with 'execstack -c <libfile>', or link it with '-z noexecstack'.
14/08/19 21:07:42 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable

看起来好像只是警告无所谓,但在启动脚本中NAMENODES=$(hdfs getconf  -namenodes),这时会把所有以上输出以空格作为分隔符作为namenode,显然出错,而且没法加载本地库,所以必须进行重新build。

网上很多说修改HADOOP_OPTS可以解决以上问题,但我试过很多次,根本不行,其实想想也是不可能的,64位的库怎么可以运行在32位呢!

从官方http://www.apache.org/dyn/closer.cgi/hadoop/core/下载源码。

源码是使用maven进行管理的,要求版本必须是maven3以上,由于maven调用了ant,因此ant也是必要的。

另外本地库使用c写的,还要求有cmake,make,auto工具,否则无法编译。总结以下是必要的包:

maven3.0+
libssl-dev
cmake
protobuf2.5+
jdk1.6+
zlib
auto工具(比如automake,autoconf)
make
openssl
build-essential
然后就可以运行 mvn package -Pdist,native -DskipTests -Dtar 进行重新构建。
另外注意JAVA_HOME环境变量的设置,必须确保JAVA_HONE/bin下有javah。
当编译一次后发现问题并不多,但这已经足够折腾我一天了,主要是由于maven编译时错误信息不太清楚,没有很清楚的说明问题所在,而只是抛出一堆异常,外部调用的auto工具和make工具的错误信息又没有显示,所以我是通过手动调用cmake和make去找问题的,否则半天不知道怎么回事。
转载请注明:http://krystism.is-programmer.com/若有错误,请多多指正,谢谢!

java中各种map

java中有各种类型的map,每种类型的map实现方式不同,但接口都类似。所有的具体map类型必须实现Map接口或者其子类,AbstractMap实现了Map接口,并实现了Map接口的部分功能,因此一般具体Map类型只需继承AbstractMap就可以了。

1. HashMap

HashMap的实现方式是一个table数组,数组元素是一个Entry类型(java8 中是Node类型,Entry变成了一个接口),Entry是一个内部类,类似c++的pair类型,b实际保存key 和value值,当put一个值,即调用put时,先计算key的hash值,通过这个hash值找到对应的table数组索引,如果hash冲突,采用的是拉链法解决冲突,即把所有同义词(hash值相等)存储在一个单链表中,因此Entry可以看作是一个单链表,next指向下一个同义词。一个好的hash应该尽量减少同义词,因为当出现hash冲突时,在同义词中查找是线性查找,效率很低。put方法代码为(java 7):

 public V put(K key, V value) {
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        if (key == null) /* null的话直接存放在table[0] */
            return putForNullKey(value);
        int hash = hash(key);
        int i = indexFor(hash, table.length); /*找到table索引 */
        for (Entry<K,V> e = table[i]; e != null; e = e.next) { /* 查找是否已经存在相同的key */
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { /* 如果hash冲突且key一样,则返回旧值,替换成新值 */
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);  //插入新的Entry
        return null; /* 没有替换,则返回null */
    }

从源码中可以看出HashMap允许null作为key值,相当于null的hash为0。当key相同时,则覆盖原来的值,并返回旧的value。判断key是否相同是调用key的equals,因此如果使用自己的类作为key,必须重载equals方法,否则引用相同时才相等。求hash值并不是直接使用key的hashCode方法,而是自己内部实现的hash方法,hash方法会调用key的hashCode方法,这样能够减少hash冲突,且保证hash值能在指定返回内,而hashCode返回的值有可能超出table的索引范围。因此自定义类要作为key还必须覆盖hashCode方法,尽量返回一个素数。

2. IdentityHashMap

上面说了HashMap的key值是否相同是调用equals方法,而IdentityHashMap原理类似,但判断key是否相同运用 == 运算符,即判断是否同一个引用,实现上也有些不同,table不再是Entry,而是一个Object对象数组(不会存在hash冲突)。put时先计算key的hash值,通过hash值找到table数组的索引i,然后table[i]存储key,table[i + 1]存储value,代码为:

  public V put(K key, V value) {
        Object k = maskNull(key);
        Object[] tab = table;
        int len = tab.length;
        int i = hash(k, len);

        Object item;
        while ( (item = tab[i]) != null) {
            if (item == k) {
                V oldValue = (V) tab[i + 1];
                tab[i + 1] = value;
                return oldValue;
            }
            i = nextKeyIndex(i, len);
        }

        modCount++;
        tab[i] = k;
        tab[i + 1] = value;
        if (++size >= threshold)
            resize(len); // len == 2 * current capacity.
        return null;
    }

因此IdentityHashMap用于存储key值重复的map。

3. Hashtable

Hashtable和HashMap类似,不过put方法是同步的,使用synchronized锁住整个方法,支持并发控制,但现在已经废弃了,因为效率太低了,取而代之的是ConcurrentHashMap。

4. ConcurrentHashMap

取代Hashtable,和Hashtable不同,它是使用段加锁的方法实现并发,而不是同步整个方法。和HashMap不同,HashMap所有的Entry保存在一个table数组中,而ConcurrentHashMap是保存在不同的Segment(段)中,通过hash值先定位到Segment(其实也是一个Segment数组),然后只需对这个Segment(继承了ReentrantLock)加锁就可以了,不影响其他Segment。

5.LinkedHashMap

LinkedHashMap是HashMap的子类,因此具有HashMap的所有特点。与HashMap不同的是,HashMap不记录顺序,迭代时不保证任何顺序,而LinkedHashMap Entry使用了一个双链表记录顺序,并且Entry多了before 和after指针,迭代默认根据插入的顺序,可以设置成按最近访问顺序,put效率会比HashMap低,毕竟会有记录顺序的开销,但迭代速度会更快,直接遍历整个双向链表即可。

6. EnumMap

EnumMap使用java Enum类型作为key,相对简单且高效,内部还是使用了table,table的值是Object类型,put时就是根据key的值(原始值,一个整型数)作为索引,直接插入,table的长度就是key的枚举个数,其put代码为:

 public V put(K key, V value) {
        typeCheck(key);

        int index = key.ordinal();
        Object oldValue = vals[index];
        vals[index] = maskNull(value);
        if (oldValue == null)
            size++;
        return unmaskNull(oldValue);
    }

首先是类型检查,判断key是否是初始化的枚举类型(构造方法必须指定枚举类型的类类型),如果value是null则映射成内部的NULL(也是一个Object对象)。由此可见EnumMap是非常高效的,插入删除访问都是O(1),不存在冲突问题。

7. TreeMap

TreeMap和之前的Map类型使用的数据结构不一样,它实现的是SortedMap,即按照key值自然顺序排列的,内部使用的数据结构是红黑树,红黑树是一种类平衡二叉树(和传统严格的平衡树有点差别),put源码为:

 public V put(K key, V value) {
        Entry<K,V> t = root;
        if (t == null) {
            compare(key, key); // type (and possibly null) check

            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        // split comparator and comparable paths
        Comparator<? super K> cpr = comparator;
        if (cpr != null) {
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        else {
            if (key == null)
                throw new NullPointerException();
            Comparable<? super K> k = (Comparable<? super K>) key;
            do {
                parent = t;
                cmp = k.compareTo(t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        Entry<K,V> e = new Entry<>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }

首先判断根是否空,不为空则遍历红黑树,看是否已经存在key,存在则直接覆盖原来的值并返回旧值,否则插入新数据,并调整红黑树。

8. ConcurrentSkipListMap

ConcurrentSkipListMap也是默认根据key值排序的,并且支持高并发访问(比ConcurrentHashMap并发性能更好,存取速度相对比ConcurrentHashMap低)。不过实现并不是使用红黑树,而是跳表,跳表的性能和查找树性能相当。

题外话:

以上介绍了8中map,如何选择取决于实际应用,如果不关心顺序且是单线程操作,使用HashMap,如果需要key按顺序排列,则使用TreeMap, 如果需要关心插入顺序或者最近访问顺序,使用LinkedHashMap。当涉及并发访问时,不关心key顺序使用ConcurrentHashMap,高并发且关心key顺序,使用ConcurrentSkipListMap,尽量不要使用Hashtable。如果key是枚举类型,则使用EnumMap。另外几乎每个Map类型都有相对应的Set版本,Set一般就是继承自Map,Set的元素值作为Map的key,而Map的value默认为一个Object对象常量,当然其实Map的value是什么都无所谓。

转载请注明:http://krystism.is-programmer.com/若有错误,请多多指正,谢谢!

使用java反射机制和json创建对象

java反射机制可以获取一个对象的所有属性以及其值,甚至可以改变其值,包括私有属性。利用这个特性可以使用json创建一个对象,尤其是java bean,非常方便。比如有一个对象有属性:id, name,有一个json对象{"id":"1000", "name" : "test"},通过反射机制可以很随意的创建一个和json对象值一样的对象,实现json对象和java对象转化。

首先需要获取一个对象的所有属性,注意到java Class对象方法getFields方法返回的是所有公共属性,包括父类和接口的属性,而getDeclaredFields()方法获取的是所有属性,包括私有属性,但不包含父类的属性。而要获取所有的包括父类的属性,没有现成的接口,于是自己写了个简单的,可能有问题,仅用于测试。

 

public  static Set<Field> getFields(Object obj) {
		Set<Field> fields = new HashSet<Field>();
		Class<? extends Object> cl = obj.getClass();
		String objName = Object.class.getName();
		while (!cl.getName().equals(objName)) {
			fields.addAll(Arrays.asList(cl.getDeclaredFields()));
			cl = cl.getSuperclass();
		}
		return fields;
	}

获取了所有的属性后,就可以通过属性设置新的值,代码为:

public static boolean setValue(Object obj, Field field, Object value) {
		field.setAccessible(true);
		try {
			field.set(obj, value);
		} catch (IllegalArgumentException | IllegalAccessException e) {
			// fixed here
		}
		return true;
	}

于是整个通过json新建对象代码为(这里使用了org.json包):

public Entity(JSONObject entity) {
		Set<Field> fields = Reflect.getFields(this);
		for (Field field : fields) {
			String _field = field.getName();
			if (entity.has(_field)) {
				Reflect.setValue(this, field, entity.get(_field));
			}
		}
	}

通过遍历所有的属性以及Json对象中存在的键值,设置对象的值。这主要在初始化java bean时非常有用。

转载请注明:http://krystism.is-programmer.com/若有错误,请多多指正,谢谢!

疯狂的java反射机制

java 从1.1就开始支持反射机制了,c++不支持反射机制。通过反射机制能够获取对象的类型和这个对象能做什么(有什么属性,什么方法),能够动态调用对象的方法,设置属性,以及创建新的实例(因此除了利用new关键字创建实例,还可以通过反射机制创建实例,其实还有两种方法,即反序列化和clone)。利用java的反射机制可以实现一些疯狂的事,做一些貌似无法实现的事。本文主要参照了2009年JavaZone会议上Heinz Kabutz发表的关于反射技术的疯狂演说 ,写得不错!

我们知道java的private属性是不允许外部直接访问修改的,而只能通过调用对象的外部接口访问和修改,但这不是绝对的,利用反射机制可以不调用对象的公共方法而修改对象属性,甚至修改final属性的值。

java String中字符存储是使用了private final char[] value,从这里我们也可以看出为什么java 字符串是不允许修改的,因为value是final的,但这仅意味着你无法通过调用它的公有方法来改变它的值,利用反射机制可以绕过这些限制而修改它,如代码:(想想这段代码的输出会是什么呢?)

import java.lang.reflect.*;
public class Test {
	public static void main(String[] args) throws Exception {
		Field field = String.class.getDeclaredField("value");
		field.setAccessible(true);
		field.set("hello!", "cheers".toCharArray());
		System.out.println("hello!");
	}
}

这里还有一个问题是字符串池,当使用多个字面字符串时(不使用new String("...")),如果已经存在这个字符串,则不会创建一个新的对象,而仅仅指向已存在字符串的引用,即

String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2);

s1 和s2是同一个对象的引用,即s1 == s2返回true。

回到上一段代码,field.set("hello!", "cheers".toCharArray()) 和 System.out.println("hello!")中字符串"hello!"其实是同一个对象,利用反射机制改变这个对象的value值,因此此时字符串"hello!"对应的value其实已经变成了"cheers",因此上段代码输出为cheers而不是hello!。

还有更神奇的,猜猜下面这段代码的输出结果是什么?

import java.lang.reflect.*;
public class Test {
	public static void main(String[] args) throws Exception {
		Field field = null;
		field = Integer.class.getDeclaredField("value");
		field.setAccessible(true);
		field.set(42, 43);
		System.out.printf("6 * 7 = %d\n", 6 * 7);
	}
}

利用反射机制还可以做很多奇妙的事,比如获取方法调用者,获取类中的方法,获取对象的内存大小等等,具体参照原文!

转载请注明:http://krystism.is-programmer.com/若有错误,请多多指正,谢谢!

switch可接受的变量数据类型

教科书都会说switch只能用于整型变量,于是c语言中很显然int, short, long, enum, char(包括无符号类型和long long)都可以作为switch变量,而float,double,struct,union显然不可以,那指针可不可以呢? 指针本质上就是一个无符号整数,根据定义应该是可以的,但在c语言中是非法的!即int i = 0; int *j = &i; switch(j){}是非法的(至少gcc是非法).

java中同样有switch语法,如果我们照般c语言的,很显然可接受的类型为int, byte, short, enum, char, long。我们觉得理应就是这样的,以至于我们不需要再作任何解释。可是当我们声明了一个long类型的变量,却发现我们怎么也编译不过去!是的,java的switch不支持long类型,这我也不理解这其中的缘由。那对象是否支持呢?也许你会很果断的回答,不支持,因为它不是整型,更确切的说不是int类型!可是事实并非如此,其实也不难理解,由于Integer, Byte等具有自动拆箱功能,因此可以作为switch变量也就不足为奇了。 好了,感觉所有的问题都解决了。但在java7中,switch变量可以传递String对象,这是java7中新增的特性。于是我是否可以作个这样的假定,switch可以传递任何对象,类似System.out.println函数,会自动调用对象的toString()方法呢? 事实上不可以,这样做是合理的。

转载请注明:http://krystism.is-programmer.com/若有错误,请多多指正,谢谢!