Javaの集合学习

  • 集合
    集合和数组很类似,都是容器可以存放多个数据。但是数组有一个致命的弱点,数组长度固定不能动态增加。所以jdk就有创建了另外一种可以动态的随意增加长度的容器,叫做集合
  • 集合的分类(不同的集合之间,存放在它们里面的数据的类型特点不一致)
    Collection集合:又分为了List集合和Set集合
    Map集合:

  • List集合
      容器:存储数据
             基本功能:增删改查
             增加:
                boolean add(E e) 将指定的元素追加到此列表的末尾(可选操作)。 
                id add(int index, E element) 将指定的元素插入此列表中的指定位置(可选操作)。
                boolean addAll(Collection<? extends E> c) 按指定集合的迭代器(可选操作)返回的顺序将指定集合中的所有元素附加到此列表的末尾。   
                boolean addAll(int index, Collection<? extends E> c) 将指定集合中的所有元素插入到此列表中的指定位置(可选操作)。
             删除:
                void clear() 从此列表中删除所有元素(可选操作)。 
                void remove(int index) 删除该列表中指定位置的元素(可选操作)。  
                boolean remove(Object o) 从列表中删除指定元素的第一个出现(如果存在)(可选操作) 
                boolean removeAll(Collection<?> c) 从此列表中删除包含在指定集合中的所有元素(可选操作)。   
             修改:
               E set(int index, E element) 用指定的元素(可选操作)替换此列表中指定位置的元素。    
             查询:  
               E get(int index) 返回此列表中指定位置的元素。 
               Iterator<E> iterator() 以正确的顺序返回该列表中的元素的迭代器。 
               Iterator迭代器对象
                      hasNext():可以通过它判断 集合里面是否还有下一个元素
                      next():获取下一个元素
               ListIterator<E> listIterator() 返回列表中的列表迭代器(按适当的顺序)。 
                      正向迭代:
                           ListIterator lit =list.listIterator();
                             while(lit.hasNext()){
                                System.out.println(lit.next());
                            }
                      反向迭代:
                            while(lit.hasPrevious()){
                                System.out.println(lit.previous());
                             }
                      注意:使用反向迭代,先要正向迭代一次。
               ListIterator<E> listIterator(int index) 从列表中的指定位置开始,返回列表中的元素(按正确顺序)的列表迭代器。 
             判断:
                boolean contains(Object o) 如果此列表包含指定的元素,则返回 true
    常用子类:
                ArrayList:底层是数组结构,默认长度是10,当我们存储的数据超过了10个之后,它会自动的增加原来的一半也是自动增加5个长度
                LinkedList:底层是链表结构,无下标排序。
                  例子: 模拟堆栈和队列的特点
                       堆栈:先进后出 (如同水杯)
                       队列:先进先出 (如同水管)
                Vector :底层是数组结构,默认长度是10,超过默认长度自增10个。它和list相比是线程安全。这个是jdk1.2就有的原始元素,以及被ArrayList淘汰

    代码实例

    package com.ly.listapi;
    
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    import java.util.ListIterator;
    
    /**
     * 使用ArrayList测试List的api
     */
    public class ListApi {
        public static void main(String[] args) {
            //创建一个集合
            List list =new ArrayList();
            List list2 =new ArrayList();
            String str="hi update";
            list2.add(1);
            list2.add(2);
            //添加元素
            list.add("张三");
            list.add(true);
            list.add(10);
            list.add(0.001f);
            list.add("1234444");
            //制定的下标添加
            list.add(2,"hi");
            //在制定的下标插入一个集合
            list.addAll(3,list2);
    //        System.out.println(list2.size());
            //删除集合中所有的元素
           /* list2.clear();*/
    //        System.out.println(list2.size());
            System.out.println("----------------------");
            list.remove(0);
            list.remove(true);
            list.removeAll(list2);
            //修改元素
            list.set(0,"hi update");
            //包含
    //        System.out.println(list.contains(str));
            /**
             * 遍历集合 (4种)
             */
            /*for(int i=0;i<list.size();i++){
                System.out.println(list.get(i));
            }*/
           /* for(Object obj:list){
                System.out.println(obj);
            }*/
            /***
             * 集合特有的迭代方式 迭代器
             */
           /* Iterator it =list.iterator();
            while(it.hasNext()){
                System.out.println(it.next());
            }*/
            /***
             * 正向迭代
             */
               ListIterator lit =list.listIterator();
               while(lit.hasNext()){
                System.out.println(lit.next());
            }/**/
            /***
             * 反向迭代
             */
            /* ListIterator lit =list.listIterator();*/
             while(lit.hasPrevious()){
                System.out.println(lit.previous());
             }
    //        System.out.println(list.size());
        }
    }
    package com.ly.linkedListDemo;
    
    import java.util.LinkedList;
    import java.util.List;
    
    /***
     * 使用linkedList 完成堆栈和队列的效果
     *  removeFirst()
        从此列表中删除并返回第一个元素。
       removeLast()
        从此列表中删除并返回最后一个元素。
     */
    public class LinkedListDemo {
        public static void main(String[] args) {
            LinkedList list =new LinkedList();
            list.add(111);
            list.add("222");
            list.add(true);
          /*  while(!list.isEmpty()){
                System.out.println(list.removeLast());
            }*/
            while(!list.isEmpty()){
                System.out.println(list.removeFirst());
            }
    
        }
    }
  • ArrayList去重(其类一致的时候会将值进行比较【个人理解,里面存储的数据类型不一样,例如Person类,遇上相同的数据类型会调用本类(例如同样是Person就调用Person)中的equals方法,但像Person这样不具备比较性的则要继承Comparable类来实现重写equal方法】
      依据equal方法(正常情况是使用hash值),则此时我们需要重写equal方法
    public class ArrayListDemo {
        public static void main(String[] args) {
            ArrayList list =new ArrayList();
            Person p=new Person();
            p.setAge(19);
            p.setUsername("aaaa");
            p.setPassword("aaaa");
            list.add(p);
            Person p1=new Person();
            p1.setAge(19);
            p1.setUsername("aaa1");
            p1.setPassword("aaaa");
            list.add(p1);
            ArrayList newList= replaceList(list);
            for(int i=0;i<newList.size();i++){
                Person pi= (Person)newList.get(i);
                System.out.println(pi.getUsername());
            }
        }
     private static ArrayList replaceList(ArrayList list) {
            ArrayList newlist =new ArrayList();
            Iterator it =list.iterator();
            while(it.hasNext()){
                Object obj =it.next();
                if(!newlist.contains(obj)){
                    newlist.add(obj);
                }
            }
            return newlist;
        }
    
    }
    package com.ly.po;
    
    /***
     * 用户对象
     */
    public class Person implements Comparable{
         private String username;
         private String password;
         private int age;
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        @Override
        public int hashCode() {
            return 0;
        }
    
        public Person() {
        }
    
        public Person(String username, String password, int age) {
            this.username = username;
            this.password = password;
            this.age = age;
        }
    
        @Override
        public boolean equals(Object obj) {
            if(obj instanceof Person){
                return this.username.equals(((Person) obj).getUsername()) && this.age==((Person) obj).getAge() && this.password.equals(((Person) obj).getPassword());
            }else{
                return false;
            }
        }
         //p.compareTo(p1){}
        @Override
        public int compareTo(Object o) {
            if(o instanceof  Person){
                Person p =(Person) o;
                return (this.getUsername().hashCode()-p.getUsername().hashCode()) -(this.age-p.age) -(this.getPassword().hashCode()-p.getPassword().hashCode());
            }
            return -1;
        }
    }

    这个的思路是:新建一个集合,迭代list,建立一个Object对象,使其通过if(!newlist.contains(obj))方法来实现判断【这里contains()方法会引用equals方法(所以需要重写equals方法),此时会将集合里的数据调用equal方法去比较obj】如果不包含则add()加入,包含则不添加

  • List有序,元素可重复,集合有索引  ||  Set无序,元素不可重复,集合无索引


  • Set的共性方法与List差不多
    1.HashSet底层是哈希表(由哈希值(有16位进制组成的数)组成的表),在Java中每个元素都有一个HashCode方法,方法返回所在的物理内存地址(一般情况下每个数的hash值不同)
    (输出对象(例如Person p)输出p(如果没有toString方法,则输出物理内存地))
  • HashSet无顺序,不允许重复
    HashSet存自定义对象去重思路:比较重复是根据hashcode值来比较,如果一样就使用equal方法再进行比较,但一般情况下hashcode的值是不相同的,所以要在自定义对象中重写hashcode方法使hashcode的值相等【public int hashcode( return 50;)】
    package com.ly.com.ly.setdemo;
    
    import com.ly.po.Person;
    
    import java.util.HashSet;
    import java.util.Iterator;
    
    /***
     * 测试hashset
     */
    public class HashSetDemo {
        public static void main(String[] args) {
            HashSet set =new HashSet();
            set.add("java 01");
            set.add("java 02");
            set.add("java 03");
            set.add("java 04");
            set.add("java 01");
            set.add("java 03");
    
            Iterator it =set.iterator();
            while(it.hasNext()){
                System.out.println(it.next());
            }
            Person p=new Person();
            p.setUsername("zhangsan");
            p.setPassword("123456");
            p.setAge(19);
            Person p2 =new Person();
            p2.setUsername("zhangsan");
            p2.setPassword("123456");
            p2.setAge(19);
            HashSet pset =new HashSet();
            pset.add(p);
            pset.add(p2);
            Iterator it1 =pset.iterator();
            while(it1.hasNext()){
                System.out.println(it1.next());
            }
        }
    }
  • TreeSet 有序无下标,以二叉树来比较,底层是二分叉树,有自己的排序规则,存放元素必须具备比较性(八大元素和String都实现了Comparable)
    要是自定义对象具备比较性,实现Comparable接口,实现comparableTo方法来进行比较【public int comparableTo(Object o){。。。}】
    package com.ly.com.ly.treesetdemo;
    
    import com.ly.com.ly.comable.MyComparator;
    import com.ly.po.Person;
    
    import java.util.Iterator;
    import java.util.TreeSet;
    
    public class TreeSetDemo {
        public static void main(String[] args) {
           /* TreeSet tset =new TreeSet();*/
    //        tset.add(true);
    //        tset.add(false);
    //        tset.add("java 03");
    //        tset.add("java 04");
    //        tset.add("java 01");
    //        Iterator it =tset.iterator();
    //        while(it.hasNext()){
    //            System.out.println(it.next());
    //       }
           TreeSet tset =new TreeSet(new MyComparator());
           Person p =new Person("211","1111",22);
           Person p1 =new Person("222","2222",30);
           Person p2 =new Person("123","2222",30);
           tset.add(p);
           tset.add(p1);
           //p和p1已经比较完了  p p1  p1 p
           tset.add(p2);
            Iterator it =tset.iterator();
            while(it.hasNext()){
                Person obj =(Person)it.next();
                System.out.println(obj.getUsername());
            }
    
        }
    }
    package com.ly.com.ly.comable;
    
    import com.ly.po.Person;
    
    import java.util.Comparator;
    
    public class MyComparator implements Comparator{
    
        @Override
        public int compare(Object o1, Object o2) {
            if(o1 instanceof Person && o2 instanceof  Person){
               return (((Person) o1).getUsername().hashCode()+((Person) o1).getPassword().hashCode()+((Person) o1).getAge())
                          -
                       (((Person) o2).getUsername().hashCode()+((Person) o2).getPassword().hashCode()+((Person) o2).getAge());
            }
            return 0;
        }

        /**
         * 实现Comparable 接口
         * 然后实现compareTo方法
         * @param o
         * @return
         */
        public int compareTo(Object o) {
            if (o instanceof User) {
                User user = (User) o;
                //比较两个对象的age 返回的是它们的减法值,如果this.age>user.age返回正数,反之返回负数,相等返回0
                return this.age-user.age;
    //            System.out.println("this:"+(this.name.hashCode()+this.age));
    //            System.out.println("user:"+(user.name.hashCode()+user.age));
    //            System.out.println("compare:"+((this.name.hashCode()+this.age)-(user.name.hashCode()+user.age)));
    //            return (this.name.hashCode()+this.age)-(user.name.hashCode()+user.age);
            }else{
                throw new RuntimeException("不是用户类");
            }
        } },

    思路:TreeSet在add()对象时会自动将对象排序,但需要有可比较性,如自定义类就需要实现comparable类,实现comparableTo方法才可以进行比较

  • TreeSet可以自传比较器(其本身自带二叉树比较)
    除了上面那种实现接口比较以外,还有一种比较方式:
    
    /**
     * 新建一个比较器对象
     * @author Administrator
     *
     */
    public class MyCompare implements Comparator{
    
        /**
         * 编写具体的比较规则
         */
        public int compare(Object o1, Object o2) {
            if(o1 instanceof User && o2 instanceof User){
                User user1 =(User)o1;
                User user2 =(User)o2;
                return user1.getAge()-user2.getAge();
            }else{
                throw new RuntimeException("要比较的类型不一致无法比较");
            }
    
        }
    
    }
    
      public static void main(String[] args) {
              TreeSet set1 =new TreeSet(new MyCompare());
                 set1.add(new User("java01",11));
                 set1.add(new User("java02",12));
                 set1.add(new User("java01",11));
                 set1.add(new User("java03",13));
                 Iterator it1 =set1.iterator();
                 while(it1.hasNext()){
                       System.out.println(it1.next());
                }
     

    当自定义(就是自己重写的equals方法)和比较器(自己写的)都存在时,以比较器为主

  • HashSet和TreeSet的区别
    HashSet        底层是哈希表,没有顺序,不允许重复
    TreeSet       底层是二叉树结构,有顺序 ,不允许重复
    但它们都是存储数据的容器
  • 泛型  由于集合数据类型不同,迭代大多需要强行转型(由大转小可能导致数据丢失),所以泛型应此而生了
    泛型:因为集合可以存储任意对象的元素,所以在开发过程中无法保证集合内元素的一致性,在早期的遍历过程中,
    需要我们不断的去把Object 转换成我们所需要的对象的类型,来过滤掉其他数据类型,遍历出来。时间长了程序员就觉得这样很麻烦,
    于是就有了泛型。泛型提供了更好的解决方案:类型参数。例如,ArrayList类用一个类型参数来指出元素的类型。 泛型是Java
    1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。 这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。 Java泛型被引入的好处是安全简单。 在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,
    而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。 泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。

    作用:
    1.申明准确类型(申明定义准确类型,集合只能插入准确的类型)

    package com.ly.com.ly.genericity;
    
    import com.ly.po.Person;
    
    import java.util.ArrayList;
    
    /***
     * 通过ArrayList 来说明泛型的作用
     * 1.申明准确的类型
     *     声明的时候就顶了准确的类型,以后集合里面只能插入定义的类型
     */
    public class ArrayListDemo {
        public static void main(String[] args) {
            ArrayList<Person> plist=new ArrayList<>();
            plist.add(new Person("aaa","aaaa",18));
            for(Person p:plist){
                System.out.println(p.getUsername()+"	"+p.getPassword()+"	"+p.getAge());
            }
        }
    }

    2.定义了抽象类型(一般用来编写扩展性强的类或者是工具类)

    package com.ly.com.ly.dao;
    
    import com.ly.po.stu;
    //T 或者E 你就可以看成是Object
    public class BaseDao<T>{
           private T t;
           public void add(T t){
               System.out.println(t+"添加");
           }
           public void update(T t){
               System.out.println(t+"修改");
           }
           public void find(T t){
               System.out.println(t+"查询");
           }
           public void delete(T t){
               System.out.println(t+"删除");
           }
    }

    然后接下来顺便将例子放出

    package com.ly.com.ly.test;
    
    import com.ly.com.ly.dao.StuDao;
    import com.ly.com.ly.dao.teacherDao;
    import com.ly.po.stu;
    import com.ly.po.teacher;
    
    public class demo {
        public static void main(String[] args) {
            teacher t =new teacher("t1","男",1000);
            teacherDao tdao =new teacherDao();
            tdao.add(t);
            tdao.update(t);
           /* tdao.addtea(t);
            tdao.updatetea(t);
            tdao.findtea(t);
            tdao.deletetea(t);*/
            stu s =new stu("aaa","男","软件1班");
            StuDao dao =new StuDao();
            dao.add(s);
            /*dao.addStu(s);
            dao.updatestu(s);
            dao.findstu(s);
            dao.deletestu(s);*/
        }
    }
    package com.ly.com.ly.dao;
    
    import com.ly.po.stu;
    import org.junit.Test;
    
    /***
     * 模拟完成stu的增删改查方法
     * */
    public class StuDao extends BaseDao{
        /*public void addStu(stu stu){
            System.out.println(stu+"添加");
        }
        public void updatestu(stu stu){
            System.out.println(stu+"修改");
        }
        public void deletestu(stu stu){
            System.out.println(stu+"删除");
        }
        public void findstu(stu stu){
            System.out.println(stu+"查询");
        }*/
    }
    package com.ly.com.ly.dao;
    
    
    import com.ly.po.stu;
    import com.ly.po.teacher;
    
    public class teacherDao extends  BaseDao{
        /*public void addtea(teacher tea){
            System.out.println(tea+"添加");
        }
        public void updatetea(teacher tea){
            System.out.println(tea+"修改");
        }
        public void deletetea(teacher tea){
            System.out.println(tea+"删除");
        }
        public void findtea(teacher tea){
            System.out.println(tea+"查询");
        }*/
    }
  • Map集合
    java中的map集合使用键(key)值(value)来保存数据,其中值(value)可以重复,但键(key)必须是唯一,也可以为空,但最多只能有一个key为空,
    它的主要实现类有HashMap、LinkedHashMap、TreeMap。
    共性方法:
        1.添加:
           put();
           putAll();
        2.删除
           clear();清空数据
           remove();通过Key删除单个值,还可以放集合删除
        3.判断
          containsKey(Object key);
          containsValue(Object value);
          isEmpty();
        4.获取
           get();
           size();
           values();
           entrySet();
           keySet(); 
    
    map的遍历:
          Map<String, String> map=new HashMap<String,String>();
              map.put("01", "java 01");
              map.put("02", "java 02");
              map.put("03", "java 03");
              map.put("04", "java 04");
              map.put("05", "java 05");
          
          /**
               * 遍历方式1
               */
              //获取到map中key值的集合
              Set<String> keys= map.keySet();
              //遍历其中的key值的集合
              Iterator<String> it=keys.iterator();
              //根据key获取到value
              while(it.hasNext()){
                  String key =it.next();
                  String value =map.get(key);
                  System.out.println("key:"+key+" value:"+value);
              }
            /**
               * 遍历方式2
               */
              //获取到map的关系映射放到set集合中
              Set<Map.Entry<String, String>> sets =map.entrySet();
              //迭代集合
              Iterator<Map.Entry<String, String>> it2=sets.iterator();
              while(it2.hasNext()){
                  //获取到集合中的每个映射关系
                  Map.Entry<String, String> map2 =it2.next();
                  //获取key和value
                  System.out.println("key:"+map2.getKey()+" value:"+map2.getValue());
              }
    package com.ly.mapdemo;
    
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Set;
    
    /***
     * 学习map的数据类型
     * mapapi:
     *     V put(K key, V value)
             将指定的值与该映射中的指定键相关联(可选操作)
     */
    public class HashMapDemo {
        public static void main(String[] args) {
            HashMap map =new HashMap();
            HashMap map1 =new HashMap();
            map1.put(null,"ssss");
            map1.put("lll",1111);
            /***
             * map添加 key,value
             * k,v
             */
            map.put("aaa",1111);
            map.put("cccc",222);
            map.put("aaa",2222);
            map.putAll(map1);
            System.out.println(map.containsKey("sskl;sks;lsk"));
            System.out.println(map.containsValue("1111"));
            System.out.println(map.get(null));
    //        map1.clear();
            map1.remove(null);
            System.out.println(map1.size());
            /***
             * map的数据结构比较特殊,它是存一组数据,那如果来区分存进去的每组数据和每组数据列,它是通过列名来区分
             * 所以说key  的值不允许出现重复的,或者会覆盖原先的数据。
             */
            System.out.println(map.size());
            /***
             * map遍历方式1
             */
           /* Set keySet=map.keySet();
            Iterator it =keySet.iterator();
            while(it.hasNext()){
                System.out.println(map.get(it.next()));
            }*/
            /***
             * 第二种遍历方式
             */
            Set<Map.Entry<String,Object>> set = map.entrySet();
            Iterator<Map.Entry<String,Object>> it =set.iterator();
            while(it.hasNext()){
                Map.Entry<String,Object> obj=it.next();
                System.out.println(obj.getKey()+":"+obj.getValue());
            }
        }
    }

    思路:上面entryset()看成是一个Set对象,Map集合名.Entry<数据类型,数据类型>是将集合看成由一个个Key和Value组成的对象



  • Hashtable:底层是哈希表的数据结构,不可以存入null键和null值,是线程同步的。jdk1.0效率低
    HashMap:底层也是哈希表数据结构,允许存入null键null值,线程不同步。(这 HashMap类大致相当于 Hashtable,除了它是不同步的,允许空值。)jdk2.0效率高
    TreeMap:底层是二叉树结构,线程不同步,可以给map集合中的key 进行排序。
    总结:通过这三个具体类的描述,我们发现它和set集合是不是很像,其实set集合底层调用的都是map集合的方法。

  • 例子
    我们用user对象来作为key,判断如果年龄和姓名一样的user对象在map中只能有一个key存一个值。
    
    public class User implements Comparable{
        private String name;
        private int age;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
        
        public User(String name, int age) {
            super();
            this.name = name;
            this.age = age;
        }
        public String toString() {
            return "User [name=" + name + ", age=" + age + "]";
        }
    
        /**
         * 重写hashCode方法
         *    
         */
    
        public int hashCode() {
            // TODO Auto-generated method stub
            return (name.hashCode()+age)*100;
        }
        /**
         * 重写 equals方法
         */
        public boolean equals(Object obj) {
            if(obj instanceof User){
                User u =(User)obj;
                return this.name.equals(u.name) && this.age==u.age;
            }else{
                return false;
            }
        }
    
        /**
         * 重写比较性的方法
         */
        public int compareTo(Object o) {
            if(o instanceof User){
                User u =(User)o;
                int num=this.age-u.age;
                if(num==0){
                    return this.name.compareTo(u.name);
                }
                return num;
            }else{
               throw new RuntimeException("传错了");    
            }
        }
        
    }
    
    public class Test {
           public static void main(String[] args) {
    //          Map<User,String> map =new HashMap<User,String>();
              Map<User,String> map=new TreeMap<User, String>();
              map.put(new User("张三",18), "北京");
              map.put(new User("张三",18), "北京");
              map.put(new User("王武",18), "北京");
              map.put(new User("王武",18), "上海");
              map.put(new User("赵六",19), "武汉");
              /**
               * 迭代
               */
             Set<User> keys= map.keySet();
             Iterator<User> it= keys.iterator();
             while(it.hasNext()){
                 User user =it.next();
                 String value =map.get(user);
                 System.out.println("key:"+user+"  value:"+value);
             }
           }
    }
原文地址:https://www.cnblogs.com/YanZhuDL/p/11549672.html