首页 文章 精选 留言 我的

精选列表

搜索[容器],共10000篇文章
优秀的个人博客,低调大师

Java容器深入浅出之List、ListIterator和ArrayList

List是Collection接口的子接口,表示的是一种有序的、可重复元素的集合。 List接口的主要实现类ArrayList和Vector,底层都是维护了一套动态的,可扩展长度的Object[]数组,通过initialCapacity参数来动态地调整长度。因此,相比较父接口Collection所提供的公共增删改方法,List接口及实现类也定义了通过索引来增删查改元素,或者基于元素查找索引的方法。更一般地,ArrayList中的元素可以为null。 List及List新增的ListIterator接口 List接口的定义方法如下 1 public class TestList { 2 3 @SuppressWarnings("unlikely-arg-type") 4 public static void main(String[] args) { 5 6 List<String> books = new ArrayList<>(); 7 //List集合添加元素的顺序是有序的 8 books.add(new String("资本论")); 9 books.add(new String("***宣言")); 10 books.add(new String("家庭私有制和国家的起源")); 11 System.out.println(books); 12 //add和addAll分别在指定索引处添加元素或另一个集合 13 books.add(1, "德意志意识形态"); 14 books.forEach(str -> System.out.println(str)); 15 //使用Lambda表达式对List进行排序 16 books.sort((o1, o2) -> (o1.length() - o2.length())); 17 System.out.println(books); 18 //删除指定索引的元素 19 books.remove(2); 20 System.out.println(books); 21 //通过指定索引返回元素(==根据HashCode来判断,此处返回false) 22 System.out.println(books.get(0) ==new String("资本论")); 23 //通过indexOf来返回指定元素所在的索引位置 24 //indexOf通过元素的equals方法来判断, 只要是true就返回对应的索引 25 System.out.println(books.indexOf(new String("德意志意识形态"))); 26 //通过索引改变元素 27 books.set(1, new String("神圣家族")); 28 //返回子集合[1,3), 不改变原集合 29 System.out.println(books.subList(1, 3)); 30 31 //使用Lambda表达式使用每个字符的长度替代原集合的元素 32 books.replaceAll(ele -> ele.length() + ""); 33 //因为A对象equals方法恒返回true,所以集合会在第一次比较中直接删除第一个元素 34 books.remove(new A()); 35 System.out.println(books); 36 //同样的,继续删除第一个元素 37 books.remove(new A()); 38 System.out.println(books.size()); 39 40 } 41 42 } 43 44 class A{ 45 //定义A类对象的equals方法恒返回true 46 public boolean equals(Object obj) { 47 return true; 48 } 49 } 与Set集合不同,List集合新增了一个继承Iterator接口的子接口ListIterator接口,可以实现反向迭代和增加元素的功能。 1 /* 2 * 与Collection集合的Iterator接口不同, List额外提供一个ListIterator接口(继承了Iterator接口), 增加了如下三个方法: 3 * 1. hasPrevious():是否还有上一个元素 4 * 2. previous():返回上一个元素 5 * 3. add():增加一个元素 6 */ 7 public class ListIteratorTest { 8 9 public static void main(String[] args) { 10 11 String[] books = { 12 "资本论","德意志意识形态","***宣言" 13 }; 14 List<String> booksList = new ArrayList<>(); 15 for(int i=0; i<books.length;i++) { 16 booksList.add(books[i]); 17 } 18 ListIterator<String> lit = booksList.listIterator(); 19 System.out.println("====下面开始正向迭代===="); 20 while(lit.hasNext()) { 21 System.out.println(lit.next()); 22 lit.add("----分隔符----"); 23 } 24 System.out.println("====下面开始反向迭代===="); 25 while(lit.hasPrevious()) { 26 System.out.println(lit.previous()); 27 } 28 29 } 30 31 } ArrayList和Vector 如前所述,ArrayList和Vector底层维护了一个动态可变的对象数组。如果创建对象时不指定容量值,会有一个默认的数组长度10。更一般地,可以通过在创建对象时指定容量值: 1. void ensureCapacity(int minCapacity):将List集合数组长度增加到minCapacity。如可提前知道元素个数,可以赋值长度,以减少频繁扩容所带来的性能开销。 2. void trimToSize():调整数组长度至当前元素的个数,以节省空间。 ArrayList与Vector相比较,需要注意以下几点: 1. ArrayList是线程不安全的,因此性能较高;Vector是线程安全的,但性能较差;后面的博文会介绍如果通过Collection提供的工具类,使ArrayList线程安全。 2. Vector及其子类Stack,是非常古旧的Java集合,并不建议使用。Stack子类所实现的栈功能,也可以通过ArrayDeque来实现。 固定长度的List 数组的工具类Arrays自带一个asList(Object... o)方法,可以将数组或指定数量的对象转换为List,不过需要注意一点: 该类是Arrays类的内部类ArrayList的实例,是固定长度的,相当于数组,只能用于遍历,不能增加和删除里面的元素。 1 public class TestArrayList { 2 3 public static void main(String[] args) { 4 5 List<String> list = Arrays.asList("AA", "BB", "CC"); 6 //显示类的类型为java.util.Arrays$ArrayList 7 System.out.println(list.getClass()); 8 9 list.forEach(System.out::println); 10 //以下会抛出运行时异常java.lang.UnsupportedOperationException 11 list.add("DD"); 12 13 } 14 15 } 线性表的性能选择 一般来说,基于数组的ArrayList在随机访问中性能较好;基于链表的LinkedList在插入、删除中性能较好。但在一般使用中,ArrayList总体性能占优,因此使用非常广泛。 1. 如果需要遍历List元素,ArrayList建议使用随机访问方法;对于LinkedList,建议使用Iterator迭代器 2. 如果需要经常插入、删除大量数据的List集合,可以考虑使用LinkedList

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

Java容器深入浅出之HashSet、TreeSet和EnumSet

Java集合中的Set接口,定义的是一类无顺序的、不可重复的对象集合。如果尝试添加相同的元素,add()方法会返回false,同时添加失败。Set接口包括3个主要的实现类:HashSet、TreeSet和EnumSet。 通过查看Java源码,事实上Java是先实现了Map,然后通过包装一个所有value都为null的集合,形成Set。 HashSet HashSet基于Hash算法实现,因此存取和查找的性能较好。HashSet的主要特点如下: 1. 无顺序的。与添加顺序不同,并且可变。 2. 线程不安全。 3. 集合元素可以是null 4. HashSet是通过元素的HashCode返回值,来确定元素存储位置。 5. 不可重复。HashSet判断元素是否重复的标准是:该元素对象的HashCode()返回值相等,并且equals()方法相等。换句话说,如果两个元素的equals方法相同,但HashCode返回值不相同,HashSet依然可以添加成功。因此,需要注意: 5.1 用Set类保存的元素,尽量保证其equals相等的同时,HashCode返回的值也相等。 5.2 当保存引用类型对象的时候,尽量不要修改其实例变量,否则可能会导致Set集合操作失灵。 1 /* 2 * 测试HashSet的对象添加规则 3 */ 4 class A{ 5 //重写类A的equals方法,恒返回true 6 public boolean equals(Object obj) { 7 return true; 8 } 9 } 10 class B{ 11 //重写类B的HashCode方法,恒返回1 12 public int hashCode() { 13 return 1; 14 } 15 } 16 class C{ 17 //重写类C的HashCode方法,恒返回2 18 public int hashCode() { 19 return 2; 20 } 21 //重写类C的equals方法,恒返回true 22 public boolean equals(Object obj) { 23 return true; 24 } 25 } 26 public class TestHashSet { 27 28 public static void main(String[] args) { 29 30 Set<Object> set = new HashSet<>(); 31 //equals相同,HashCode不相同: 32 //两个对象添加到hash表的不同位置 33 set.add(new A()); 34 set.add(new A()); 35 //HashCode相同, equals不同 36 //此时Hash表会在该HashCode位置,使用链式结构保存这些对象,导致性能下降. 37 set.add(new B()); 38 set.add(new B()); 39 //C类对象只被添加了一次. 40 set.add(new C()); 41 set.add(new C()); 42 43 System.out.println(set); 44 45 } 46 47 } LinkedHashSet LinkedHashSet是HashSet的子类。虽然同样按照元素的HashCode来确定存储位置,但该类同时使用链表来维护添加顺序,也因此,其性能稍低,但是迭代访问元素时优势较大。 TreeSet TreeSet底层采用的是红黑树的数据结构,并且是有序的集合。因此,TreeSet增加了访问第一个、前一个、后一个和最后一个的方法,并且提供了3个截取子树的方法。 TreeSet包括两种排序方法:自然排序和定制排序,默认使用自然排序。 1 public class TestTreeSet { 2 3 public static void main(String[] args) { 4 TreeSet<Integer> nums = new TreeSet<>(); 5 nums.add(5); 6 nums.add(2); 7 nums.add(10); 8 nums.add(-9); 9 //输出,此时已按自然排序 10 System.out.println(nums); 11 System.out.println(nums.first()); 12 System.out.println(nums.last()); 13 //返回<4的子序列 14 System.out.println(nums.headSet(4)); 15 //返回>=5的子序列 16 System.out.println(nums.tailSet(5)); 17 //返回[-3, 4)的子序列 18 System.out.println(nums.subSet(-3, 4)); 19 } 20 21 } TreeSet的自然排序 指的是TreeSet会调用元素的compareTo(Object o)方法来比较元素之间的大小关系,然后将集合按升序排列。 当把一个对象加入TreeSet集合中时,会调用compareTo方法与集合中其它对象比较大小,然后根据红黑树结构定位存储位置。如比较相等,则无法添加。 自然排序中判断两个对象是否相等的唯一标准:两个对象通过compareTo方法返回是否为0。 因此,当重写两个对象的equals方法时,应当重写其compareTo方法,确保有一致的结果。与HashSet类似,当保存引用类型对象的时候,尽量不要修改其实例变量,否则可能会导致操作失效。 TreeSet的定制排序 创建TreeSet集合的对象时,提供一个Comparator对象与该TreeSet集合关联,由该Comparator对象负责元素集合的排序逻辑。 定制排序中判断两个对象是否相等的标准:Comparator对象中比较两个元素是否返回0 class M{ int age; public M(int age) { super(); this.age = age; } @Override public String toString() { return "M [age=" + age + "]"; } } public class TestTreeSetComparator { public static void main(String[] args) { TreeSet<M> ts = new TreeSet<>((o1, o2) -> { M m1 = (M)o1; M m2 = (M)o2; //自定义排序:M对象的age属性越大,对比结果反而越小 return m1.age > m2.age ? -1 : m1.age < m2.age ? 1 : 0; }); ts.add(new M(5)); ts.add(new M(-3)); ts.add(new M(9)); System.out.println(ts); } } EnumSet EnumSet专为枚举类设计,其元素必须是指定枚举类的枚举值,并且不能为null。 EnumSet为有序集合,顺序由枚举值在枚举类中的定义顺序决定。其常用方法如下: 1 enum Season{ 2 SPRING, SUMMER, FALL, WINTER 3 } 4 5 public class TestEnumSet { 6 7 public static void main(String[] args) { 8 //创建一个指定枚举类Season的枚举集合 9 EnumSet<Season> es1 = EnumSet.allOf(Season.class); 10 System.out.println(es1); 11 //创建一个指定枚举类Season的空集合 12 EnumSet<Season> es2 = EnumSet.noneOf(Season.class); 13 System.out.println(es2); 14 //手动添加枚举值 15 es2.add(Season.WINTER); 16 es2.add(Season.SUMMER); 17 System.out.println(es2); 18 //创建一个指定枚举值的枚举集合 19 EnumSet<Season> es3 = EnumSet.of(Season.SPRING, Season.FALL); 20 System.out.println(es3); 21 //创建一个指定枚举值及顺序的枚举集合 22 EnumSet<Season> es4 = EnumSet.range(Season.SUMMER, Season.WINTER); 23 System.out.println(es4); 24 //创建一个es4集合的其余集合es5, 其中es4+es5=es1 25 EnumSet<Season> es5 = EnumSet.complementOf(es4); 26 System.out.println(es5); 27 } 28 } 关于HashSet和TreeSet的选择 作为两个Set接口的典型实现,一般的选择考虑点是: 1. 优先选择HashSet,快速满足日常添加、查询的操作。 2. 有迭代需求时,可以考虑LinkedHashSet 3. 当需要一个排序的Set的时候,再考虑TreeSet 4. 操作枚举类时,优先考虑EnumSet 当需要在多线程环境下使用集合,应该使用Collections工具类在创建集合时就封装为线程安全。 SortedSet set = Collections.synchronizedSortedSet(new TreeSet());

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

Java容器深入浅出之PriorityQueue、ArrayDeque和LinkedList

Queue用于模拟一种FIFO(first in first out)的队列结构。一般来说,典型的队列结构不允许随机访问队列中的元素。队列包含的方法为: 1. 入队 void add(Object o): 指定元素加入队列尾部 boolean offer(Object o):同上,在有限容量队列中,此方法更好 2. 出队 Object poll():获取头部元素,并从队列中删除;如果队列为空,则返回null Object remove():获取头部元素,并从队列中删除; 3. 出队不删除 Object peek():获取头部元素,不删除;如果队列为空,则返回null Object element():获取头部元素,不删除; PriorityQueue PriorityQueue是Queue接口的实现类,但是它并不是一个FIFO的队列实现,具体表现在: 1. 保存顺序与FIFO无关,而是按照元素大小进行重排序;因此poll出来的是按照有小到大来取。 2. 不允许保存null,排序规则有自然排序和定制排序两种,规则与TreeSet一致。 Deque接口与ArrayDeque实现类 Deque实现的是一个双端队列,因此它具有“FIFO队列”及“栈”的方法特性,其中ArrayDeque是其典型的实现类。 1. ArrayDeque的栈实现 1 public class ArrayDequeStack { 2 3 public static void main(String[] args) { 4 ArrayDeque<String> stack = new ArrayDeque<>(); 5 //压栈,此时AAA在最下,CCC在最外 6 stack.push("AAA"); 7 stack.push("BBB"); 8 stack.push("CCC"); 9 System.out.println(stack); 10 //获取最后添加的元素,但不删除 11 System.out.println(stack.peek()); 12 System.out.println(stack); 13 //弹出最后添加的元素 14 System.out.println(stack.pop()); 15 System.out.println(stack); 16 } 17 18 } 2. ArrayDeque的FIFO队列实现 1 public class ArrayDequeQueue { 2 3 public static void main(String[] args) { 4 ArrayDeque<String> queue = new ArrayDeque<>(); 5 //入队 6 queue.offer("AAA"); 7 queue.offer("BBB"); 8 queue.offer("CCC"); 9 System.out.println(queue); 10 //获取但不出队 11 System.out.println(queue.peek()); 12 System.out.println(queue); 13 //出队 14 System.out.println(queue.poll()); 15 System.out.println(queue); 16 } 17 18 } LinkedList实现类 LinkedList比较特殊,它既实现了List接口,同时也实现了Deque接口。因此它具备了List、队列和栈的特性,在应用开发中有广泛的用途。 1 public class TestLinkedList { 2 3 public static void main(String[] args) { 4 LinkedList<String> ll = new LinkedList<>(); 5 //入队 6 ll.offer("AAA"); 7 //压栈 8 ll.push("BBB"); 9 //双端的另一端入队 10 ll.addFirst("NNN"); 11 ll.forEach(str -> System.out.println("遍历中:" + str)); 12 //获取队头 13 System.out.println(ll.peekFirst()); 14 //获取队尾 15 System.out.println(ll.peekLast()); 16 //弹栈 17 System.out.println(ll.pop()); 18 System.out.println(ll); 19 //双端的后端出列 20 System.out.println(ll.pollLast()); 21 System.out.println(ll); 22 } 23 }

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

容器化RDS|计算存储分离架构下的 IO 优化

计算存储分离架构 架构示意图如下: 存储层由分布式文件系统组成,以Provisoner的方式集成到Kubernetes. 在我们看来, 计算存储分离的最大优势在于: 将有状态的数据下沉到存储层,这使得RDS在调度时,无需感知计算节点的存储介质,只需调度到满足计算资源要求的Node,数据库实例启动时,只需在分布式文件系统挂载mapping的volume即可,可以显著的提高数据库实例的部署密度和计算资源利用率。 其他的好处还有很多,譬如架构更清晰,扩展更方便,问题定位更简单等,这里不赘述。 计算存储分离架构的缺点 俗话说的好: 上帝为你关上一扇窗的同时,再关上一扇门。 如下图所示 相较本地存储, 网络开销会成为IO开销的一部分, 我们认为会带来两个很明显的问题: 数据库是Latency Sensitive型应用, 网络延时会极大影响数据库能力(QPS,TPS); 在高密度部署的场景, 网络带宽会成为瓶颈, 可能导致计算 & 存储资源利用不充分。 其实还有一个极其重要的问题,由于kubernetes本身没有提供Voting服务和类似Oracle Rac的Fence机制,在计算存储分离架构下,当集群发生脑裂,并触发Node Controller和Kubelet的驱逐机制时,可能会出现多个数据库实例同时访问一份数据文件导致DataCorruption的情况,数据的损失对用户而言是不可估量也不可忍受的。 我们在kubernetes 1.7.8下使用Oracle,MySQL都可以100%复现这个场景,通过在Kubernetes上添加Fence机制,我们已解决该问题。如果大家有兴趣,会再做专门的分享。 下面,就需要结合MySQL的特性来进行有针对性的优化。 以下测试方案的设计,测试数据的梳理来自于沃趣科技MySQL专家@董大爷和 @波多野老师。 DoubleWrite 在MySQL中我们首先想到了DoubleWrite.首先看下官方解释,它是干什么的 : The InnoDB doublewrite buffer was implemented to recover from half-written pages. This can happen when there's a power failure while InnoDB is writing a page to disk. On reading that page, InnoDB can discover the corruption from the mismatch of the page checksum. However, in order to recover, an intact copy of the page would be needed. The double write buffer provides such a copy. Whenever InnoDB flushes a page to disk, it is first written to the double write buffer. Only when the buffer is safely flushed to disk will InnoDB write the page to the final destination. When recovering, InnoDB scans the double write buffer and for each valid page in the buffer checks if the page in the data file is valid too. Although data is written twice, the doublewrite buffer does not require twice as much I/O, as data is written to the buffer in a large sequential chunk with a single fsync() call. There is extra time consumed however, and the effect becomes visible with fast storage and a heavy write load. 简单说DoubleWrite的实现是防止数据页写入时发生故障导致页损坏(partial write),所以每次写数据文件时都要将一份数据写到共享表空间中,当启动时发现数据页Checkum校验不正确时会使用共享表空间中副本进行恢复,从DoubleWrite实现来看这部分会产生一定量的IO.所以: 最好的优化就是减少IO, 在底层存储介质或文件系统支持Atomic Write的前提下,可以关闭MySQL的DoubleWrite以减少IO。 单机架构 : 关闭DoubleWrite MariaDB已支持该功能(底层存储介质需支持Atomic Write),并在单机环境做了相关测试。数据如下: 结论:单机环境下,启用Atomic Write(关闭DoubleWrite)能立即带来30%左右的写性能改善。DoubleWrite 原文地址 :http://blog.mariadb.org/mariadb-introduces-atomic-writes/ 计算存储分离架构 : 关闭DoubleWrite 所以, 重点是我们需要测试一下在计算存储分离架构下(分布式存储必须支持Atomic Write), 关闭DoubleWriteBuffer的收益。 测试场景 采用Sysbench 模拟 OLTP 敷在模型 (跟 MariaDB相同) 数据库版本选择了更流行的 MySQL 5.7.19 (测试时的最新版本) 由本地存储改为分布式文件系统 测试数据量, 数据文件大写 1、10GB 2、100GB 测试结果 : 10GB数据量 Sysbench 指标: 分布式文件系统指标: 在计算存储分离架构下, 启用Atomic Write(关闭 DoubleWrite), 10GB数据量, 因为大部分数据已经缓存到数据库 buffer cache 中,所以在 IO 不是瓶颈的情况下: Sysbench指标, 提升不明显 tps ↑0.2656%,qps ↑0.2797%,rst ↑14.9651% 分布式文件系统指标 Throughput 下降53%, 显著优化了网络带宽 测试结果 : 100GB数据量 Sysbench 指标: 分布式文件系统指标: 在计算存储分离架构下, 启用Atomic Write(关闭DoubleWrite), 100GB数据量, 因为大部分数据无法缓存到数据库 buffer cache 中,所以在 IO 是瓶颈的情况下: Sysbench指标, 提升明显: TPS ↑28.0892%,QPS ↑28.0893%,RST ↓169.2033% 分布式文件系统指标 IOPS 提升22.3% Latency 下降 39% 在IOPS 提升22.3%的情况下, Throughput 仅多消耗 3.6% 原文发布时间为:2018-01-11 本文作者:熊中哲 本文来自云栖社区合作伙伴“老叶茶馆”,了解相关信息可以关注“老叶茶馆”微信公众号

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

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

用户登录
用户注册