zoukankan      html  css  js  c++  java
  • 疯狂Java学习笔记(017)·集合类

    一、ArrayList 对自定义类对象去重

      1.思路:创建新集合,从原集合里拿出元素放入新集合,放入的同时做判断使用的是contains方法。

      通过查看contains方法的源码,实际上调用的是要添加元素的equals方法。

      如果要添加元素对已经存在的元素调用equals方法,返回true表面已经存在;返回false表面不存在。

      默认equals是继承自Object类的,只是简单的比较地址,自定义类要重写equals方法,告知虚拟机到底什么情况下才是“相等”的。

    二、并发修改异常(ConcurrentModificationException)

      在使用迭代器遍历ArrayList的时候,使用集合本身的方法修改集合,将导致并发修改异常:

      两种方法:

      1.使用迭代器遍历,使用迭代器删除元素。

      2.使用集合本身遍历,使用集合本身方法删除元素。

      如果添加元素:

      查看Iterator接口的方法,没有添加元素的方法,但是它的子接口ListIterator有。

      1.ListIterator迭代,迭代器添加元素。

      2.集合本身迭代,使用集合本身方法添加元素。

    三、Vector类

      Vector类是1.0就已经有了,在1.2被整合到集合框架中,其中的大部分方法都和ArrayList相同,但是它是线程安全的,所以效率低。

      特有方法:

    •   addElement( );
    •   removeElement( );
    •   elements( );

    四、LinkedList类

    •   LinkedList类底层使用的是链表结构保存数据。
    •   绝大多数方法和ArrayList相同,只不过多了一些对首尾元素操作的方法。
    •   包括:
    •     addFirst( )
    •     addLast( )
    •     remove( )

      LinkedList底层使用得是链表,但是自己维护了一个索引,所以,提供了get(int index)的方法来通过索引获取元素,但此方法的效率很低,一般不使用。

     五、关于List接口的实现类的选择

    •   需要线程安全的话是选择Vector,但是也可以通过其他方式让其他两种类也达到线程安全,所以,一般也不会使用Vector。
    •   不需要线程安全的话,考虑是增删多还是查询多:增删多,使用LinkedList;查询多,使用ArrayList.
    •   ArrayList比较通用。

    六、泛型

      需要了解的方面:

    • JDK1.5的新特性
      1. 在JDK1.5之前,把对象放到集合中,集合不会记住元素的类型,取出时,全都变成Object类型。
      2. 集合接口,集合类中出现的<>就是泛型,即参数化类型<>中的字母代表的是类型。
      3. 提高了程序的安全性,不符合类型的元素不能添加。
      4. 将运行期遇到的问题转移到了编译期。
      5. 记住了元素的类型,取出元素时省区了类型强转的麻烦。
    • 泛型出现的原因

        业务场景:如一个学生类,包含一个score成员变量,有可能时以下类型:int,double,String,因此为了方便,统一使用Object类型的变量接收。

        问题:取出之后的类型容易出现类型转换异常

    • 泛型类 泛型方法

        泛型类:在类上添加一个类型的定义,在使用类时指定一个类型,就可以对传入的数据进行类型检查,在取出时,不必进行类型转换了,这样的类就是泛型类。

            泛型接口,泛型抽象!

            把泛型定义到接口,类名后。

            在类中就可以使用泛型当成一个类型。

            成员变量的类型,方法返回值类型,方阿飞的形参类型,方法中的局部变量类型。

            new后不能用泛型。

        泛型类的缺点:每次针对不同的类型的参数,都必须创建不同类型的对象。这是的方法依赖于创建对象时的类型,他们之间的耦合度太高,为了降低这种耦合度,可以把泛型定义在方法上,这就是泛型方法。

        泛型方法:

         把泛型放到方法上定义!

        方法的返回值,形参类型,方法内部的局部变量类型。

        泛型没有多态性:

        想要实现泛型的类似多态性的用法,使用泛型通配符

        

        ?:任何类型都可以用

        ?extends A:A类和A类的子类可以使用

        ? super A:A类和A类的父类可以使用!

        Set接口:元素不重复

        不同的子类对重复的理解不同

          HashSet:使用哈希算法对元素进行唯一性限制

          TreeSet:使用二叉树结构保证元素唯一,且排序

        HashSet类:

        使用哈希算法保证元素唯一!

    哈希表=数组+链表

    数组:桶(bucket),槽位(slot)

    链表:元素发生“哈希碰撞”时,继续调用equals方法,若有多个元素,就是以链表形式存在的,共享同一个槽位。

    HashSet如何保证元素的唯一性?

    即:add(Object o)的执行过程:

    1. 先调用被添加元素的hashCode()方法,返回一个Int值。通过这个值计算出一个和槽位对应的一个值。
    2. 判断对应的槽位上是否为空,为空,就说明当前元素就是第一个元素,就直接添加。不为空,说明发生了哈希碰撞,再次调用被添加元素equals方法。把已经存在的元素当成参数。
    3. 返回值为true,重复了,要填间的元素不能添加。
    4. 返回值为false,没有重复,要添加的元素以链表的形式添加到对应的槽位上。
    5. 想要被HashSet窜出的对象所在的类,必须重写hashCode()和equals方法,
    6. IDE中可以自动生成,生成的原则就是让所有的成员变量都参与到这两个方法计算中。
    7. IDE就是Integrated Develpment Eviroment,集成开发环境。

          

    • 自定义泛型类
    • 自定义泛型方法
    • 泛型匹配

    List接口总结:

    • ArrayList:底层使用的是数组存储元素,Objcet[],线程不安全,效率高。查询快增删慢。
    • LinkedList:底层使用的是链表(双向链表),查询慢,增删快。
    • Vection:底层使用的是数组存储元素,线程安全,效率低。

    如何选择:Arraylist比较通用。如果增删多选择LinkedList,Vector基本不用,已经被Arraylist替代。

    练习:

    使用LinkedList类模拟栈结构的集合。(FILO,LIFO)FIFO: First in, First out.先进先出。 LIFO: Last in, First out.(注意模拟栈结构,不是直接使用LinkedList,即:有一个集合,其中的元素是先进后出。)

    代码内容:

    1.使用ArrayList实现字符串的去重

    思路1:创建一个新的空集合,将原集合中的数据依次放到新集合中,放的同时判断:新集合中是否已经包含了这个元素,如果不包含,再添加到新集合!!!如果已经包含,就不添加
    package com.test2;
    
    import java.util.ArrayList;
    
    /*
     * 使用ArrayList实现字符串的去重
     * List : 特点:元素可重复!!!
     * 
     * 思路:
     * 创建新的集合,把原集合中的元素放到新集合中,在放的同时,进行判断:
     *     如果存在,就不添加;如果不存在,就添加.
     *     最终新集合中就是去重后的结果!!!
     * 
     */
    public class Demo {
    
        public static void main(String[] args) {
            ArrayList list = new ArrayList();
            list.add("abc");
            list.add("abc");
            list.add("java");
            list.add("java");
            list.add("java");
            list.add("def");
            list.add("mysql");
            
            //创建一个新的集合
            ArrayList list2 = new ArrayList();
            //遍历原集合,到新集合中判断:
            for (Object obj : list) {
                if(!list2.contains(obj)){
                    list2.add(obj);
                }
            }
    
            //遍历新集合
            for (Object obj : list2) {
                System.out.println(obj);
            }
            
        }
    
    }

    思路2:

    思考:不使用新集合是否能达到去重目的??
    依次遍历集合中的元素,和其后的所有元素进行比较,若碰到相等的,就删除!
    package com.test2;
    
    import java.util.ArrayList;
    
    /*
     * 使用ArrayList实现字符串的去重
     * List : 特点:元素可重复!!!
     * 
     * 思路:
     * 创建新的集合,把原集合中的元素放到新集合中,在放的同时,进行判断:
     *     如果存在,就不添加;如果不存在,就添加.
     *     最终新集合中就是去重后的结果!!!
     * 
     * 思路2:不创建新集合
     *     依次拿出集合中的元素,和其后的所有元素进行比较,如果遇到相等的,就删除 后面的元素!!
     * 
     * 如果有多个连续出现的重复元素,会发生漏删除现象!
     * 优化:
     *     > 1.遇到删除的元素后,循环变量不要自增,继续判断当前位置上的元素
     *     > 2.从后往前遍历
     *     
     * 
     */
    public class Demo {
    
        public static void main(String[] args) {
            ArrayList list = new ArrayList();
            list.add("abc");
            list.add("java");
            list.add("abc");
            list.add("abc");
            list.add("java");
            list.add("def");
            list.add("mysql");
            list.add("java");
        
            //优化1:遇到删除的元素后,循环变量不要自增,继续判断当前位置上的元素
            for(int i = 0;i<list.size()-1;i++){
                for(int j = i+1;j<list.size();j++){
                    if(list.get(i).equals(list.get(j))){
                        //删除后面的元素
                        list.remove(j);
                        j--;
                    }
                }
            }
                    
            //优化2:从后往前遍历
            for(int i = 0;i<list.size()-1;i++){
                for(int j = list.size() - 1;j>i;j--){
                    if(list.get(i).equals(list.get(j))){
                        //删除后面的元素
                        list.remove(j);
                    }
                }
            }
            for (Object obj : list) {
                System.out.println(obj);
            }            
            
        }
    
    }

     3.使用数组保存10个范围在1-20之间的随机数,要求数值不能重复。

    方法一:
    public
    class Work1 { public static void main(String[] args) { //创建一个新数组 int[] arr = new int[10]; for(int i = 0;i < 10;i++){ arr[i] = (int) (Math.random() * 20 + 1);//生成随机数 for (int j = 0; j < i; j++) {//(遍历数组中储存进去的值,i中有几个值则循环几次) if (arr[j] == arr[i]) {//把储存在数组中的值j 和 随机出的值i 做比较 i--; //数组的值索引-1,i的循环次数回到上次 break; } } } for (int i : arr) { System.out.print(i + " "); }
    方法二:
    package
    com.test; import java.util.Arrays; /* * 使用数组保存10个范围在1-20之间的随机数.要求数值不能重复! */ public class RandomArrayDemo { public static void main(String[] args) { int[] arr = generateArray(5, 0, 5); System.out.println(Arrays.toString(arr)); } /** * 随机产生一个int数组. * @param length 生成数组的长度 * @param start 数组中最小值 * @param end 数组中最大值 * @return 随机的数组 */ public static int[] generateArray(int length,int start,int end){ int[] arr = new int[length]; for (int i = 0; i < arr.length; i++) { //产生随机值 int r = (int)(Math.random() * (end - start + 1) + start); //和已经存在的元素进行比较,防止默认值的元素放不进去!! for(int j = 0;j<i;j++){ if(arr[j] == r){ //重新产生随机数 r = (int)(Math.random() * (end - start + 1) + start); j=-1;//!!! i-- } } // arr[i] = r; } return arr; } }

     4.从键盘录入一个字符串,将其中以m/M之前字母开头的单词放到一个集合中,
        以m/M之后的字母开头的单词放到另一个集合中,并将两个集合元素输出.

    package com.test;
    
    import java.util.ArrayList;
    import java.util.Scanner;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    /*
     * 从键盘录入一个字符串,将其中以m/M之前字母开头的单词放到一个集合中,
        以m/M之后的字母开头的单词放到另一个集合中,并将两个集合元素输出.
     */
    public class ExtractWordsDemo {
    
        public static void main(String[] args) {
            String s =    new Scanner(System.in).nextLine();
            Matcher m1 = Pattern.compile("\b[a-lA-L]\w*\b").matcher(s);
            Matcher m2 = Pattern.compile("\b[m-zM-Z]\w*\b").matcher(s);
            
            ArrayList<String> list1 = new ArrayList<String>();
            ArrayList<String> list2 = new ArrayList<String>();
            
            while(m1.find()){
                list1.add(m1.group());
            }
            while(m2.find()){
                list2.add(m2.group());
            }
    
            for (String string : list1) {
                System.out.println(string);
            }
            
            System.out.println("---------------");
            for (String string : list2) {
                System.out.println(string);
            }
        }
    
    }

  • 相关阅读:
    fzu 2138
    hdu 1598 暴力+并查集
    poj 1734 floyd求最小环,可得到环上的每个点
    floyd求最小环 模板
    fzu 2087并查集的运用求最小生成树的等效边
    hdu 2586 lca在线算法(朴素算法)
    CF 602 D. Lipshitz Sequence 数学 + 单调栈 + 优化
    Problem 2238 Daxia & Wzc's problem 1627 瞬间移动
    D. Tavas and Malekas DFS模拟 + kmp + hash || kmp + hash
    K-th Number 线段树的区间第K大
  • 原文地址:https://www.cnblogs.com/akinodoo/p/9937058.html
Copyright © 2011-2022 走看看