前言:在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 容器在实际项目开发中应用的更多相关文章

  1. Java IO在实际项目开发中应用

    IO是java绕不过去的槛,在开发中io无处不在, 正如同 世界上本没有路,java io写多了,也就知道了大体是什么意思,在读完thinking in java 感觉就更清晰了,结合具体的业务场景, ...

  2. Java 容器在实际项目中的应用

    前言:在java开发中我们离不开集合数组等,在java中有个专有名词:"容器" ,下面会结合Thinking in Java的知识和实际开发中业务场景讲述一下容器在Web项目中的用 ...

  3. 《Maven在Java项目开发中的应用》论文笔记(十七)

    标题:Maven在Java项目开发中的应用 一.基本信息 时间:2019 来源:山西农业大学 关键词:Maven:Java Web:仓库:开发人员:极限编程; 二.研究内容 1.Maven 基本原理概 ...

  4. Java项目开发中实现分页的三种方式一篇包会

    前言   Java项目开发中经常要用到分页功能,现在普遍使用SpringBoot进行快速开发,而数据层主要整合SpringDataJPA和MyBatis两种框架,这两种框架都提供了相应的分页工具,使用 ...

  5. 团队项目开发中,常见的版本控制有svn,git

    团队项目开发中,常见的版本控制有svn,git

  6. 项目开发中的一些注意事项以及技巧总结 基于Repository模式设计项目架构—你可以参考的项目架构设计 Asp.Net Core中使用RSA加密 EF Core中的多对多映射如何实现? asp.net core下的如何给网站做安全设置 获取服务端https证书 Js异常捕获

    项目开发中的一些注意事项以及技巧总结   1.jquery采用ajax向后端请求时,MVC框架并不能返回View的数据,也就是一般我们使用View().PartialView()等,只能返回json以 ...

  7. Angular 项目开发中父子组件传参

    在项目开发中经常会遇到 组件之间传参的问题.今天总结下在使用angular的项目中父子组件传参的问题: 1.父组件向子组件传参: 然后在父组件中 然后在父组件的html中 然后就可以在子组件中使用了 ...

  8. 炼金术(1): 识别项目开发中的ProtoType、Demo、MVP

    软件开发是很分裂的,只有不断使用原则和规律,才能带来质量. 只要不是玩具性质的项目,项目应该可以大概划分为0-1,1-10,10-100,100-1000四个种重要阶段.其中,0-1是原型验证性的:1 ...

  9. 项目开发中的git简单使用

    原文地址: https://www.zhuyilong.fun/tech/the-blog-git.html 示例远程仓库地址: https://github.com/zhu-longge/gitWo ...

随机推荐

  1. 根据wsdl文件用soapUi快速创建webService服务(有图有真相)

    最近公司业务上使用webservice 频繁.由于之前都是自己搭建webservice 自己定义提供给别人服务,现在则相反需求都是根据人家提供的wsdl 文件来生成 我们平台需要提供的接口.刚开始不知 ...

  2. java 操作 redis

    1.Java 使用 Redis 只需要下载一个jar包即可 地址:http://maven.outofmemory.cn/redis.clients/jedis/2.5.2/ 工程

  3. LCA——求解最近公共祖先

    LCA 在有根树中,两个节点 u 和 v 的公共祖先中距离最近的那个被称为最近公共祖先(LCA,Lowest Common Ancestor). 有多种算法解决 LCA 或相关的问题. 基于二分搜索的 ...

  4. C. Friends

    C. Friends 题意 对于任一点,求到此点距离不超过6的节点数. 分析 第一次dfs,形成一个以 1 为根的有向树,设 down[i][j] 为以i为根节点,距离 i 点距离不超过 j 的节点数 ...

  5. awk内引用shell变量【自己手动加精】

    题目 [root@localhost ~]# cat 1.txt iii sss ddd 执行命令 [root@localhost ~]# A=0 [root@localhost ~]# awk '{ ...

  6. PyQt5环境搭建及cx_freeze打包exe

    Python的图形库也有好几个,Qt文档和使用面还是要广一些. 打包成可执行文件的也有好几个,PyInstaller用的比较多,但是PyInstaller目前还不支持Python3.6(开发版支持3. ...

  7. C# 中文在URL中的编码

      UTF-8中,一个汉字对应三个字节,GB2312中一个汉字占用两个字节. 不论何种编码,字母数字都不编码,特殊符号编码后占用一个字节. //按照UTF-8进行编码 string tempSearc ...

  8. Myeclipse8.6注册机代码,不用到处找注册机了

    import java.io.*; public class MyEclipseGen { private static final String LL = "Decompiling thi ...

  9. iOS自定义弹出视图

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #78492a } p.p2 { margin: 0.0px 0. ...

  10. c++,函数名不要和某个类名相同 (syntax error : missing ';' before identifier....)

    直接上代码: // outside.h class Outside { private: class Inner { public: Inner(Outside& out) : out_(ou ...