Java 容器在实际项目开发中应用
前言:在java开发中我们离不开集合数组等,在java中有个专有名词:“容器” ,下面会结合Thinking in Java的知识和实际开发中业务场景讲述一下容器在Web项目中的用法。可结合图片代码了解Java中的容器

备注 :这个地方 ,参考于朝向远方的博客Java容器详解,既然前人总结的这么好,我就直接拿来用,在这里更注重在实际开发中的例子,感谢那些总结的前辈们,辛苦了。
简单的数组例子
Thinking in Java 中并没有把数组归为Java的容器,实际上数组的确不是Java独有的c++ ,c都有数组。但是,在web开发时我还是把数组归类到容器中,因为他们说白了都是在做相同的事情
另外还有一个细节点就是:我翻遍了我开发过的项目,但是很惊讶的发现,这么多项目里直接用数组存储对象极为少见。想想也是,java是面向对象的,而数组对java总归是有点偏底层。
珍惜这来之不易的demo吧:
public Map<String, String> getDimValue() {
if (this.dimValue != null)
return dimValue;
this.dimValue = new HashMap<String, String>();
if (this.dim != null && this.dim.length() != 0) {
String[] strDims = this.dim.split(",");//可以用截取的方式,得到String[]
for (String s : strDims) {
String[] dims = s.split("\\:");
this.dimValue.put(dims[0], dims[1]);//数组访问通过下标,但是注意 最多到array[array.length-1],越界直接抛出异常,和c++不一样
}
}
return this.dimValue;
}
数组(array)是最常见的数据结构。数组是相同类型元素的有序集合,并有固定的大小(可容纳固定数目的元素)。数组可以根据下标(index)来随机存取(random access)元素。在内存中,数组通常是一段连续的存储单元。
Java支持数组这一数据结构。我们需要说明每个数组的类型和大小,java利用byte[] 可以表示blob字段,存放图片,xml,json等。String[]则可以用来存一些字符串,id, code等。
//web项目中倒是常用 byte[]来存放blob字段等
@Type(type = "org.springframework.orm.hibernate3.support.BlobByteArrayType") private byte[] globals;
在说明类型时,在类型说明(String)后面增加一个[],来说明是一个数组。使用new创建容器时,需要说明数组的大小;或者是 直接 int a = {1,2,3} 这样直接用{}同时初始化。
数组可以通过遍历的形式转为其他容器类型,但是其他类型可以通过 toArray()快速转为数组(下文中会说到Arrays这个工具类可以把数组转为List)
第一个分支:Collection
在开发中,Collection最常用的就是两个类: Set和List。因为同属于一个Collection下,相互转化方便,调用的方法也类似。(collection Api)
Java中常用方法接口:
* boolean add(Object obj): 添加对象,集合发生变化则返回true * Iterator iterator():返回Iterator接口的对象 * int size() * boolean isEmpty() * boolean contains(Object obj) * void clear() * <T> T[] toArray(T[] a)
上述接口参照于:wishyouhappy的博客:java容器总结。
1:List集合
具体可以查看list中文文档,文档中清楚的描述到List<E>是一个实现了 Collection的接口,而我们可以直接用List 声明对象(接口可以直接声明一个对象)。容器的引用为List类型,但容器的实现为ArrayList类。这里是将接口与实现类分离。事实上,同一种抽象数据结构(ADT)都可以有多种实施方法(比如栈可以实施为数组和链表)。这样的分离允许我们更自由的选择ADT的实施方式(参考于Java容器详解)
java中较为常用的 ArrayList,LinkedList, 集合中的元素可以相等,是有顺序的
public class Test {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
//添加单个元素
for(String s1:"hehe wo shi lao da".split(" ")){
list.add(s1);
}
//添加多个元素
list.addAll(Arrays.asList("nan dao ni bu xin?".split(" ")));//Arrays是一个工具类,可以帮助我们少些遍历代码
System.out.println(list.toString());//list重写了toString方法,输出list中每一个元素
//修改位置为i的元素
for(int i = 0; i<list.size();i++){
list.set(i, "u");
}
System.out.println(list.toString());
list.removeAll(Arrays.asList(new String[]{"u"}));//这个地方为了测试 我初始化了一个字符数组 new String[]{"u"}
}
}
上边的代码只是为了说明 list的主要用途,实际上开发中可能用不到这么多,比较常用的也就
- add()方法加入新的元素
- get()方法可以获取容器中的元素,传递一个整数下标作为参数
- remove()方法可以删除容器中的元素,传递一个整数下标作为参数。(有另一个remove(),传递元素自身作为参数)
- size()方法用来返回容器中元素的总数。
- toString() 多用于调试代码是,查看list中的内容
- addAll() 添加一个相同类型的list
List中 还有一个实习类 LinkedList 比较常用,它可以用来做队列 的实现,也可以变相完成栈的工作。
主要方法有:
- get(int index):返回此列表中指定位置处的元素。
- getFirst():返回此列表的第一个元素。
- getLast():返回此列表的最后一个元素。
- indexOf(Object o):返回此列表中首次出现的指定元素的索引,如果此列表中不包含该元素,则返回 -1。
- lastIndexOf(Object o):返回此列表中最后出现的指定元素的索引,如果此列表中不包含该元素,则返回 -1。
- remove():获取并移除此列表的头(第一个元素)
- removeFirst():移除并返回此列表的第一个元素
- removeLast():移除并返回此列表的最后一个元素
ListedList采用的是链式存储。链式存储就会定一个节点Node。包括三部分前驱节点、后继节点以及data值。所以存储存储的时候他的物理地址不一定是连续的
具体内容可参照java提高篇(二二)---LinkedList 下面列出了linkedList的部分源码(不建议一开始就看)
//其中size表示的LinkedList的大小,header表示链表的表头,Entry为节点对象。
private transient Entry<E> header = new Entry<E>(null, null, null);
private transient int size = 0;
...
//内部类,定义了存储的元素。该元素的前一个元素、后一个元素,这是典型的双向链表定义方式
private static class Entry<E> {
E element; //元素节点
Entry<E> next; //下一个元素
Entry<E> previous; //上一个元素
Entry(E element, Entry<E> next, Entry<E> previous) {
this.element = element;
this.next = next;
this.previous = previous;
}
}
.....
/ **
* 添加指定 collection 中的所有元素到此列表的结尾,顺序是指定 collection 的迭代器返回这些元素的顺序。
*/
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
/**
* 将指定 collection 中的所有元素从指定位置开始插入此列表。其中index表示在其中插入指定collection中第一个元素的索引
*/
public boolean addAll(int index, Collection<? extends E> c) {
//若插入的位置小于0或者大于链表长度,则抛出IndexOutOfBoundsException异常
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
Object[] a = c.toArray();
int numNew = a.length; //插入元素的个数
//若插入的元素为空,则返回false
if (numNew == 0)
return false;
//modCount:在AbstractList中定义的,表示从结构上修改列表的次数
modCount++;
//获取插入位置的节点,若插入的位置在size处,则是头节点,否则获取index位置处的节点
Entry<E> successor = (index == size ? header : entry(index));
//插入位置的前一个节点,在插入过程中需要修改该节点的next引用:指向插入的节点元素
Entry<E> predecessor = successor.previous;
//执行插入动作
for (int i = 0; i < numNew; i++) {
//构造一个节点e,这里已经执行了插入节点动作同时修改了相邻节点的指向引用
//
Entry<E> e = new Entry<E>((E) a[i], successor, predecessor);
//将插入位置前一个节点的下一个元素引用指向当前元素
predecessor.next = e;
//修改插入位置的前一个节点,这样做的目的是将插入位置右移一位,保证后续的元素是插在该元素的后面,确保这些元素的顺序
predecessor = e;
}
successor.previous = predecessor;
//修改容量大小
size += numNew;
return true;
}
...
private Entry<E> addBefore(E e, Entry<E> entry) {
//利用Entry构造函数构建一个新节点 newEntry,
Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
//修改newEntry的前后节点的引用,确保其链表的引用关系是正确的
newEntry.previous.next = newEntry;
newEntry.next.previous = newEntry;
//容量+1
size++;
//修改次数+1
modCount++;
return newEntry;
}
2:Set集合
集合(set)也是元素的集合。集合中不允许有等值的元素,集合的元素没有顺序:
我们用Set多数时候是利用它的特性,没有重复的元素,例如:
2.1 HashSet:HashSet查询速度比较快,但是存储的元素是随机的并没有排序
public class Test
{
public static void main(String[] args)
{
Set<Integer> s1 = new HashSet<Integer>();
s1.add(4);
s1.add(5);
s1.add(4);
s1.remove(5);
System.out.println(s1);
System.out.println(s1.size());
}
}
我们可以用它去过滤重复数据,Set 可以轻松的转为List,因为构造方法传入参数是Collection<? extends E> c
Set<String> set = new HashSet<String>();
set.add("h");
set.add("h");
List<String> fromSets = new ArrayList<String>(set);
System.out.println(fromSets.toString());
Set<String> s1 = new HashSet<String>(fromSets);
System.out.println(s1.toString());
这个地方,非常有意思的是,HashSet中竟然持有的是HashMap,利用HashMap存取数据
public HashSet(Collection<? extends E> c) {
map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
HashSet只有add方法,没有get方法(这和list稍微不同)。但是HashSet 实现了Iterator<E> iterator()。可以通过Iterator遍历,具体可以查看Set中文文档
2.2:TreeSet
TreeSet是将元素存储红-黑树结构中,所以存储的结果是有顺序的
public static void main(String[] args){
Random random=new Random(47);
Set<Integer> intset=new TreeSet<Integer>();
for (int i=0;i<10000;i++){
intset.add(random.nextInt(30));
}
System.out.print(intset);
}
3:collection中的Iterator
Iterator的官方文档,一般Set想要取元素只能通过迭代器,而list也可以用迭代器(一般都是用get)
public class Test
{
public static void main(String[] args)
{
List<Integer> l1 = new ArrayList<Integer>();
l1.add(4);
l1.add(5);
l1.add(2);
Iterator i = l1.iterator();
while(i.hasNext()) {
System.out.println(i.next());
}
}
}
Collection可以用foreach,因为其实现了Iterator接口
public class IteratorClass {
public Iterator<String> iterator(){
return new Itr();
}
private class Itr implements Iterator<String>{
protected String[] words=("Hello Java").split(" ");
private int index=0;
public boolean hasNext() {
return index<words.length;
}
public String next() {
return words[index++];
}
public void remove() {
}
}
}
foreach循环最终也会转化为Iterator遍历 (Iterator it=iterator;iterators.hasNext();)
Iterator iterators=new IteratorClass().iterator();
for (Iterator it=iterator;iterators.hasNext();) {
System.out.println(iterators.next());
}
while (iterators.hasNext()){
System.out.println(iterators.next());
}
下面说一下Java中极容易出错的点:
for 循环查找集合中某个元素并删除:极容易出现java.util.ConcurrentModificationException
List<String> list = new ArrayList<String>();
list.addAll(Arrays.asList("nan dao ni bu xin?".split(" ")));
for(String st:list){
System.out.println(st);
if(st.equals("ni")){
list.remove(st);
}
}
解决方式是将数组转化为Iterator,然后利用it.remove();删除数组中的元素
public static void main(String[] args) {
List<String> list=new ArrayList<String>();
list.add("a");
list.add("bb");
list.add("a22");
Iterator<String> it=list.iterator();
//去除数组中"a"的元素
while(it.hasNext()){
String st=it.next();
if(st.equals("a")){
it.remove();
}
}
}
第二个分支:Map
在web项目中,Map是非常常用的,当然在很多时候,Map会被一些包装类给替代掉(这实际上是敏捷开发中提到用vo替换map).但是Map还是无法阻挡的容器一哥。
Java中常用的方法接口
* Object get(Object key) * Object put(Object key, Object value) * Set keySet() : returns the keys set Set<K> keySet() * Set entrySet(): returns mappings set Set<Map.Entry<K,V>> entrySet() * containsKey() * containsValue()
Map是键值对的集合。Map中的每个元素是一个键值对,即一个键(key)和它对应的对象值(value)。对于Map容器,我们可以通过键来找到对应的对象。
哈希表是Map常见的一种实现方式,也是实际开发中用的最广泛的 (HashMap),想要具体了解HashMap的原理,可以参考 hashmap实现原理浅析
public class Test
{
public static void main(String[] args)
{
Map<String, Integer> m1 = new HashMap<String, Integer>();
m1.put("Vamei", 12);
m1.put("Jerry", 5);
m1.put("Tom", 18);
System.out.println(m1.get("Vamei"));
}
}
在Map中,我们使用put()方法来添加元素,用get()方法来获得元素。
Map还提供了下面的方法,来返回一个Collection:
- keySet() 将所有的键转换为Set
- values() 将所有的值转换为List
- containsKey验证主要是否存在、containsValue验证值是否存在
- entrySet获取键值对。
总结:
java中有一些工具类来帮助我们处理容器相关的内容。比如Arrays,java中的一些类都有用到这些工具类
ArrayList源码中的clone方法
/**
* Returns a shallow copy of this <tt>ArrayList</tt> instance. (The
* elements themselves are not copied.)
*
* @return a clone of this <tt>ArrayList</tt> instance
*/
public Object clone() {
try {
ArrayList<E> v = (ArrayList<E>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError();
}
}
ArrayList源码中的 toArray()方法
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
如果你对Arrays这个工具类有兴趣,可以看一下源码,它最终调用到了本地方法(折叠起来,是不希望给读者带来困惑)
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
Arrays的一些其他方法:
- sort(): 对传入的集合排序 (具体算法可以参考Java Arrays.sort源代码解析)
- aslist(): 把数组转为List
- binarySearch():二分查找数组
- deepToString():把二维数组转为String
- fill():快速填充数组
再比如Collections:
可以参考thinking in java之Collections工具类的使用
package countainers;
import java.util.*;
import static net.mindview.util.Print.*;
public class Utilities {
static List<String> list = Arrays.asList(
"one Two three Four five six one".split(" "));
public static void main(String[] args) {
print(list);
print("'list' disjoint (Four)?: " +
Collections.disjoint(list,
Collections.singletonList("Four")));
print("max: " + Collections.max(list));
print("min: " + Collections.min(list));
print("max w/ comparator: " + Collections.max(list,
String.CASE_INSENSITIVE_ORDER));
print("min w/ comparator: " + Collections.min(list,
String.CASE_INSENSITIVE_ORDER));
List<String> sublist =
Arrays.asList("Four five six".split(" "));
print("indexOfSubList: " +
Collections.indexOfSubList(list, sublist));
print("lastIndexOfSubList: " +
Collections.lastIndexOfSubList(list, sublist));
Collections.replaceAll(list, "one", "Yo");
print("replaceAll: " + list);
Collections.reverse(list);
print("reverse: " + list);
Collections.rotate(list, 3);
print("rotate: " + list);
List<String> source =
Arrays.asList("in the matrix".split(" "));
Collections.copy(list, source);
print("copy: " + list);
Collections.swap(list, 0, list.size() - 1);
print("swap: " + list);
Collections.shuffle(list, new Random(47));
print("shuffled: " + list);
Collections.fill(list, "pop");
print("fill: " + list);
print("frequency of 'pop': " +
Collections.frequency(list, "pop"));
List<String> dups = Collections.nCopies(3, "snap");
print("dups: " + dups);
print("'list' disjoint 'dups'?: " +
Collections.disjoint(list, dups));
// Getting an old-style Enumeration:
Enumeration<String> e = Collections.enumeration(dups);
Vector<String> v = new Vector<String>();
while(e.hasMoreElements())
v.addElement(e.nextElement());
// Converting an old-style Vector
// to a List via an Enumeration:
ArrayList<String> arrayList =
Collections.list(v.elements());
print("arrayList: " + arrayList);
}
}
max():取集合的最大元素
subList():截取list
addAll():添加集合
有兴趣的可以去看一下源码,我觉得非常有帮助
Java 容器在实际项目开发中应用的更多相关文章
- Java IO在实际项目开发中应用
IO是java绕不过去的槛,在开发中io无处不在, 正如同 世界上本没有路,java io写多了,也就知道了大体是什么意思,在读完thinking in java 感觉就更清晰了,结合具体的业务场景, ...
- Java 容器在实际项目中的应用
前言:在java开发中我们离不开集合数组等,在java中有个专有名词:"容器" ,下面会结合Thinking in Java的知识和实际开发中业务场景讲述一下容器在Web项目中的用 ...
- 《Maven在Java项目开发中的应用》论文笔记(十七)
标题:Maven在Java项目开发中的应用 一.基本信息 时间:2019 来源:山西农业大学 关键词:Maven:Java Web:仓库:开发人员:极限编程; 二.研究内容 1.Maven 基本原理概 ...
- Java项目开发中实现分页的三种方式一篇包会
前言 Java项目开发中经常要用到分页功能,现在普遍使用SpringBoot进行快速开发,而数据层主要整合SpringDataJPA和MyBatis两种框架,这两种框架都提供了相应的分页工具,使用 ...
- 团队项目开发中,常见的版本控制有svn,git
团队项目开发中,常见的版本控制有svn,git
- 项目开发中的一些注意事项以及技巧总结 基于Repository模式设计项目架构—你可以参考的项目架构设计 Asp.Net Core中使用RSA加密 EF Core中的多对多映射如何实现? asp.net core下的如何给网站做安全设置 获取服务端https证书 Js异常捕获
项目开发中的一些注意事项以及技巧总结 1.jquery采用ajax向后端请求时,MVC框架并不能返回View的数据,也就是一般我们使用View().PartialView()等,只能返回json以 ...
- Angular 项目开发中父子组件传参
在项目开发中经常会遇到 组件之间传参的问题.今天总结下在使用angular的项目中父子组件传参的问题: 1.父组件向子组件传参: 然后在父组件中 然后在父组件的html中 然后就可以在子组件中使用了 ...
- 炼金术(1): 识别项目开发中的ProtoType、Demo、MVP
软件开发是很分裂的,只有不断使用原则和规律,才能带来质量. 只要不是玩具性质的项目,项目应该可以大概划分为0-1,1-10,10-100,100-1000四个种重要阶段.其中,0-1是原型验证性的:1 ...
- 项目开发中的git简单使用
原文地址: https://www.zhuyilong.fun/tech/the-blog-git.html 示例远程仓库地址: https://github.com/zhu-longge/gitWo ...
随机推荐
- Spring-bean作用域scope详解
Spring Framework支持五种作用域(其中有三种只能用在基于web的Spring ApplicationContext). singleton 在每个Spring IoC容器中一个bean定 ...
- java中文件操作《一》
在日常的开发中我们经常会碰到对文件的操作,在java中对文件的操作都在java.io包下,这个包下的类有File.inputStream.outputStream.FileInputStream.Fi ...
- java mvc框架系列总结ssh,ssm,servlet
2016年10月3日 10:36:40 一直以来都很想写属于自己的博客,一来可以分享自己的学习经验,二来可以及时总结,毕竟博客是写给所有人看的,需要更加仔细的注意每个细节,而不是仅仅让自己看懂. 学了 ...
- 前端向后台的华丽转身 — PHP基础篇
这一次,本K带大家来看一下关于PHP中数组.字符串的一些注意事项和函数(方法). 一.PHP中的数组 (一)PHP中的数组简介 数组类型是PHP两种复合数据类型之一.根据下标的不同,可以将PHP中的数 ...
- Postgres Linux 维护 随笔1(启动篇)
关于postgres 起停操作随笔 Linux 环境下,对Postgres 起停常用代码 Postgres 启动 : pg_ctl start Postgres 停止 : pg_ctl stop Po ...
- Elasticsearch 与 Kafka 整合剖析
1.概述 目前,随着大数据的浪潮,Kafka 被越来越多的企业所认可,如今的Kafka已发展到0.10.x,其优秀的特性也带给我们解决实际业务的方案.对于数据分流来说,既可以分流到离线存储平台(HDF ...
- mysql之 MySQL 主从基于 GTID 复制原理概述
一. 什么是GTID ( Global transaction identifiers ):MySQL-5.6.2开始支持,MySQL-5.6.10后完善,GTID 分成两部分,一部分是服务的UUid ...
- git 常用的命令符
1. linux操作命令: touch Filename:创建一个文件: mkdir 文件夹名字:创建一个文件夹: cd pathname:切换磁盘: cd ..:返回上一级: rm Filename ...
- Hibernate 案例
搭建一个Hibernate环境,开发步骤: 1. 下载源码 版本:hibernate-distribution-3.6.0.Final 2. 引入jar文件 hibernate3.j ...
- c# networkcomms 3.0实现模拟登陆总结
最近项目需要做一个客户查询状态系统,当前上位机缺少服务功能,于是找到了networkcomms 开源框架,作为项目使用. 最新版networkcomms 下载地址:https://github.com ...