概述 工具类 就是封装平常用的方法,不需要你重复造轮子,节省开发人员时间,提高工作效率。谷歌作为大公司,当然会从日常的工作中提取中很多高效率的方法出来。所以就诞生了guava。 guava的优点: 高效设计良好的API,被Google的开发者设计,实现和使用 遵循高效的java语法实践 使代码更刻度,简洁,简单 节约时间,资源,提高生产力 Guava工程包含了若干被Google的 Java项目广泛依赖 的核心库,例如: 集合 [collections] 缓存 [caching] 原生类型支持 [primitives support] 并发库 [concurrency libraries] 通用注解 [common annotations] 字符串处理 [string processing] I/O 等等。 使用 引入gradle依赖(引入Jar包)
1 |
compile 'com.google.guava:guava:26.0-jre' |
1.集合的创建
1 2 3 4 5 6 7 8 9 |
// 普通Collection的创建 List<String> list = Lists.newArrayList(); Set<String> set = Sets.newHashSet(); Map<String, String> map = Maps.newHashMap(); // 不变Collection的创建 ImmutableList<String> iList = ImmutableList.of("a", "b", "c"); ImmutableSet<String> iSet = ImmutableSet.of("e1", "e2"); ImmutableMap<String, String> iMap = ImmutableMap.of("k1", "v1", "k2", "v2"); |
创建不可变集合 先理解什么是immutable(不可变)对象 在多线程操作下,是线程安全的 所有不可变集合会比可变集合更有效的利用资源 中途不可改变
1 |
ImmutableList<String> immutableList = ImmutableList.of("1","2","3","4"); |
这声明了一个不可变的List集合,List中有数据1,2,3,4。类中的 操作集合的方法(譬如add, set, sort, replace等)都被声明过期,并且抛出异常。 而没用guava之前是需要声明并且加各种包裹集合才能实现这个功能
1 2 3 4 5 |
// add 方法 @Deprecated @Override public final void add(int index, E element) { throw new UnsupportedOperationException(); } |
当我们需要一个map中包含key为String类型,value为List类型的时候,以前我们是这样写的
1 2 3 4 5 6 |
Map<String,List<Integer>> map = new HashMap<String,List<Integer>>(); List<Integer> list = new ArrayList<Integer>(); list.add(1); list.add(2); map.put("aa", list); System.out.println(map.get("aa"));//[1, 2] |
而现在
1 2 3 4 |
Multimap<String,Integer> map = ArrayListMultimap.create(); map.put("aa", 1); map.put("aa", 2); System.out.println(map.get("aa")); //[1, 2] |
其他的黑科技集合
1 2 3 4 5 6 7 8 9 10 11 12 13 |
MultiSet: 无序+可重复 count()方法获取单词的次数 增强了可读性+操作简单 创建方式: Multiset<String> set = HashMultiset.create(); Multimap: key-value key可以重复 创建方式: Multimap<String, String> teachers = ArrayListMultimap.create(); BiMap: 双向Map(Bidirectional Map) 键与值都不能重复 创建方式: BiMap<String, String> biMap = HashBiMap.create(); Table: 双键的Map Map--> Table-->rowKey+columnKey+value //和sql中的联合主键有点像 创建方式: Table<String, String, Integer> tables = HashBasedTable.create(); ...等等(guava中还有很多java里面没有给出的集合类型) |
2.将集合转换为特定规则的字符串 以前我们将list转换为特定规则的字符串是这样写的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
//use java List<String> list = new ArrayList<String>(); list.add("aa"); list.add("bb"); list.add("cc"); String str = ""; for(int i=0; i<list.size(); i++){ str = str + "-" +list.get(i); } //str 为-aa-bb-cc //use guava List<String> list = new ArrayList<String>(); list.add("aa"); list.add("bb"); list.add("cc"); String result = Joiner.on("-").join(list); //result为 aa-bb-cc |
把map集合转换为特定规则的字符串
1 2 3 4 5 |
Map<String, Integer> map = Maps.newHashMap(); map.put("xiaoming", 12); map.put("xiaohong",13); String result = Joiner.on(",").withKeyValueSeparator("=").join(map); // result为 xiaoming=12,xiaohong=13 |
3.将String转换为特定的集合
1 2 3 4 5 6 7 8 9 10 11 12 |
//use java List<String> list = new ArrayList<String>(); String a = "1-2-3-4-5-6"; String[] strs = a.split("-"); for(int i=0; i<strs.length; i++){ list.add(strs[i]); } //use guava String str = "1-2-3-4-5-6"; List<String> list = Splitter.on("-").splitToList(str); //list为 [1, 2, 3, 4, 5, 6] |
如果
1 |
str="1-2-3-4- 5- 6 "; |
guava还可以使用 omitEmptyStrings().trimResults() 去除空串与空格
1 2 3 |
String str = "1-2-3-4- 5- 6 "; List<String> list = Splitter.on("-").omitEmptyStrings().trimResults().splitToList(str); System.out.println(list); |
将String转换为map
1 2 |
String str = "xiaoming=11,xiaohong=23"; Map<String,String> map = Splitter.on(",").withKeyValueSeparator("=").split(str); |
4.guava还支持多个字符切割,或者特定的正则分隔
1 2 |
String input = "aa.dd,,ff,,."; List<String> result = Splitter.onPattern("[.|,]").omitEmptyStrings().splitToList(input); |
关于字符串的操作 都是在Splitter这个类上进行的
1 2 3 4 5 6 7 8 9 10 |
// 判断匹配结果 boolean result = CharMatcher.inRange('a', 'z').or(CharMatcher.inRange('A', 'Z')).matches('K'); //true // 保留数字文本 CharMatcher.digit() 已过时 retain 保留 //String s1 = CharMatcher.digit().retainFrom("abc 123 efg"); //123 String s1 = CharMatcher.inRange('0', '9').retainFrom("abc 123 efg"); // 123 // 删除数字文本 remove 删除 // String s2 = CharMatcher.digit().removeFrom("abc 123 efg"); //abc efg String s2 = CharMatcher.inRange('0', '9').removeFrom("abc 123 efg"); // abc efg |
[…]
View Details转自:http://www.codeceo.com/article/apache-commons-tools.html Apache Commons包含了很多开源的工具,用于解决平时编程经常会遇到的问题,减少重复劳动。我选了一些比较常用的项目做简单介绍。文中用了很多网上现成的东西,我只是做了一个汇总整理。 一、Commons BeanUtils http://jakarta.apache.org/commons/beanutils/index.html 说明:针对Bean的一个工具集。由于Bean往往是有一堆get和set组成,所以BeanUtils也是在此基础上进行一些包装。 使用示例:功能有很多,网站上有详细介绍。一个比较常用的功能是Bean Copy,也就是copy bean的属性。如果做分层架构开发的话就会用到,比如从PO(Persistent Object)拷贝数据到VO(Value Object)。 传统方法如下: //得到TeacherForm TeacherForm teacherForm=(TeacherForm)form; //构造Teacher对象 Teacher teacher=new Teacher(); //赋值 teacher.setName(teacherForm.getName()); teacher.setAge(teacherForm.getAge()); teacher.setGender(teacherForm.getGender()); teacher.setMajor(teacherForm.getMajor()); teacher.setDepartment(teacherForm.getDepartment()); //持久化Teacher对象到数据库 HibernateDAO= ; HibernateDAO.save(teacher); 使用BeanUtils后,代码就大大改观了,如下所示: //得到TeacherForm TeacherForm teacherForm=(TeacherForm)form; //构造Teacher对象 Teacher teacher=new Teacher(); //赋值 BeanUtils.copyProperties(teacher,teacherForm); //持久化Teacher对象到数据库 HibernateDAO= ; HibernateDAO.save(teacher); 二、Commons CLI http://jakarta.apache.org/commons/cli/index.html 说明:这是一个处理命令的工具。比如main方法输入的string[]需要解析。你可以预先定义好参数的规则,然后就可以调用CLI来解析。 使用示例: // create Options object Options options = new Options(); // add t option, option is the command parameter, false indicates that // this parameter is not required. options.addOption(“t”, false, “display current time”); options.addOption("c", true, "country code"); CommandLineParser parser = new PosixParser(); CommandLine cmd […]
View Details此问题中缺少的一个关键信息是使用Azure。因为aspnetcore是在Azure devops管道中发布程序包所必需的,所以可能要使用微软的源或者更新一些配置: 故此通过 curl https://packages.microsoft.com/config/rhel/7/prod.repo > ./microsoft-prod.repo sudo cp ./microsoft-prod.repo /etc/yum.repos.d/ yum update -y 可以解决。 ———————————————— 版权声明:本文为CSDN博主「Zero_77」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/Zero_77/article/details/115902042
View DetailsGit :fatal: refusing to merge unrelated histories解决 今天本地创建了一个仓库(有README),把本地仓库和Github上关联以后,发现git pull,git feach提醒fatal: refusing to merge unrelated histories 上网查到原因是两个分支是两个不同的版本,具有不同的提交历史 加一句 $git pull origin master --allow-unrelated-histories 1 可以允许不相关历史提,强制合并,确实解决了这个问题,感谢网友 ———————————————— 版权声明:本文为CSDN博主「天骄山仔」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_39400546/article/details/100150320
View DetailsTo get started with Docker Engine on CentOS, make sure you meet the prerequisites, then install Docker. Prerequisites OS requirements To install Docker Engine, you need a maintained version of CentOS 7 or 8. Archived versions aren’t supported or tested. The centos-extras repository must be enabled. This repository is enabled by default, but if you have disabled it, you need to re-enable it. The overlay2 storage driver is recommended. Uninstall old versions Older versions of Docker were called docker or docker-engine. If these are installed, uninstall them, along with associated dependencies.
1 2 3 4 5 6 7 8 |
$ sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine |
It’s OK if yum reports that none […]
View Details为什么使用雪花ID 在以前的项目中,最常见的两种主键类型是自增Id和UUID,在比较这两种ID之前首先要搞明白一个问题,就是为什么主键有序比无序查询效率要快,因为自增Id和UUID之间最大的不同点就在于有序性。 我们都知道,当我们定义了主键时,数据库会选择表的主键作为聚集索引(B+Tree),mysql 在底层是以数据页为单位来存储数据的。 也就是说如果主键为自增 id 的话,mysql 在写满一个数据页的时候,直接申请另一个新数据页接着写就可以了。如果一个数据页存满了,mysql 就会去申请一个新的数据页来存储数据。如果主键是UUID,为了确保索引有序,mysql 就需要将每次插入的数据都放到合适的位置上。这就造成了页分裂,这个大量移动数据的过程是会严重影响插入效率的。 一句话总结就是,InnoDB表的数据写入顺序能和B+树索引的叶子节点顺序一致的话,这时候存取效率是最高的。 但是为什么很多情况又不用自增id作为主键呢? 容易导致主键重复。比如导入旧数据时,线上又有新的数据新增,这时就有可能在导入时发生主键重复的异常。为了避免导入数据时出现主键重复的情况,要选择在应用停业后导入旧数据,导入完成后再启动应用。显然这样会造成不必要的麻烦。而UUID作为主键就不用担心这种情况。 不利于数据库的扩展。当采用自增id时,分库分表也会有主键重复的问题。UUID则不用担心这种问题。 那么问题就来了,自增id会担心主键重复,UUID不能保证有序性,有没有一种ID既是有序的,又是唯一的呢? 当然有,就是雪花ID。 什么是雪花ID snowflake是Twitter开源的分布式ID生成算法,结果是64bit的Long类型的ID,有着全局唯一和有序递增的特点。 最高位是符号位,因为生成的 ID 总是正数,始终为0,不可用。 41位的时间序列,精确到毫秒级,41位的长度可以使用69年。时间位还有一个很重要的作用是可以根据时间进行排序。 10位的机器标识,10位的长度最多支持部署1024个节点。 12位的计数序列号,序列号即一系列的自增ID,可以支持同一节点同一毫秒生成多个ID序号,12位的计数序列号支持每个节点每毫秒产生4096个ID序号。 缺点也是有的,就是强依赖机器时钟,如果机器上时钟回拨,有可能会导致主键重复的问题。 Java实现雪花ID 下面是用Java实现雪花ID的代码,供大家参考一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
public class SnowflakeIdWorker { /** * 开始时间:2020-01-01 00:00:00 */ private final long beginTs = 1577808000000L; private final long workerIdBits = 10; /** * 2^10 - 1 = 1023 */ private final long maxWorkerId = -1L ^ (-1L << workerIdBits); private final long sequenceBits = 12; /** * 2^12 - 1 = 4095 */ private final long maxSequence = -1L ^ (-1L << sequenceBits); /** * 时间戳左移22位 */ private final long timestampLeftOffset = workerIdBits + sequenceBits; /** * 业务ID左移12位 */ private final long workerIdLeftOffset = sequenceBits; /** * 合并了机器ID和数据标示ID,统称业务ID,10位 */ private long workerId; /** * 毫秒内序列,12位,2^12 = 4096个数字 */ private long sequence = 0L; /** * 上一次生成的ID的时间戳,同一个worker中 */ private long lastTimestamp = -1L; public SnowflakeIdWorker(long workerId) { if (workerId > maxWorkerId || workerId < 0) { throw new IllegalArgumentException(String.format("WorkerId必须大于或等于0且小于或等于%d", maxWorkerId)); } this.workerId = workerId; } public synchronized long nextId() { long ts = System.currentTimeMillis(); if (ts < lastTimestamp) { throw new RuntimeException(String.format("系统时钟回退了%d毫秒", (lastTimestamp - ts))); } // 同一时间内,则计算序列号 if (ts == lastTimestamp) { // 序列号溢出 if (++sequence > maxSequence) { ts = tilNextMillis(lastTimestamp); sequence = 0L; } } else { // 时间戳改变,重置序列号 sequence = 0L; } lastTimestamp = ts; // 0 - 00000000 00000000 00000000 00000000 00000000 0 - 00000000 00 - 00000000 0000 // 左移后,低位补0,进行按位或运算相当于二进制拼接 // 本来高位还有个0<<63,0与任何数字按位或都是本身,所以写不写效果一样 return (ts - beginTs) << timestampLeftOffset | workerId << workerIdLeftOffset | sequence; } /** * 阻塞到下一个毫秒 * * @param lastTimestamp * @return */ private long tilNextMillis(long lastTimestamp) { long ts = System.currentTimeMillis(); while (ts <= lastTimestamp) { ts = System.currentTimeMillis(); } return ts; } public static void main(String[] args) { SnowflakeIdWorker snowflakeIdWorker = new SnowflakeIdWorker(7); for (int i = 0; i < 10; i++) { long id = snowflakeIdWorker.nextId(); System.out.println(id); } } } |
main方法,测试结果如下:
1 2 3 4 5 6 7 8 9 10 |
184309536616640512 184309536616640513 184309536616640514 184309536616640515 184309536616640516 184309536616640517 184309536616640518 184309536616640519 184309536616640520 184309536616640521 |
总结 在大部分公司的开发项目中里,雪花ID是主流的ID生成策略,除了自己实现之外,目前市场上也有很多开源的实现,比如: 美团开源的Leaf 百度开源的UidGenerator 有兴趣的可以自行观摩一下,那么这篇文章就写到这里了,感谢大家的阅读。 作者:java技术爱好者 链接:https://juejin.cn/post/6965510420387856398 来源:掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
View DetailsStringBuilder转化为String
1 2 |
String str = "abcdefghijklmnopqrs"; StringBuilder stb = new StringBuilder(str); |
整型数组转化为字符串
1 2 3 4 5 |
StringBuilder s = new StringBuilder(); for(i=1;i<=n;i++) { s.append(String.valueOf(a[i])); } String str = ""+s; |
字符串转化为整形数组
1 2 3 4 5 |
String str="123456"; int[] a = new int[str.length()]; for(int i=0;i<str.length();i++) { a[i] = str.charAt(i)-'0'; } |
字符串转化为字符数组
1 2 3 |
String str="123456"; char[] c = str.toCharArray() ; System.out.println(c); |
字符数组转化为字符串
1 2 3 |
char[] c = {'a','s','d','4','5',}; String str = new String(c); System.out.println(str); |
字符数组转化为整型数组
1 2 3 4 5 6 |
char[] c = { '1', '2', '3', '4', '5', }; int[] a = new int[c.length]; for (int i = 0; i < 5; i++) { a[i] = c[i] - '0'; System.out.println(a[i]); } |
整型数组转化为字符数组
1 2 3 4 5 6 |
int[] a = {1,2,3,4,5}; char[] c = new char[a.length]; for (int i = 0; i < 5; i++) { c[i] = (char) (a[i]+'0'); System.out.println(c[i]); } |
整型数转化为字符串
1 2 3 |
1.String str = Integer.toString(i); 2.String s = String.valueOf(i); 3.String s = "" + i; |
字符串转化为整型数
1 |
int i = Integer.valueOf(str).intValue(); |
java类型转换 Integer String Long Float Double Date 1如何将字串 String 转换成整数 int? A. 有两个方法:
1 2 3 4 5 6 7 |
1). int i = Integer.parseInt([String]); 或 i = Integer.parseInt([String],[int radix]); 2). int i = Integer.valueOf(my_str).intValue(); 注: 字串转成 Double, Float, Long 的方法大同小异. |
2 如何将整数 int 转换成字串 String ? A. 有叁种方法:
1 2 3 4 5 6 7 |
1.) String s = String.valueOf(i); 2.) String s = Integer.toString(i); 3.) String s = "" + i; 注: Double, Float, Long 转成字串的方法大同小异. |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
package cn.com.lwkj.erts.register; import java.sql.Date; public class TypeChange { public TypeChange() { } //change the string type to the int type public static int stringToInt(String intstr) { Integer integer; integer = Integer.valueOf(intstr); return integer.intValue(); } //change int type to the string type public static String intToString(int value) { Integer integer = new Integer(value); return integer.toString(); } //change the string type to the float type public static float stringToFloat(String floatstr) { Float floatee; floatee = Float.valueOf(floatstr); return floatee.floatValue(); } //change the float type to the string type public static String floatToString(float value) { Float floatee = new Float(value); return floatee.toString(); } //change the string type to the sqlDate type public static java.sql.Date stringToDate(String dateStr) { return java.sql.Date.valueOf(dateStr); } //change the sqlDate type to the string type public static String dateToString(java.sql.Date datee) { return datee.toString(); } public static void main(String[] args) { java.sql.Date day ; day = TypeChange.stringToDate("2003-11-3"); String strday = TypeChange.dateToString(day); System.out.println(strday); } } /* 我们 www.jb51.net */ |
JAVA中常用数据类型转换函数 虽然都能在JAVA API中找到,整理一下做个备份。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
string->byte Byte static byte parseByte(String s) byte->string Byte static String toString(byte b) char->string Character static String to String (char c) string->Short Short static Short parseShort(String s) Short->String Short static String toString(Short s) String->Integer Integer static int parseInt(String s) Integer->String Integer static String tostring(int i) String->Long Long static long parseLong(String s) Long->String Long static String toString(Long i) String->Float Float static float parseFloat(String s) Float->String Float static String toString(float f) String->Double Double static double parseDouble(String s) Double->String Double static String toString(Double) ++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
数据类型 基本类型有以下四种: int长度数据类型有:byte(8bits)、short(16bits)、int(32bits)、long(64bits)、 float长度数据类型有:单精度(32bits float)、双精度(64bits double) boolean类型变量的取值有:ture、false char数据类型有:unicode字符,16位 对应的类类型:Integer、Float、Boolean、Character、Double、Short、Byte、Long 转换原则 从低精度向高精度转换 byte 、short、int、long、float、double、char 注:两个char型运算时,自动转换为int型;当char与别的类型运算时,也会先自动转换为int型的,再做其它类型的自动转换 基本类型向类类型转换 正向转换:通过类包装器来new出一个新的类类型的变量 Integer a= new Integer(2); 反向转换:通过类包装器来转换 int b=a.intValue(); 类类型向字符串转换 正向转换:因为每个类都是object类的子类,而所有的object类都有一个toString()函数,所以通过toString()函数来转换即可 反向转换:通过类包装器new出一个新的类类型的变量 eg1: int i=Integer.valueOf(“123”).intValue() 说明:上例是将一个字符串转化成一个Integer对象,然后再调用这个对象的intValue()方法返回其对应的int数值。 eg2: float f=Float.valueOf(“123”).floatValue() 说明:上例是将一个字符串转化成一个Float对象,然后再调用这个对象的floatValue()方法返回其对应的float数值。 eg3: boolean b=Boolean.valueOf(“123”).booleanValue() 说明:上例是将一个字符串转化成一个Boolean对象,然后再调用这个对象的booleanValue()方法返回其对应的boolean数值。 eg4:double d=Double.valueOf(“123”).doublue() […]
View Details类型转换虽然很简单,但是还是有些小细节要多注意。 String转化为int:
1 2 |
String test="123"; int number=Integer.parseInt(test); |
String转化为Integer,可以如下所示。 也适用于int转化为Integer:
1 2 3 |
String test="123"; // String test="abc"; //会报错:NumberFormatException: For input string Integer number=Integer.valueOf(test); |
注意:不管是使用Integer.parseInt(),还是使用Integer.valueOf()将字符串转换成数字, 如果是非数字的字符串,会报错:NumberFormatException: For input string: "" 另外,Integer类取值和 int 类型取值一致,取值范围是从-2147483648 至 2147483647(-231至 231-1) ,包括-2147483648 和 2147483647。 如果超过了这个范围,也会报错。比如Integer.valueOf("2147483648"),超过了Integer范围。因此会报错: For input string: "2147483648" 更安全的做法是,使用apache包的NumberUtils,如下: 注意:NumberUtils只处理整数,不能用来处理小数。
1 2 3 4 5 |
String str="abc"; //str不为数字时,设置默认值为 0 int num = NumberUtils.toInt(str); //str不为数字时,设置默认值为其他值,比如1 int defaultNum=NumberUtils.toInt(str,1); |
String转BigDecimal:
1 2 |
String str1="2.30"; BigDecimal bd=new BigDecimal(str1); |
String转double :
1 |
double value = NumberUtils.toDouble("4.23"); |
Double转化为int:
1 2 |
Double test=new Double("1.23"); //Double初始化,最好用String保证精度 int result=test.intValue(); |
其他类型转String:
1 2 |
// Object obj="123"; String test=String.valueOf(obj); |
注意:当String.valueOf()的参数obj为null时,返回值是字符串"null"!!而不是null。 如果希望obj为null时,返回"",可以使用apache-commons-lang的包,如下所示:
1 2 |
Object object=null; String str = ObjectUtils.toString(object); //object为null时,结果为"" |
如果希望obj为null时,返回null,如下: ObjectUtils.toString(object,nullStr),第二参数nullStr表示,当object为null时,方法返回的值。
1 2 3 4 |
// Object obj=null; Object object="123"; String str = ObjectUtils.toString(object,null); //相当于 String str= (object == null) ? null : object.toString(); |
Integer转double: 使用doubleValue()方法,或者 (double)强制转换。
1 2 3 |
Integer a= new Integer(5); int intvalue=a.intValue(); double doublevalue=a.doubleValue(); |
其他类型转Double:
1 |
Double rate= Double.valueOf(obj); |
比较小数是否相等。 比较Double是否相等。比较BigDecimal是否相等。 如下所示:
1 2 3 4 |
double value=1.23; if (BigDecimal.ZERO.compareTo(BigDecimal.valueOf(value)) == 0) { // } |
比较Double类型的大小:
1 2 3 |
if (Double.valueOf(d1).compareTo(Double.valueOf(d2))<0) { //... } |
比较double类型的大小: 除了用BigDemical的compare()方法,可以直接用Double.doubleToLongBits()的结果值用==,>,<进行比较
1 2 3 |
if(Double.doubleToLongBits(d1) == Double.doubleToLongBits(d2))){ // } |
from:https://www.cnblogs.com/expiator/p/12602446.html
View Details
1 2 |
Severity Code Description Project File Line Suppression State Error CS7038 Failed to emit module 'JTHY.Web': Changing the version of an assembly reference is not allowed during debugging: 'RUC.Base, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' changed version to '0.0.0.0'. JTHY.Web 1 Active |
如题,公司的一个项目。 原因:由于未知的原因,被引用项目的GUID和引用的GUID不一致引起的这个问题。 解决:重新引用也无效,那只有手动编辑项目文件了~
View Detailsvolatile这个关键字可能很多朋友都听说过,或许也都用过。在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果。在Java 5之后,volatile关键字才得以重获生机。 volatile关键字虽然从字面上理解起来比较简单,但是要用好不是一件容易的事情。由于volatile关键字是与Java的内存模型有关的,因此在讲述volatile关键之前,我们先来了解一下与内存模型相关的概念和知识,然后分析了volatile关键字的实现原理,最后给出了几个使用volatile关键字的场景。 以下是本文的目录大纲: 一.内存模型的相关概念 二.并发编程中的三个概念 三.Java内存模型 四..深入剖析volatile关键字 五.使用volatile关键字的场景 若有不正之处请多多谅解,并欢迎批评指正。 请尊重作者劳动成果,转载请标明原文链接: http://www.cnblogs.com/dolphin0520/p/3920373.html 一.内存模型的相关概念 大家都知道,计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据的读取和写入。由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,这时就存在一个问题,由于CPU执行速度很快,而从内存读取数据和向内存写入数据的过程跟CPU执行指令的速度比起来要慢的多,因此如果任何时候对数据的操作都要通过和内存的交互来进行,会大大降低指令执行的速度。因此在CPU里面就有了高速缓存。 也就是,当程序在运行过程中,会将运算需要的数据从主存复制一份到CPU的高速缓存当中,那么CPU进行计算时就可以直接从它的高速缓存读取数据和向其中写入数据,当运算结束之后,再将高速缓存中的数据刷新到主存当中。举个简单的例子,比如下面的这段代码: 1 i = i + 1; 当线程执行这个语句时,会先从主存当中读取i的值,然后复制一份到高速缓存当中,然后CPU执行指令对i进行加1操作,然后将数据写入高速缓存,最后将高速缓存中i最新的值刷新到主存当中。 这个代码在单线程中运行是没有任何问题的,但是在多线程中运行就会有问题了。在多核CPU中,每条线程可能运行于不同的CPU中,因此每个线程运行时有自己的高速缓存(对单核CPU来说,其实也会出现这种问题,只不过是以线程调度的形式来分别执行的)。本文我们以多核CPU为例。 比如同时有2个线程执行这段代码,假如初始时i的值为0,那么我们希望两个线程执行完之后i的值变为2。但是事实会是这样吗? 可能存在下面一种情况:初始时,两个线程分别读取i的值存入各自所在的CPU的高速缓存当中,然后线程1进行加1操作,然后把i的最新值1写入到内存。此时线程2的高速缓存当中i的值还是0,进行加1操作之后,i的值为1,然后线程2把i的值写入内存。 最终结果i的值是1,而不是2。这就是著名的缓存一致性问题。通常称这种被多个线程访问的变量为共享变量。 也就是说,如果一个变量在多个CPU中都存在缓存(一般在多线程编程时才会出现),那么就可能存在缓存不一致的问题。 为了解决缓存不一致性问题,通常来说有以下2种解决方法: 1)通过在总线加LOCK#锁的方式 2)通过缓存一致性协议 这2种方式都是硬件层面上提供的方式。 在早期的CPU当中,是通过在总线上加LOCK#锁的形式来解决缓存不一致的问题。因为CPU和其他部件进行通信都是通过总线来进行的,如果对总线加LOCK#锁的话,也就是说阻塞了其他CPU对其他部件访问(如内存),从而使得只能有一个CPU能使用这个变量的内存。比如上面例子中 如果一个线程在执行 i = i +1,如果在执行这段代码的过程中,在总线上发出了LCOK#锁的信号,那么只有等待这段代码完全执行完毕之后,其他CPU才能从变量i所在的内存读取变量,然后进行相应的操作。这样就解决了缓存不一致的问题。 但是上面的方式会有一个问题,由于在锁住总线期间,其他CPU无法访问内存,导致效率低下。 所以就出现了缓存一致性协议。最出名的就是Intel 的MESI协议,MESI协议保证了每个缓存中使用的共享变量的副本是一致的。它核心的思想是:当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时,发现自己缓存中缓存该变量的缓存行是无效的,那么它就会从内存重新读取。 二.并发编程中的三个概念 在并发编程中,我们通常会遇到以下三个问题:原子性问题,可见性问题,有序性问题。我们先看具体看一下这三个概念: 1.原子性 原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。 一个很经典的例子就是银行账户转账问题: 比如从账户A向账户B转1000元,那么必然包括2个操作:从账户A减去1000元,往账户B加上1000元。 试想一下,如果这2个操作不具备原子性,会造成什么样的后果。假如从账户A减去1000元之后,操作突然中止。然后又从B取出了500元,取出500元之后,再执行 往账户B加上1000元 的操作。这样就会导致账户A虽然减去了1000元,但是账户B没有收到这个转过来的1000元。 所以这2个操作必须要具备原子性才能保证不出现一些意外的问题。 同样地反映到并发编程中会出现什么结果呢? 举个最简单的例子,大家想一下假如为一个32位的变量赋值过程不具备原子性的话,会发生什么后果? 1 i = 9; 假若一个线程执行到这个语句时,我暂且假设为一个32位的变量赋值包括两个过程:为低16位赋值,为高16位赋值。 那么就可能发生一种情况:当将低16位数值写入之后,突然被中断,而此时又有一个线程去读取i的值,那么读取到的就是错误的数据。 2.可见性 可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。 举个简单的例子,看下面这段代码: 1 2 3 4 5 6 //线程1执行的代码 int i = 0; i = 10; //线程2执行的代码 j = i; 假若执行线程1的是CPU1,执行线程2的是CPU2。由上面的分析可知,当线程1执行 i =10这句时,会先把i的初始值加载到CPU1的高速缓存中,然后赋值为10,那么在CPU1的高速缓存当中i的值变为10了,却没有立即写入到主存当中。 此时线程2执行 j = i,它会先去主存读取i的值并加载到CPU2的缓存当中,注意此时内存当中i的值还是0,那么就会使得j的值为0,而不是10. 这就是可见性问题,线程1对变量i修改了之后,线程2没有立即看到线程1修改的值。 3.有序性 有序性:即程序执行的顺序按照代码的先后顺序执行。举个简单的例子,看下面这段代码: 1 2 […]
View Details