首页 文章 精选 留言 我的

精选列表

搜索[快速入门],共10000篇文章
优秀的个人博客,低调大师

【Java入门提高篇】Day30 Java容器类详解(十二)TreeMap详解

今天来看看Map家族的另一名大将——TreeMap。前面已经介绍过Map家族的两名大将,分别是HashMap,LinkedHashMap。HashMap可以高效查找和存储元素,LinkedHashMap可以在高效查找的基础上对元素进行有序遍历,那么TreeMap又有什么特点呢?别急别急,看完这篇你就知道了。 本篇主要从以下几个方面对TreeMap进行介绍: 1、TreeMap的特性以及使用栗子 2、TreeMap继承结构简介 3、TreeMap源码分析 本篇预计食用10分钟,请各位食客合理分配时间。 一、TreeMap的特性以及使用栗子 1. 键值不允许重复2. 默认会对键进行排序,所以键必须实现Comparable接口或者使用外部比较器3. 查找、移除、添加操作的时间复杂度为log(n)4.底层使用的数据结构是红黑树 没错,又是让你欲仙欲死的红黑树,不过不要慌,跟之前介绍HashMap时的红黑树是一毛一样的,所以这一篇里,不打算再做介绍啦,如果对红黑树的内容有些遗忘了,可以动动小手,往前面翻一翻。 先来看一个TreeMap的使用小栗子。 public class TreeMapTest { public static void main(String[] args){ TreeMap<String, Integer> grades = new TreeMap<>(); grades.put("Frank", 100); grades.put("Alice", 95); grades.put("Mary", 90); grades.put("Bob", 85); grades.put("Jack", 90); System.out.println(grades); System.out.println(grades.subMap("Bob", "Jack")); System.out.println(grades.subMap("Bob", true, "Jack", true)); System.out.println(grades.ceilingEntry("Bob")); System.out.println(grades.ceilingKey("Bob")); System.out.println(grades.higherEntry("Bob")); System.out.println(grades.higherKey("Bob")); System.out.println(grades.headMap("Bob")); System.out.println(grades.headMap("Bob", true)); System.out.println(grades.tailMap("Bob")); System.out.println(grades.tailMap("Bob", true)); System.out.println(grades.containsKey("Bob")); System.out.println(grades.containsValue(90)); System.out.println(grades.descendingMap()); System.out.println(grades.descendingKeySet()); } } 输出如下: {Alice=95, Bob=85, Frank=100, Jack=90, Mary=90} {Bob=85, Frank=100} {Bob=85, Frank=100, Jack=90} Bob=85 Bob Frank=100 Frank {Alice=95} {Alice=95, Bob=85} {Bob=85, Frank=100, Jack=90, Mary=90} {Bob=85, Frank=100, Jack=90, Mary=90} true true {Mary=90, Jack=90, Frank=100, Bob=85, Alice=95} [Mary, Jack, Frank, Bob, Alice] 可以看到,放入TreeMap中的元素默认按键值升序排列,这里的键值类型为String,使用String的CompareTo方法进行比较和排序。subMap返回当前Map的子Map,headMap和tailMap也是如此, 二、TreeMap继承结构简介 TreeMap继承自AbstractMap,实现了NavigableMap接口,继承关系图如下: 对于AbstractMap相信大家已经不陌生了,HashMap也是继承自AbstractMap,里面有对Map接口的一些默认实现。这里我们可以看到两个新的接口——SortedMap和NavigableMap。SortedMap接口继承自Map接口,从名字就能看出。SortedMap相比Map接口,憎加了排序的功能,内部的方法也不多,简单了解一下就好了 NavigableMap接口继承自SortedMap接口,主要提供一下导航方法: 说了这么多没啥营养的,接下来还是讲讲真正的干货吧。 三、TreeMap源码分析 JDK 1.8中的TreeMap源码有两千多行,还是比较多的。所以本文并不打算逐句分析所有的源码,而是挑选几个常用的内部类和方法进行分析。这些方法实现的功能分别是查找、遍历、插入、删除等,其他的方法小伙伴们有兴趣可以自己分析。TreeMap实现的核心部分是关于红黑树的实现,其绝大部分的方法基本都是对底层红黑树增、删、查操作的一个封装。就像前面所说,只要弄懂了红黑树原理,TreeMap 就没什么秘密了。关于红黑树的原理,可以参考前面关于HashMap红黑树的文章,本篇文章不会对此展开讨论。 TreeMap的主要数据结构是红黑树,而这红黑树结构的承载者便是内部类Entry,先来看看这个Entry类: static final class Entry<K,V> implements Map.Entry<K,V> { K key; V value; Entry<K,V> left; Entry<K,V> right; Entry<K,V> parent; boolean color = BLACK; /** * 构造函数*/ Entry(K key, V value, Entry<K,V> parent) { this.key = key; this.value = value; this.parent = parent; } public K getKey() { return key; } public V getValue() { return value; } public V setValue(V value) { V oldValue = this.value; this.value = value; return oldValue; } public boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry<?,?> e = (Map.Entry<?,?>)o; return valEquals(key,e.getKey()) && valEquals(value,e.getValue()); } public int hashCode() { int keyHash = (key==null ? 0 : key.hashCode()); int valueHash = (value==null ? 0 : value.hashCode()); return keyHash ^ valueHash; } public String toString() { return key + "=" + value; } } 其实内部的结构也很简单,主要有key,value和三个分别指向左孩子,右孩子,父节点的引用,以及用来标识颜色的color成员变量。再来看看TreeMap中的几个重要的成员变量: /** * 外部比较器 */ private final Comparator<? super K> comparator; private transient Entry<K,V> root; /** * 键值对数量 */ private transient int size = 0; private transient int modCount = 0; private static final boolean RED = false; private static final boolean BLACK = true; /** * 键值对集合 */ private transient EntrySet entrySet; /** * 键的集合 */ private transient KeySet<K> navigableKeySet; /** * 倒序Map */ private transient NavigableMap<K,V> descendingMap; comparator用于对map中的键进行排序,root指向红黑树的根节点,size表示键值对的数量,modCount相信已经不陌生了,表示内部结构被修改的次数,RED和BLACK是两个内部常量,即红黑两种颜色,false表示红,true表示黑。entrySet是键值对的集合,navigableKeySet是键的集合,最后一个descendingMap是当前map的一个倒序map。 在TreeMap中有很多内部类,可以先看图了解一下: 前前后后一共18个内部类,不过不要慌,其实里面跟迭代器相关的类就占了一半多(10个),跟子Map相关的类占4个,剩下4个就是跟内部集合相关的了。接下来还是一起来看看那些最常用的方法吧: // 插入元素 public V put(K key, V value) { TreeMap.Entry<K,V> t = root; if (t == null) { // 检查类型以及key是否为null // 如果外部比较器为null,且key也为null则会抛出空指针异常 // 如果TreeMap未设置外部比较器,且传入的对象未实现Comparable接口 // 则会抛出ClassCastException异常 compare(key, key); // type (and possibly null) check // 如果根节点不存在,则用传入的键值对信息生成一个根节点 root = new TreeMap.Entry<>(key, value, null); size = 1; modCount++; return null; } int cmp; TreeMap.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 // 找到相等的key则替换其value return t.setValue(value); // 一直循环,直到待比较的节点为null } while (t != null); } else { // 如果外部比较器为null // 如果key为null则抛出空指针 if (key == null) throw new NullPointerException(); // 如果key未实现comparable接口则会抛出异常 @SuppressWarnings("unchecked") Comparable<? super K> k = (Comparable<? super K>) key; do { // 跟上面逻辑类似,只是用key的compareTo方法进行比较,而不是用外部比较器的compare方法 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); } // 生成键值对 TreeMap.Entry<K,V> e = new TreeMap.Entry<>(key, value, parent); // 连接到当前map的左孩子位置或者右孩子位置 if (cmp < 0) parent.left = e; else parent.right = e; // 插入后的调整 fixAfterInsertion(e); size++; modCount++; return null; } 其实这里的逻辑跟HashMap中TreeNode的插入逻辑十分类似,也是先找到要插入的位置,然后再进行结构调整。这里的结构调整即红黑树的结构调整,在前面HashMap中已经详细介绍过了,这里就不重复介绍了,调整过程是完全一样的。 /** * 插入后的调整 */ private void fixAfterInsertion(TreeMap.Entry<K,V> x) { // 将插入的节点初始化为红色节点 x.color = RED; // 如果x不为null且x不是根节点,且x的父节点是红色,此时祖父节点一定为黑色 while (x != null && x != root && x.parent.color == RED) { // 如果x的父节点为祖父节点的左孩子 if (parentOf(x) == leftOf(parentOf(parentOf(x)))) { // y指向x的叔叔节点 TreeMap.Entry<K,V> y = rightOf(parentOf(parentOf(x))); // 如果叔叔节点也是红色,则进行变色处理 if (colorOf(y) == RED) { // 父节点变成黑色 setColor(parentOf(x), BLACK); // 叔叔节点变成黑色 setColor(y, BLACK); // 祖父节点变成黑色 setColor(parentOf(parentOf(x)), RED); // 将x指向祖父节点,继续往上调整 x = parentOf(parentOf(x)); } else { // 如果叔叔节点是黑色节点 // 如果x是父节点的右孩子 if (x == rightOf(parentOf(x))) { // 将x指向其父节点 x = parentOf(x); // 左旋 rotateLeft(x); } // 将x的父节点置为黑色 setColor(parentOf(x), BLACK); // 将x的祖父节点置为红色 setColor(parentOf(parentOf(x)), RED); // 将祖父节点右旋 rotateRight(parentOf(parentOf(x))); } } else { // 这里类似操作 TreeMap.Entry<K,V> y = leftOf(parentOf(parentOf(x))); if (colorOf(y) == RED) { setColor(parentOf(x), BLACK); setColor(y, BLACK); setColor(parentOf(parentOf(x)), RED); x = parentOf(parentOf(x)); } else { if (x == leftOf(parentOf(x))) { x = parentOf(x); rotateRight(x); } setColor(parentOf(x), BLACK); setColor(parentOf(parentOf(x)), RED); rotateLeft(parentOf(parentOf(x))); } } } root.color = BLACK; } 说完了插入,再来看看删除操作。 // 删除节点 public V remove(Object key) { // 先找到该key对应的键值对 TreeMap.Entry<K,V> p = getEntry(key); if (p == null) // 如果未找到则返回null return null; V oldValue = p.value; // 找到后删除该键值对 deleteEntry(p); return oldValue; } final TreeMap.Entry<K,V> getEntry(Object key) { // 为了性能,卸载了比较器的版本 if (comparator != null) return getEntryUsingComparator(key); if (key == null) throw new NullPointerException(); @SuppressWarnings("unchecked") Comparable<? super K> k = (Comparable<? super K>) key; TreeMap.Entry<K,V> p = root; // 使用compareTo方法进行查找 while (p != null) { int cmp = k.compareTo(p.key); if (cmp < 0) p = p.left; else if (cmp > 0) p = p.right; else return p; } return null; } // 使用比较器的getEntry版本。 从getEntry分离以获得性能。 // (对于大多数方法而言,这不值得做,这些方法较少依赖于比较器性能,但在这里是值得的。) final TreeMap.Entry<K,V> getEntryUsingComparator(Object key) { @SuppressWarnings("unchecked") K k = (K) key; Comparator<? super K> cpr = comparator; // 使用比较器进行二分查找 if (cpr != null) { TreeMap.Entry<K,V> p = root; while (p != null) { int cmp = cpr.compare(k, p.key); if (cmp < 0) p = p.left; else if (cmp > 0) p = p.right; else return p; } } return null; } /** * 删除节点,并调整红黑树以保持它的平衡 */ private void deleteEntry(TreeMap.Entry<K,V> p) { modCount++; size--; // 如果p的左右孩子均不为空,则找到p的后继节点,并且将p指向该后继节点 if (p.left != null && p.right != null) { TreeMap.Entry<K,V> s = successor(p); p.key = s.key; p.value = s.value; p = s; } // p has 2 children // 修复替补节点 // 用替补节点替换待删除的节点后,需要对其原来所在位置结构进行修复 TreeMap.Entry<K,V> replacement = (p.left != null ? p.left : p.right); if (replacement != null) { replacement.parent = p.parent; if (p.parent == null) root = replacement; else if (p == p.parent.left) p.parent.left = replacement; else p.parent.right = replacement; p.left = p.right = p.parent = null; // 如果p的颜色是黑色,则进行删除后的修复 if (p.color == BLACK) fixAfterDeletion(replacement); } else if (p.parent == null) { root = null; } else { if (p.color == BLACK) fixAfterDeletion(p); if (p.parent != null) { if (p == p.parent.left) p.parent.left = null; else if (p == p.parent.right) p.parent.right = null; p.parent = null; } } } /** * 返回指定节点的后继节点 */ static <K,V> TreeMap.Entry<K,V> successor(TreeMap.Entry<K,V> t) { if (t == null) return null; else if (t.right != null) { TreeMap.Entry<K,V> p = t.right; // 如果右子树不为空,则找到右子树的最左节点作为后继节点 while (p.left != null) p = p.left; return p; } else { TreeMap.Entry<K,V> p = t.parent; TreeMap.Entry<K,V> ch = t; // 如果右子树为空且当前节点为其父节点的左孩子,则直接返回 // 如果为其父节点的右孩子,则一直往上找,直到找到根节点或者当前节点为其父节点的左孩子时,用其做为后继节点 while (p != null && ch == p.right) { ch = p; p = p.parent; } return p; } } /** * 进行删除后的结构修复 * @param x */ private void fixAfterDeletion(TreeMap.Entry<K,V> x) { while (x != root && colorOf(x) == BLACK) { // 如果x是父节点的左孩子 if (x == leftOf(parentOf(x))) { // sib指向x的兄弟节点 TreeMap.Entry<K,V> sib = rightOf(parentOf(x)); // 如果sib是红色,则进行变色处理 if (colorOf(sib) == RED) { // 兄弟节点改为黑色 setColor(sib, BLACK); // 父节点改为红色 setColor(parentOf(x), RED); // 父节点左旋 rotateLeft(parentOf(x)); // sib指向x的父节点的右孩子 sib = rightOf(parentOf(x)); } // 如果sib的左孩子和右孩子都是黑色,则进行变色处理 if (colorOf(leftOf(sib)) == BLACK && colorOf(rightOf(sib)) == BLACK) { // 将sib置为红色 setColor(sib, RED); // x指向其父节点 x = parentOf(x); } else { // 如果sib的右孩子是黑色而左孩子是红色,则变色右旋 if (colorOf(rightOf(sib)) == BLACK) { setColor(leftOf(sib), BLACK); setColor(sib, RED); rotateRight(sib); sib = rightOf(parentOf(x)); } // 变色左旋 setColor(sib, colorOf(parentOf(x))); setColor(parentOf(x), BLACK); setColor(rightOf(sib), BLACK); rotateLeft(parentOf(x)); x = root; } } else { // symmetric // 跟上面操作类似 TreeMap.Entry<K,V> sib = leftOf(parentOf(x)); if (colorOf(sib) == RED) { setColor(sib, BLACK); setColor(parentOf(x), RED); rotateRight(parentOf(x)); sib = leftOf(parentOf(x)); } if (colorOf(rightOf(sib)) == BLACK && colorOf(leftOf(sib)) == BLACK) { setColor(sib, RED); x = parentOf(x); } else { if (colorOf(leftOf(sib)) == BLACK) { setColor(rightOf(sib), BLACK); setColor(sib, RED); rotateLeft(sib); sib = leftOf(parentOf(x)); } setColor(sib, colorOf(parentOf(x))); setColor(parentOf(x), BLACK); setColor(leftOf(sib), BLACK); rotateRight(parentOf(x)); x = root; } } } setColor(x, BLACK); } 嗯,对比一下HashMap的删除操作,核心步骤是完全一样的,所以可以对照前面的HashMap红黑树详解进行食用。 到此,这一篇就很水的讲完啦= = 最近这段时间烦心事比较多,对发展方向也考虑了很多,想做的事情很多,反而让我止步不前,不过很多事情是急不来的,还是好好写写博客,多做总结分享吧。 机会只留给有准备的人。 真正重要的东西,用眼睛是看不见的。

优秀的个人博客,低调大师

Golang 入门系列(二) Go语言基础语法及需要注意的坑

上一章节我们已经了解了 Go 环境的配置,不了解的,请查看前面的文章https://www.cnblogs.com/zhangweizhong/p/9459945.html,本章节我们将学习 Go 语言的基础语法中需要注意的点。 GO语言基础语法 go 的基础语法,我这里就不细说了,大家可以查看这个文章,学习Go 详细的语法:http://www.runoob.com/go/go-basic-syntax.html 最好是对照上面的例子,一个一个敲出来,这样效果最好。 下面是一个Go 程序的基本结构,包含(包声明,引入包,函数等) package main // 定义包名,package main表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包。 import "fmt" // 导入需要使用的包(的函数,或其他元素) func main() { // 程序的入口函数。main 函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数。 fmt.Println("Hello, World!") } GO语言注意的坑 不管学啥,刚开始都会遇到各种各样的坑。下面就来总结下学习go语言的过程中,遇到的各种坑。 1. 写C# 的人都会将“{” 独立一行,但是这在go 里面是错误的 “{” 必须更方法体 在同一行。我第一次写go 的就犯了这个错误,还不知道错误在哪。 func main() { fmt.Println("Hello, World!") } 2.if…else 语句中的 else 必须和 if 的 ’ } ’ 在同一行,否则编译错误 var a int = 30 if a < 20 { fmt.Print("a<20") } else { fmt.Print("a>=20") } 2. 包名的定义。你必须在源文件中非注释的第一行声明包名,如:package main。package main表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包。 package main 3.在 Go 程序中,一行代表一个语句结束。每个语句不需要像 C 家族中的其它语言一样以分号 ; 结尾,因为这些工作都将由 Go 编译器自动完成。 如果你打算将多个语句写在同一行,则必须使用 ; 人为区分,但在实际开发中我们并不鼓励这种做法。 fmt.Println("Hello, World!") fmt.Println("www.fpeach.com") 3.main()函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数。然而,每个package 中,只能有一个main() 函数,否则会报main redeclared in this block previous declaration at .. 的错误。 package main import "fmt" func main() { /* 这是我的第一个简单的程序 */ fmt.Println("Hello, World!") } 4. 当函数、结构等标识符以一个大写字母开头,如:GetInfo,那么使用这种形式的标识符的对象就可以被外部包的代码所使用,这被称为导出(像面向对象语言中的 public);标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 protected )。 // 公有函数,可以被外部包的代码所使用 func Test() { . . . } // 私有函数,包的内部是可见、func test2() { . . . } 5. 标识符用来命名变量、类型等程序实体。一个标识符实际上就是一个或是多个字母(A~Z和a~z)数字(0~9)、下划线_组成的序列,但是第一个字符必须是字母或下划线而不能是数字。 以下是无效的标识符: 1ab(以数字开头) case(Go 语言的关键字) a+b(运算符是不允许的) 6. 错误 no new variables on left side of := ,意思是,“左边一个新的变量也没有!” func main() { var b int = 20 b := 30 fmt.Print(b) } 解决办法就是:对于x,y:=....这种形式,只要把其中的一个变量命名成新的就可以了。 7.不能使用++自增或- -自减运算符初始化变量和对变量赋值 package main import "fmt" func main(){ var a int = 10 var b int = a++ var c int = 20 c = a++ fmt.Print(a, b, c) } 作者:章为忠 出处:http://www.fpeach.com/ 本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。如有问题,可以微信:18618243664联系我,非常感谢。扫下面的二维码关注我的微信公众号。

优秀的个人博客,低调大师

【Java入门提高篇】Day20 Java容器类详解(三)List接口

今天要说的是Collection族长下的三名大将之一,List,Set,Queue中的List,它们都继承自Collection接口,所以Collection接口的所有操作,它们自然也是有的。 List,Set,Queue,分别是列表,集合,队列的意思,代表着Collection家族下的三种不同的势力,它们各有所长,也各有所短,就像骑兵,步兵和水兵,各有各的优势,并没有谁一定比谁更好的说法,合适的才是最好的。接下来,将会分别介绍这三名大将,从中你也会看到它们各自的特点。 本篇先来介绍一下List接口。 我们先来看看List的源码: public interface List<E> extends Collection<E> { // 查询接口 /** * 列表元素个数 */ int size(); /** * 是否为空 */ boolean isEmpty(); /** * 是否包含某元素 */ boolean contains(Object o); /** * 返回一个List迭代器 */ Iterator<E> iterator(); /** * 将List转换为Object数组 */ Object[] toArray(); /** * 转换为指定类型数组 */ <T> T[] toArray(T[] a); // 修改操作 /** * 添加元素,成功返回true */ boolean add(E e); /** * 移除某一个元素,成功返回true */ boolean remove(Object o); // 批量操作 /** * 判断是否包含集合C 中的所有元素 */ boolean containsAll(Collection<?> c); /** * 将集合C 中所有元素添加到列表 */ boolean addAll(Collection<? extends E> c); /** * 将集合C 中所有元素添加到列表,添加在序号为index的元素之后 */ boolean addAll(int index, Collection<? extends E> c); /** * 从列表中移除集合C 中所有元素 */ boolean removeAll(Collection<?> c); /** * 从列表中移除所有不在集合C 中的元素 */ boolean retainAll(Collection<?> c); /** * 全部替换 */ default void replaceAll(UnaryOperator<E> operator) { Objects.requireNonNull(operator); final ListIterator<E> li = this.listIterator(); while (li.hasNext()) { li.set(operator.apply(li.next())); } } /** * 根据指定的比较器来排序,如果传入的比较器是null,则元素必须实现Comparable 接口 */ @SuppressWarnings({"unchecked", "rawtypes"}) default void sort(Comparator<? super E> c) { Object[] a = this.toArray(); Arrays.sort(a, (Comparator) c); ListIterator<E> i = this.listIterator(); for (Object e : a) { i.next(); i.set((E) e); } } /** * 移除所有元素 */ void clear(); // 比较和hash boolean equals(Object o); int hashCode(); // 根据序号进行的操作 /** * 获取指定序号的元素 */ E get(int index); /** * 替换指定序号的元素 */ E set(int index, E element); /** * 在指定序号的元素后插入元素 */ void add(int index, E element); /** * 移除指定序号的元素 */ E remove(int index); // 搜索操作 /** * 返回元素第一次出现的位置,如果未找到则返回-1 */ int indexOf(Object o); /** * 返回元素出现的最后一个位置 */ int lastIndexOf(Object o); // List迭代器 /** * 返回一个List迭代器 */ ListIterator<E> listIterator(); /** * 返回一个序号从Index开始的List迭代器 */ ListIterator<E> listIterator(int index); // 视图 /** * 返回一个子队列,序列从fromIndex到toIndex,包含fromIndex,不包含toIndex * 对子队列的修改会影响原队列 * 如果原队列修改,那么对子队列的影响是未定义的 */ java.util.List<E> subList(int fromIndex, int toIndex); /** * 创建一个可分割的迭代器(用于并行计算) */ @Override default Spliterator<E> spliterator() { return Spliterators.spliterator(this, Spliterator.ORDERED); } } 其实JDK里的注释已经十分丰富,大家平时有时间可以多看看,为了方便阅读,我这里用简单粗暴的语言进行了精简翻译。 List即列表,存储的是有序集合,里面的元素有序存储,可以重复,所谓有序集合,顾名思义,就是里面的元素存放是有顺序的,每个插入的元素都对应着一个序号,可以根据序号获取元素。 List支持的操作也很丰富,最常用的增删改查,批量添加,批量替换,批量删除,还有搜索,排序操作,还支持普通迭代器和可分割式迭代器,前者主要用于遍历,后者则主要用于并行式计算,关于迭代器的知识后面会统一介绍。下面是使用常见操作的一个小栗子: public class Test { public static void main(String[] args){ test(); } static void test(){ List<Integer> integers = new ArrayList<>(); List<Integer> integersA = new ArrayList<>(); //添加元素 integers.add(1); integers.add(2); integers.add(3); integers.add(4); integersA.add(1); integersA.add(2); integersA.add(33); integersA.add(44); System.out.println("列表大小:" + integers.size()); System.out.println("是否为空:" + integers.isEmpty()); System.out.println("是否包含某元素:" + integers.contains(2)); System.out.println("是否包含全部元素:" + integers.containsAll(integersA)); //转换为数组 Integer[] integerArray = integers.toArray(new Integer[0]); System.out.println("遍历数组:"); for (int i = 0; i < integerArray.length; i++){ System.out.println(integerArray[i]); } System.out.println("当前列表integers:" + integers); //批量添加 System.out.println("批量添加元素"); integers.addAll(integersA); System.out.println("当前列表integers:" + integers); //移除元素 System.out.println("移除元素"); integers.remove(1); System.out.println("当前列表integers:" + integers); //批量移除 System.out.println("批量移除元素"); integers.removeAll(integersA); System.out.println("当前列表integers:" + integers); //开始替换 System.out.println("批量替换元素"); integers.replaceAll(it -> it + 1); System.out.println("当前列表integers:" + integers); //从列表中移除所有不在集合integersA中的元素 integersA.add(2); integersA.add(4); System.out.println("保留元素"); integers.retainAll(integersA); System.out.println("当前列表integers:" + integers); //插入 System.out.println("开始插入"); System.out.println("当前列表integersA:" + integersA); integersA.add(2,155); integersA.add(1,125); System.out.println("当前列表integersA:" + integersA); //排序 System.out.println("开始排序——使用内部比较器"); integersA.sort(null); System.out.println("当前列表integersA:" + integersA); System.out.println("开始排序——使用外部比较器"); integersA.sort((itA, itB) -> itB - itA); System.out.println("当前列表integersA:" + integersA); //序号操作 Integer a = integersA.get(2); System.out.println("integersA第三个元素是:" + a); System.out.println("开始替换"); integersA.set(3, 66); System.out.println("当前列表integersA:" + integersA); System.out.println("开始移除"); integersA.remove(3); System.out.println("当前列表integersA:" + integersA); //搜索操作 System.out.println("查找元素2(第一次出现)位置:" + integersA.indexOf(2)); System.out.println("查找元素2(最后一次出现)位置:" + integersA.lastIndexOf(2)); //子队列操作 List<Integer> subList = integersA.subList(0, 4); System.out.println("子队列:" + subList); subList.add(5); subList.add(5); subList.add(5); System.out.println("当前子列表:" + subList); System.out.println("当前列表integersA:" + integersA); integersA.add(1, 233); integersA.add(1, 233); integersA.add(1, 233); System.out.println("当前列表integersA:" + integersA); System.out.println("当前子列表:" + subList); } } 大家可以先想想结果,再下看面的答案。 实际输出如下: 列表大小:4 是否为空:false 是否包含某元素:true 是否包含全部元素:false 遍历数组: 1 2 3 4 当前列表integers:[1, 2, 3, 4] 批量添加元素 当前列表integers:[1, 2, 3, 4, 1, 2, 33, 44] 移除元素 当前列表integers:[1, 3, 4, 1, 2, 33, 44] 批量移除元素 当前列表integers:[3, 4] 批量替换元素 当前列表integers:[4, 5] 保留元素 当前列表integers:[4] 开始插入 当前列表integersA:[1, 2, 33, 44, 2, 4] 当前列表integersA:[1, 125, 2, 155, 33, 44, 2, 4] 开始排序——使用内部比较器 当前列表integersA:[1, 2, 2, 4, 33, 44, 125, 155] 开始排序——使用外部比较器 当前列表integersA:[155, 125, 44, 33, 4, 2, 2, 1] integersA第三个元素是:44 开始替换 当前列表integersA:[155, 125, 44, 66, 4, 2, 2, 1] 开始移除 当前列表integersA:[155, 125, 44, 4, 2, 2, 1] 查找元素33(第一次出现)位置:4 查找元素33(最后一次出现)位置:5 子队列:[155, 125, 44, 4] 当前子列表:[155, 125, 44, 4, 5, 5, 5] 当前列表integersA:[155, 125, 44, 4, 5, 5, 5, 2, 2, 1] 当前列表integersA:[155, 233, 233, 233, 125, 44, 4, 5, 5, 5, 2, 2, 1] Exception in thread "main" java.util.ConcurrentModificationException at java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1239) at java.util.ArrayList$SubList.listIterator(ArrayList.java:1099) at java.util.AbstractList.listIterator(AbstractList.java:299) at java.util.ArrayList$SubList.iterator(ArrayList.java:1095) at java.util.AbstractCollection.toString(AbstractCollection.java:454) at java.lang.String.valueOf(String.java:2994) at java.lang.StringBuilder.append(StringBuilder.java:131) at com.frank.chapter20.Test.test(Test.java:115) at com.frank.chapter20.Test.main(Test.java:15) 不知道符不符合你的预期,这里关于内部比较器和外部比较器的知识只一笔带过,Integer类型是实现了Comparable接口的,所以sort方法传入null时会使用Integer的内部比较器进行排序,而使用外部比较器时,使用的是Java8的新特性,lamada表达式,省去了方法名和参数类型,因为函数式接口不存在重载方法,所以编译器可以推断出参数类型,这样就不用再大费周章的用new语法去创建一个比较器(当然,只是语法糖而已,如果不是很理解比较器,可以先行百度,后面的文章里也会有详细介绍)。在最后报出了一个ConcurrentModificationException,因为原队列修改后,子队列视图就被破坏了,所以再次访问子视图时就会报错。 List是最常用的容器类,List最大的特点便是要求元素有序存储,List跟数组相比,最大的优势在于List大小可以动态扩展,但数组支持随机存取,所以当元素个数的固定的时候,使用数组往往效率更高。(当然,一般情况下还是使用List吧,因为支持的操作更加丰富,比如进行排序时不需要自己写算法)。 一般来说,对元素没有特殊要求,不需要去重存储,没有先进先出的要求的场景下,List是最好的选择。 List接口下有多个常用的实现类,每个类都有其特点,具体选择哪种类需要根据实际情况进行选择。 希望大家能通过这篇文章,了解List的主要方法及其使用方法以及常用场景,关于List的常见具体实现类的讲解将在之后的文章里进行说明和比较。 本篇到此结束,欢迎大家继续关注。 真正重要的东西,用眼睛是看不见的。

优秀的个人博客,低调大师

【Java入门提高篇】Day19 Java容器类详解(二)Map接口

上一篇里介绍了容器家族里的大族长——Collection接口,今天来看看容器家族里的二族长——Map接口。 Map也是容器家族的一个大分支,但里面的元素都是以键值对(key-value)的形式存放的,就像字典一样,用相应的key就可以拿到相应的value。 先来看看Map接口的内容,下面是阉割版的Map接口(去掉了defaultmethod),去掉的部分涉及Stream操作,属于Map的高级用法,所以暂时不做介绍。 import java.io.Serializable; import java.util.Collection; import java.util.Comparator; import java.util.Objects; import java.util.Set; public interface Map<K,V> { // 查询操作 /** * 返回键值对数量 */ int size(); /** * Map是否为空 */ boolean isEmpty(); /** * Map中是否包含指定的key */ boolean containsKey(Object key); /** * Map中是否包含指定的value */ boolean containsValue(Object value); /** * 根据key获取对应的value */ V get(Object key); // Modification Operations /** * 插入键值对,如果Map中已经存在该key,则新的value会覆盖原来的value */ V put(K key, V value); /** * 移除指定key对应的键值对,并返回相应的value */ V remove(Object key); // 批量操作 /** * 将另一个Map中的键值对全部复制过来 */ void putAll(Map<? extends K, ? extends V> m); /** * 移除所有键值对 */ void clear(); // 视图 /** * 返回包含Map中所有key的(Set类型)键视图,对Map的修改也会影响到键视图 */ Set<K> keySet(); /** * 返回包含Map中所有value的(Collection类型)值视图,对Map的修改也会影响到值视图 */ Collection<V> values(); /** * 返回包含Map中所有键值对的(java.util.Map.Entry类型)键值对视图 */ Set<Map.Entry<K, V>> entrySet(); /** * Map 键值对接口 */ interface Entry<K,V> { /** * 返回键 */ K getKey(); /** * 返回值 */ V getValue(); /** * 设置键 */ V setValue(V value); boolean equals(Object o); int hashCode(); /** * 键比较器(内部比较器) */ public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() { return (Comparator<Map.Entry<K, V>> & Serializable) (c1, c2) -> c1.getKey().compareTo(c2.getKey()); } /** * 值比较器(内部比较器) */ public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K,V>> comparingByValue() { return (Comparator<Map.Entry<K, V>> & Serializable) (c1, c2) -> c1.getValue().compareTo(c2.getValue()); } /** * 键比较器(外部比较器) */ public static <K, V> Comparator<Map.Entry<K, V>> comparingByKey(Comparator<? super K> cmp) { Objects.requireNonNull(cmp); return (Comparator<Map.Entry<K, V>> & Serializable) (c1, c2) -> cmp.compare(c1.getKey(), c2.getKey()); } /** * 值比较器(外部比较器) */ public static <K, V> Comparator<Map.Entry<K, V>> comparingByValue(Comparator<? super V> cmp) { Objects.requireNonNull(cmp); return (Comparator<Map.Entry<K, V>> & Serializable) (c1, c2) -> cmp.compare(c1.getValue(), c2.getValue()); } } // 比较和散列 boolean equals(Object o); int hashCode(); } 可以看到,Map接口的内容,其实比Collection接口更丰富,这里因为省略了很多高级方法,而且里面包含了另外一个接口,Map.Entry接口,也就是一直所说的键值对,这个接口是Map中元素需要实现的接口。 Map有三种遍历方式:1.通过遍历KeySet来遍历所有键值对,2.通过遍历EntrySet来实现,3.通过EntrySet的Iterator来遍历。这里还有一个新概念——视图,视图其实就是一个集合,但是是一个不能修改的集合,只能对视图进行查询和遍历操作,在Map中一共有三个视图,键视图,值视图,键值对视图,下面可以看一个小栗子: public class Test { public static void main(String[] args){ Map<Integer, Integer> map = new HashMap<>(); map.put(1,11); map.put(2,22); map.put(3,33); Set<Integer> keys = map.keySet(); Collection<Integer> values = map.values(); Set<Map.Entry<Integer,Integer>> entries = map.entrySet(); Iterator<Map.Entry<Integer,Integer>> iterator = entries.iterator(); System.out.println(keys); System.out.println(values); System.out.println(entries); System.out.println("按keyset遍历"); for (Integer key : keys){ System.out.println("key:" + key + " value:" + map.get(key)); } System.out.println("按键值对遍历"); for (Map.Entry<Integer,Integer> entry : entries){ System.out.println("entry:" + entry); } System.out.println("按iterator遍历"); while (iterator.hasNext()){ Map.Entry<Integer,Integer> entry = iterator.next(); System.out.println("entry:" + entry); } map.put(2,444); map.put(4,44); System.out.println("修改后的视图"); System.out.println(keys); System.out.println(values); System.out.println(entries); keys.add(5); values.add(55); } } 输出如下: [1, 2, 3] [11, 22, 33] [1=11, 2=22, 3=33] 按keyset遍历 key:1 value:11 key:2 value:22 key:3 value:33 按键值对遍历 entry:1=11 entry:2=22 entry:3=33 按iterator遍历 entry:1=11 entry:2=22 entry:3=33 修改后的视图 [1, 2, 3, 4] [11, 444, 33, 44] [1=11, 2=444, 3=33, 4=44] Exception in thread "main" java.lang.UnsupportedOperationException at java.util.AbstractCollection.add(AbstractCollection.java:262) at com.frank.chapter19.Test.main(Test.java:44) 栗子里介绍了三种遍历方式,也看到了三种视图的样子,当我们试图修改视图时,抛出了一个UnsupportedOperationException异常,表明该视图集合无法修改。 在Map.Entry接口里,还可以看到外部比较器和内部比较器,这两个概念暂时也不做介绍,在之后的文章里会介绍。 关于Map,要说的主要就这么多了,目前来说只需要知道Map是以键值对的形式进行存取,并了解Map接口中的主要方法及其作用,了解Map的遍历方法,和视图的概念就已经足够了。 本篇到此结束,欢迎大家继续关注。 真正重要的东西,用眼睛是看不见的。

优秀的个人博客,低调大师

【Java入门提高篇】Day18 Java容器类详解(一)Collection接口

今天来看看Java里的一个大家伙,那就是容器。 所谓容器,就是专门用来装对象的东西,如果你学过高数,没错,就跟里面说的集合是一个概念,就是一堆对象的集合体,但是集合类是容器类中的一个子集,为了区别表示,所以还是叫容器类,之后所说的集合类只是容器里的一个子集,之后会有详细介绍。 容器就是用来存放和管理其他类对象的地方,你可以把它理解为仓库管家,当你有东西需要存放和管理的时候,就要记得来找它。你也许会说,不是有数组吗?确实,用数组存放一堆相同类型对象也是一个不错的选择,但是有一个很大的缺陷,那就是数组大小只能是固定的,不能从数组里动态添加和删除一个对象,要扩容的时候,就只能新建一个数组然后把原来的对象全部复制到新的数组里,而且只能存放相同类型的对象,使用起来不够灵活。然而我们的管家就不一样了。 国际惯例,先来看一个栗子: public class Test { public static void main(String args[]){ //小明打算学Java,买了三本书 Book bookA = new Book("Java核心技术(卷一)", 88.9); Book bookB = new Book("Java核心技术(卷二)", 88.6); Book bookC = new Book("Java编程思想", 99.0); //他想了想,放哪呢?到处放怕之后会找不到,放书架以后书变多了找起来就很麻烦 //于是他找了个管家 Map<String, Book> bookMap = new HashMap<>(3); //然后跟管家说,这三本书先放你这了,要用的时候找你拿 bookMap.put(bookA.getName(), bookA); bookMap.put(bookB.getName(), bookB); bookMap.put(bookC.getName(), bookC); //勤劳的管家兢兢业业的保存好了三本书 //小明回到家,想检查一下管家老不老实 //“管家,把Java核心技术(卷一)给我拿过来” Book bookD = bookMap.get("Java核心技术(卷一)"); //他查看了一下这本书的信息并跟原来的信息校验了一番 System.out.println(bookD); System.out.println(bookA.equals(bookD)); //并同样校验了另外两本书 Book bookE = bookMap.get("Java核心技术(卷二)"); System.out.println(bookE); System.out.println(bookB.equals(bookE)); Book bookF = bookMap.get("Java编程思想"); System.out.println(bookF); System.out.println(bookC.equals(bookF)); //嗯,看来管家没有玩花样,还是原来的书,晚饭给他加个蛋 } } class Book{ private String name; private Double price; public Book(String name, Double price) { this.name = name; this.price = price; } public Double getPrice() { return price; } public void setPrice(Double price) { this.price = price; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Book{" + "name='" + name + '\'' + ", price=" + price + '}'; } } 输出如下: Book{name='Java核心技术(卷一)', price=88.9} true Book{name='Java核心技术(卷二)', price=88.6} true Book{name='Java编程思想', price=99.0} true 相信大家看过小明和管家的故事之后,对容器这个概念应该有初步的了解了。容器一般来说就是在你需要存放一系列对象时,可以给你管理对象的好管家。 当然,容器家族里并不只有HashMap这一个管家,最开始就说了,容器可是一个庞大的家族。先来看一张图感受一下吧: 好像有点多?关系有点复杂。没错,除了并发包里的集合类以外的大部分容器类差不多都在这了,这个图,emmmm...看看就好,我们还是看下面这个图吧 别慌,其实最常用的就是这么几个了,Collection和Map是两个大的接口,Collection下有三个子接口,List,Queue,Set,下面是最常用的三个类,ArrayList,LinkedList,HashSet。Map接口下最常用的就要数上面栗子里的HashMap了。正如你看到的那样,容器类里有很多不同的实现类,也就是不同的管家,他们有的不同的能力,各有所长也各有所短,至于他们的具体介绍,将会留到之后的几篇文章里介绍,本篇作为集合的介绍篇就不多做讲解了。 需要注意的是,容器中只能存放对象,而不能存放基本类型。所以当你将一个 int 型数据 1放入容器中的时候,其实它会自动装箱转换成 Integer 类后存入的,Java中每一种基本类型都有对应的引用类型。在容器存放的是多个对象的引用,对象本身还是放在堆内存中。容器可以存放不同类型,不限数量的数据类型。 Collection接口 Collection接口是容器家族里的老大哥,是最基本的容器接口,但是这里的Collection跟容器并不是等价关系,因为你仔细看看上面的图就知道,容器家族里还有另外一个老大哥,那就是Map接口。一个Collection代表一组Object,即Collection的元素(Elements)。Collection接口下有三个子接口,分别是List,Set,Queue,它们各有各的特点,下面会一一介绍,但是都继承于Collection接口,所以继承了Collection的所有特性。 我们可以来看看Collection接口都有哪些方法: public interface Collection<E> extends Iterable<E> { //查询操作 /** * 返回集合中元素个数 */ int size(); /** * 集合是否为空 */ boolean isEmpty(); /** * 是否包含某个元素 */ boolean contains(Object o); /** * 取迭代器 */ @Override Iterator<E> iterator(); /** * 转到数组 */ Object[] toArray(); /** * 转到指定数组 */ <T> T[] toArray(T[] a); // 修改操作 /** * 添加元素 */ boolean add(E e); /** * 移除元素 */ boolean remove(Object o); // 批量操作 /** * 是否全部包含 */ boolean containsAll(Collection<?> c); /** * 全部添加 */ boolean addAll(Collection<? extends E> c); /** * 全部移除 */ boolean removeAll(Collection<?> c); /** * 条件移除 */ default boolean removeIf(Predicate<? super E> filter) { Objects.requireNonNull(filter); boolean removed = false; final Iterator<E> each = iterator(); while (each.hasNext()) { if (filter.test(each.next())) { each.remove(); removed = true; } } return removed; } /** * 保留全部 */ boolean retainAll(Collection<?> c); /** * 清空 */ void clear(); // 比较和哈希 /** * 比较是否相等 */ boolean equals(Object o); /** * 取哈希值 */ int hashCode(); @Override default Spliterator<E> spliterator() { return Spliterators.spliterator(this, 0); } //流操作 default Stream<E> stream() { return StreamSupport.stream(spliterator(), false); } default Stream<E> parallelStream() { return StreamSupport.stream(spliterator(), true); } } 可以看出,Collection的操作还是蛮多的,增删该查和批量操作都有,至于迭代器是什么东西,后面的篇章会有详细介绍。最后两个方法涉及到了流操作,这是Java8里新添加的特性,关于流操作的知识,这里暂时不多说,以后在做讲解。 通过本篇,你只需要了解一下集合是什么,为什么要有集合,集合家族的全貌,了解一下Collection接口中有哪些方法就足够了,之后的文章会从以下几方面来介绍容器家族: 1.Map接口 2.Iterable接口 3.List,Set,Queue接口 4.ArrayList使用方式和应用场景+源码剖析 5.HashSet使用方式和应用场景+源码剖析 6.LinkedList使用方式和应用场景+源码剖析 7.HashMap使用方式和应用场景+源码剖析 今天的讲解就到此为止了,仅仅介绍了容器的基本概念,作为容器学习的开胃菜,后面一系列的文章都会围绕容器展开,希望大家继续关注!真正重要的东西,用眼睛是看不见的。

优秀的个人博客,低调大师

Storm编程入门API系列之Storm的Topology多个Workers数目控制实现

继续编写 StormTopologyMoreWorker.java package zhouls.bigdata.stormDemo; import java.util.Map; import org.apache.storm.Config; import org.apache.storm.LocalCluster; import org.apache.storm.StormSubmitter; import org.apache.storm.generated.AlreadyAliveException; import org.apache.storm.generated.AuthorizationException; import org.apache.storm.generated.InvalidTopologyException; import org.apache.storm.spout.SpoutOutputCollector; import org.apache.storm.task.OutputCollector; import org.apache.storm.task.TopologyContext; import org.apache.storm.topology.OutputFieldsDeclarer; import org.apache.storm.topology.TopologyBuilder; import org.apache.storm.topology.base.BaseRichBolt; import org.apache.storm.topology.base.BaseRichSpout; import org.apache.storm.tuple.Fields; import org.apache.storm.tuple.Tuple; import org.apache.storm.tuple.Values; import org.apache.storm.utils.Utils; public class StormTopologyMoreWorker { public static class MySpout extends BaseRichSpout{ private Map conf; private TopologyContext context; private SpoutOutputCollector collector; public void open(Map conf, TopologyContext context, SpoutOutputCollector collector) { this.conf = conf; this.collector = collector; this.context = context; } int num = 0; public void nextTuple() { num++; System.out.println("spout:"+num); this.collector.emit(new Values(num)); Utils.sleep(1000); } public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(new Fields("num")); } } public static class MyBolt extends BaseRichBolt{ private Map stormConf; private TopologyContext context; private OutputCollector collector; public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) { this.stormConf = stormConf; this.context = context; this.collector = collector; } int sum = 0; public void execute(Tuple input) { Integer num = input.getIntegerByField("num"); sum += num; System.out.println("sum="+sum); } public void declareOutputFields(OutputFieldsDeclarer declarer) { } } public static void main(String[] args) { TopologyBuilder topologyBuilder = new TopologyBuilder(); String spout_id = MySpout.class.getSimpleName(); String bolt_id = MyBolt.class.getSimpleName(); topologyBuilder.setSpout(spout_id, new MySpout()); topologyBuilder.setBolt(bolt_id, new MyBolt()).shuffleGrouping(spout_id); Config config = new Config(); config.setNumWorkers(2); String topology_name = StormTopologyMoreWorker.class.getSimpleName(); if(args.length==0){ //在本地运行 LocalCluster localCluster = new LocalCluster(); localCluster.submitTopology(topology_name, config, topologyBuilder.createTopology()); }else{ //在集群运行 try { StormSubmitter.submitTopology(topology_name, config, topologyBuilder.createTopology()); } catch (AlreadyAliveException e) { e.printStackTrace(); } catch (InvalidTopologyException e) { e.printStackTrace(); } catch (AuthorizationException e) { e.printStackTrace(); } } } } 打jar包 [hadoop@master jar]$ pwd /home/hadoop/app/apache-storm-1.0.2/jar [hadoop@master jar]$ ll total 8 -rw-r--r-- 1 hadoop hadoop 4869 Jul 27 22:17 StormTopology.jar [hadoop@master jar]$ rz [hadoop@master jar]$ ll total 16 -rw-r--r-- 1 hadoop hadoop 4869 Jul 27 22:17 StormTopology.jar -rw-r--r-- 1 hadoop hadoop 4992 Jul 27 22:39 StormTopologyMoreWorker.jar 提交作业之前 [hadoop@master apache-storm-1.0.2]$ pwd /home/hadoop/app/apache-storm-1.0.2 [hadoop@master apache-storm-1.0.2]$ ll total 208 drwxrwxr-x 2 hadoop hadoop 4096 May 21 17:18 bin -rw-r--r-- 1 hadoop hadoop 82317 Jul 27 2016 CHANGELOG.md drwxrwxr-x 2 hadoop hadoop 4096 Jul 27 20:12 conf drwxrwxr-x 3 hadoop hadoop 4096 Jul 27 2016 examples drwxrwxr-x 17 hadoop hadoop 4096 May 21 17:18 external drwxrwxr-x 2 hadoop hadoop 4096 Jul 27 2016 extlib drwxrwxr-x 2 hadoop hadoop 4096 Jul 27 2016 extlib-daemon drwxrwxr-x 2 hadoop hadoop 4096 Jul 27 22:39 jar drwxrwxr-x 2 hadoop hadoop 4096 May 21 17:18 lib -rw-r--r-- 1 hadoop hadoop 32101 Jul 27 2016 LICENSE drwxrwxr-x 2 hadoop hadoop 4096 May 21 17:18 log4j2 drwxrwxr-x 2 hadoop hadoop 4096 May 21 19:05 logs -rw-r--r-- 1 hadoop hadoop 981 Jul 27 2016 NOTICE drwxrwxr-x 6 hadoop hadoop 4096 May 21 17:18 public -rw-r--r-- 1 hadoop hadoop 15287 Jul 27 2016 README.markdown -rw-r--r-- 1 hadoop hadoop 6 Jul 27 2016 RELEASE -rw-r--r-- 1 hadoop hadoop 23774 Jul 27 2016 SECURITY.md [hadoop@master apache-storm-1.0.2]$ bin/storm jar jar/StormTopologyMoreWorker.jar zhouls.bigdata.stormDemo.StormTopologyMoreWorker aaa Running: /home/hadoop/app/jdk/bin/java -client -Ddaemon.name= -Dstorm.options= -Dstorm.home=/home/hadoop/app/apache-storm-1.0.2 -Dstorm.log.dir=/home/hadoop/app/apache-storm-1.0.2/logs -Djava.library.path=/usr/local/lib:/opt/local/lib:/usr/lib -Dstorm.conf.file= -cp /home/hadoop/app/apache-storm-1.0.2/lib/log4j-api-2.1.jar:/home/hadoop/app/apache-storm-1.0.2/lib/kryo-3.0.3.jar:/home/hadoop/app/apache-storm-1.0.2/lib/storm-rename-hack-1.0.2.jar:/home/hadoop/app/apache-storm-1.0.2/lib/log4j-core-2.1.jar:/home/hadoop/app/apache-storm-1.0.2/lib/slf4j-api-1.7.7.jar:/home/hadoop/app/apache-storm-1.0.2/lib/minlog-1.3.0.jar:/home/hadoop/app/apache-storm-1.0.2/lib/objenesis-2.1.jar:/home/hadoop/app/apache-storm-1.0.2/lib/clojure-1.7.0.jar:/home/hadoop/app/apache-storm-1.0.2/lib/servlet-api-2.5.jar:/home/hadoop/app/apache-storm-1.0.2/lib/log4j-slf4j-impl-2.1.jar:/home/hadoop/app/apache-storm-1.0.2/lib/log4j-over-slf4j-1.6.6.jar:/home/hadoop/app/apache-storm-1.0.2/lib/storm-core-1.0.2.jar:/home/hadoop/app/apache-storm-1.0.2/lib/disruptor-3.3.2.jar:/home/hadoop/app/apache-storm-1.0.2/lib/asm-5.0.3.jar:/home/hadoop/app/apache-storm-1.0.2/lib/reflectasm-1.10.1.jar:jar/StormTopologyMoreWorker.jar:/home/hadoop/app/apache-storm-1.0.2/conf:/home/hadoop/app/apache-storm-1.0.2/bin -Dstorm.jar=jar/StormTopologyMoreWorker.jar zhouls.bigdata.stormDemo.StormTopologyMoreWorker aaa 6687 [main] INFO o.a.s.StormSubmitter - Generated ZooKeeper secret payload for MD5-digest: -5319119555480935017:-9144362215090188990 7204 [main] INFO o.a.s.s.a.AuthUtils - Got AutoCreds [] 7785 [main] INFO o.a.s.StormSubmitter - Uploading topology jar jar/StormTopologyMoreWorker.jar to assigned location: /home/hadoop/data/storm/nimbus/inbox/stormjar-ea1d6383-ca31-4033-b784-e06 856000894.jar 7875 [main] INFO o.a.s.StormSubmitter - Successfully uploaded topology jar to assigned location: /home/hadoop/data/storm/nimbus/inbox/stormjar-ea1d6383-ca31-4033-b784-e06856000894.jar 7876 [main] INFO o.a.s.StormSubmitter - Submitting topology StormTopologyMoreWorker in distributed mode with conf {"storm.zookeeper.topology.auth.scheme":"digest","storm.zookeeper.topology.auth.payload":"-5319119555480935017:-9144362215090188990","topology.workers":2} 8962 [main] INFO o.a.s.StormSubmitter - Finished submitting topology: StormTopologyMoreWorker [hadoop@master apache-storm-1.0.2]$ 提交之后 为什么,会是如上的数字呢?大家要学,就要深入去学和理解。 因为,我之前运行的StormTopology没有停掉 现在呢,我将之前运行的StormTopology给停掉,然后,再来看。非常重要 即提示,我们,30秒之后,kill掉。如果大家等不及,可以设置时间短些 为什么,会是如上的数字呢?大家要学,就要深入去学和理解。 本文转自大数据躺过的坑博客园博客,原文链接:http://www.cnblogs.com/zlslch/p/7247860.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android GIS开发系列-- 入门季(8) Json与Geometry的相互转换

在Android中json数据十分普遍,也很实用,在Arcgis中也同样支持Json数据,Json与Geometry可以相互转换,达到我们想要的数据。 一、Geometry转换成Json数据 这个实现十分简单,比如我们将一个点转换为Json,这时也同样用到GeometryEngine这个强大的类。 Point point = new Point(113, 23); String json = GeometryEngine.geometryToJson(SpatialReference.create(SpatialReference.WKID_WGS84), point); Log.w("TAG", "json===" + json); 打印Log的结果为json==={"x":113.0,"y":23.0,"spatialReference":{"wkid":4326}},是不是很简单。 二、Json转换为Geometry 同样用到GeometryEngine类中的jsonToGeometry方法,我们将上面的json再转换回去。 try { String jsonStr = "{\"x\":113.0,\"y\":23.0,\"spatialReference\":{\"wkid\":4326}}"; JsonFactory jsonFactory = new JsonFactory(); JsonParser jsonParser = jsonFactory.createJsonParser(jsonStr); MapGeometry mapGeometry = GeometryEngine.jsonToGeometry(jsonParser); Point mPoint = (Point) mapGeometry.getGeometry(); Log.i("TAG","mPoint---"+mPoint.getX()+"==="+mPoint.getY()); } catch (IOException e) { e.printStackTrace(); } 代码执行结果mPoint---113.0===23.0。注:jsonFactory.createJsonParser这个方法可带入的参数也是比较多的,比如:file、outputStream、byte数组等等,有兴趣的小伙伴可以研究研究。 没有整理与归纳的知识,一文不值!高度概括与梳理的知识,才是自己真正的知识与技能。 永远不要让自己的自由、好奇、充满创造力的想法被现实的框架所束缚,让创造力自由成长吧! 多花时间,关心他(她)人,正如别人所关心你的。理想的腾飞与实现,没有别人的支持与帮助,是万万不能的。 本文转自wenglabs博客园博客,原文链接http://www.cnblogs.com/arxive/p/7751932.html :,如需转载请自行联系原作者

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册