Java集合框架——Map

Map

Map与Collection的不同

  • Map与Collection在集合框架中属并列存在
  • Map存储的是键值对
  • Map存储元素使用put方法,Collection使用add方法
  • Map集合没有直接取出元素的方法,而是先转成Set集合,在通过迭代获取元素
  • Map集合中键要保证唯一性

    总结:Map是一个双列集合,一次存一对(键值对),而且要保证键的唯一性。

Map集合中的常用方法

Map集合中的常用方法有:

  • 添加
    • V put(K key, V value)
    • void putAll(Map<? extends K,? extends V> m)
  • 删除
    • void clear()
    • rV remove(Object key)
  • 判断
    • boolean containsKey(Object key)
    • boolean containsValue(Object value)
    • boolean isEmpty()
  • 获取
    • V get(Object key)
    • int size()
    • Collection<V> values()
    • entrySet()
    • keySet()

下面我们就来演示以上的一些方法。首先我们创建一个集合容器,使用Map接口的一个实现类——HashMap。

Map<String, String> map = new HashMap<String, String>();
  • 添加元素

    map.put("01", "zhangsan1");
    map.put("02", "zhangsan2");
    map.put("03", "zhangsan3");

    注意:添加元素时,如果出现相同的键,那么后添加的值会覆盖原有的键对应的值,并且put()方法会返回被覆盖的值。如:

    System.out.println("put:"+map.put("01", "zhangsan1")); // null,因为"01"键所对应的原来的值为null
    System.out.println("put:"+map.put("01", "wangwu")); // zhangsan1,因为此时"01"键所对应的原来的值为"zhangsan1"
  • 判断

    System.out.println("containsKey:"+map.containsKey("022")); // containsKey:false
  • 删除

    System.out.println("remove:"+map.remove("02")); // remove:zhangsan2
  • 获取

    System.out.println("get:"+map.get("023")); // get:null

    在通过get()方法获取集合中的元素时,可以发现:

    1. 可以通过get()方法的返回值来判断一个键是否存在,通过返回null来判断
    2. HashMap允许使用null值和null,如:

      map.put("04", null); 
      System.out.println("get:"+map.get("04")); // get:null
      
      map.put(null, "haha");
      System.out.println("get:"+map.get(null)); // get:haha
  • 获取集合中所有的值

    Collection<String> coll = map.values();
    System.out.println(coll);

Map集合中常用类

  • Map

    • HashTable:底层是哈希表数据结构,不可以存入null键null值。该集合是线程同步的。JDK1.0,效率低
    • HashMap:底层是哈希表数据结构,允许使用null值和null键,该集合是不同步的。JDK1.2,效率高
      • LinkedHashMap:HashMap接口的子类,该接口是哈希表和链接列表的实现,具有可预知的迭代顺序。在后面的学习中会经常用到。
    • TreeMap:底层是二叉树数据结构,线程不同步,可以给Map集合中的键进行排序。排序原理与TreeSet相同。

    Map和Set很像,其实Set底层就是使用了Map集合。

Map集合的两种取出方式

Map集合的两种取出方式:

  1. Set<K> keySet():将map所有的键存入到Set集合,因为Set集合具备迭代器,所以可以通过迭代方式取出所有的键,再根据get()方法,获取每一个键对应的值。Map集合的取出原理:将map集合转成set集合,再通过迭代器取出。
  2. Set<Map.Entry<K,V>> entrySet():将map集合中的映射关系存入到了Set集合中,而这个关系的数据类型就是:Map.Entry

现在我们就来演示Map集合的第一种取出方式。

import java.util.*;

class MapDemo {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<String, String>();
        map.put("02", "zhangsan2");
        map.put("03", "zhangsan3");
        map.put("01", "zhangsan1");
        map.put("04", "zhangsan4");

        // 先获取map集合的所有的键的Set集合,keySet()
        Set<String> ketSet = map.keySet();
        // 有了Set集合,就可以获取其迭代器
        Iterator<String> it = ketSet.iterator();
        while(it.hasNext()) {
            String key = it.next();
            // 有了键就可以通过map集合的get()方法获取其对应的值
            String value = map.get(key);
            System.out.println("key:"+key+", value:"+value);
        }
    }
}

紧接着我们再来演示Map集合的第二种取出方式。

import java.util.*;

class MapDemo {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<String, String>();
        map.put("02", "zhangsan2");
        map.put("03", "zhangsan3");
        map.put("01", "zhangsan1");
        map.put("04", "zhangsan4");

        // 将map集合中的映射关系取出,存入到set集合中
        Set<Map.Entry<String, String>> entrySet = map.entrySet();
        Iterator<Map.Entry<String, String>> it = entrySet.iterator();
        while(it.hasNext()) {
            Map.Entry<String, String> me = it.next();
            String key = me.getKey();
            String value = me.getValue();
            System.out.println(key+":"+value);
        }
    }
}

Map.Entry,其实Entry也是一个接口,它是Map接口中的一个内部接口。源码我们可以理解为:

interface Map {
    // entry就是Map接口中的内部接口
    public static interface Entry {
        public abstract Object getKey();
        public abstract Object getValue();
    }
}
class HashMap implements Map {
    class HaHa implements Map.Entry {
        public Object getKey() {}
        public Object getValue() {}
    }
}

练习一、每一个雇员都有对应的归属地。雇员Employee,地址String。雇员属性:姓名,年龄。将雇员和归属地存储到HashMap集合中并取出,注意:姓名和年龄相同的视为同一个雇员,须保证雇员的唯一性。
解:

  1. 描述雇员,由于要将雇员对象作为键存入HashMap集合中,所以为了保证键的唯一性,雇员类中应该覆盖掉hashCode()和equals()方法。

    public class Employee {
        private String name;
        private int age;
    
        public Employee() {
            super();
        }
    
        public Employee(String name, int age) {
            super();
            this.name = name;
            this.age = 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;
        }
    
        @Override
        public String toString() {
            return "Employee [name=" + name + ", age=" + age + "]";
        }
    
        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + age;
            result = prime * result + ((name == null) ? 0 : name.hashCode());
            return result;
        }
    
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            Employee other = (Employee) obj;
            if (age != other.age)
                return false;
            if (name == null) {
                if (other.name != null)
                    return false;
            } else if (!name.equals(other.name))
                return false;
            return true;
        }
    
    }
  2. 定义Map容器,将雇员作为键,地址作为值存入。

    Map<Employee, String> map = new HashMap<Employee, String>();
    map.put(new Employee("xiaozhang", 24), "北京");
    map.put(new Employee("laoli", 34), "上海");
    map.put(new Employee("mingming", 26), "南京");
    map.put(new Employee("xili", 30), "广州");
    map.put(new Employee("laoli", 34), "铁岭");
  3. 获取map集合中的元素,在这里我们只使用keySet()方法进行取出。

    Set<Employee> keySet = map.keySet();
    for (Employee employee : keySet) {
        String value = map.get(employee);
        System.out.println(employee.getName() + ":" + employee.getAge() + "..." + value);
    }

练习二、现在我们又有了这样一个需求:按照雇员的年龄进行升序排序并取出。
解:因为数据是以键值对的形式存在的。所以要使用可以排序的Map集合——TreeMap。

  1. 描述雇员,为了能按照雇员的年龄进行升序排序,我们可以让自定义的Employee类实现Comparable接口(强制让Employee类具备比较性),覆盖compareTo()方法。

    public class Employee implements Comparable<Employee> {
        private String name;
        private int age;
    
        public Employee() {
            super();
        }
    
        public Employee(String name, int age) {
            super();
            this.name = name;
            this.age = 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;
        }
    
        @Override
        public String toString() {
            return "Employee [name=" + name + ", age=" + age + "]";
        }
    
        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + age;
            result = prime * result + ((name == null) ? 0 : name.hashCode());
            return result;
        }
    
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            Employee other = (Employee) obj;
            if (age != other.age)
                return false;
            if (name == null) {
                if (other.name != null)
                    return false;
            } else if (!name.equals(other.name))
                return false;
            return true;
        }
    
        @Override
        public int compareTo(Employee o) {
            int temp = this.age - o.age;
            return temp == 0 ? this.name.compareTo(o.name) : temp;
        }
    
    }
  2. 定义Map容器,将雇员作为键,地址作为值存入。

    Map<Employee, String> map = new TreeMap<Employee, String>();
    
    map.put(new Employee("xiaozhang", 24), "北京");
    map.put(new Employee("laoli", 34), "上海");
    map.put(new Employee("mingming", 26), "南京");
    map.put(new Employee("xili", 30), "广州");
    map.put(new Employee("laoli", 34), "铁岭");
  3. 获取map集合中的元素,在这里我们只使用entrySet()方法进行取出。

    Set<Map.Entry<Employee, String>> entrySet = map.entrySet();
    for (Map.Entry<Employee, String> me : entrySet) {
        Employee key = me.getKey();
        String value = me.getValue();
        System.out.println(key.getName() + ":" + key.getAge() + "..." + value);
    }

练习三、现在我们又有了这样一个需求:按照雇员的姓名进行升序排序并取出。
解:因为数据是以键值对的形式存在的。所以要使用可以排序的Map集合——TreeMap。

  1. 描述雇员,雇员类在以上练习中我们已描述,在此省略。
  2. 定义一个按姓名排序的比较器。

    Comparator<Employee> comparator = new Comparator<Employee>() {
    
        @Override
        public int compare(Employee o1, Employee o2) {
            int temp = o1.getName().compareTo(o2.getName());
            return temp == 0 ? o1.getAge() - o2.getAge() : temp;
        }
    };
  3. 定义Map容器,将雇员作为键,地址作为值存入。

    Map<Employee, String> map = new TreeMap<Employee, String>(comparator);
    
    map.put(new Employee("xiaozhang", 24), "北京");
    map.put(new Employee("laoli", 34), "上海");
    map.put(new Employee("mingming", 26), "南京");
    map.put(new Employee("xili", 30), "广州");
    map.put(new Employee("laoli", 34), "铁岭");
  4. 获取map集合中的元素,在这里我们只使用entrySet()方法进行取出。

    Set<Map.Entry<Employee, String>> entrySet = map.entrySet();
    for (Map.Entry<Employee, String> me : entrySet) {
        Employee key = me.getKey();
        String value = me.getValue();
        System.out.println(key.getName() + ":" + key.getAge() + "..." + value);
    }

练习四、获取字符串(比如”bwaerbctyxbacecrtdcvr”)中每一个字母出现的次数,要求结果格式为:a(2)b(1)d(3)…
解:通过结果发现,每一个字母都有对应的次数,说明字母和次数之间具有映射关系。注意:当发现有映射关系时,可以选择map集合,因为map集合中存放的就是映射关系。
本题思路:

  1. 将字符串转换为字符数组。因为要对每一个字母进行操作。
  2. 定义一个Map集合,因为打印结果的字母有顺序,所以使用TreeMap集合。
  3. 遍历字符数组,将每一个字母作为键去查map集合。如果返回null,将该字母和1存入到map集合中。如果返回不是null,说明该字母在map集合中已经存在,并有对应次数。那么就获取该次数并进行自增,然后将该字母和自增后的次数存入到map集合中,覆盖掉原来键所对应的值。
  4. 将map集合中的数据变成指定的字符串返回。
public class Test {

    public static void main(String[] args) {
        /*
         * 作业:"bwaerbctyxbacecrtdcvr"
         * 获取字符串中每一个字母出现的次数,要求结果格式:a(2)b(1)d(3)...
         */
        String str = "bw?aer+bct=yxb-acecrtdcvr";
        String char_count = getCharCount(str);
        System.out.println(char_count);
    }

    public static String getCharCount(String str) {
        // 1,将字符串转成字符数组
        char[] chs = str.toCharArray();
        // 2,定义Map集合表
        Map<Character, Integer> map = new TreeMap<Character, Integer>();
        // 3,遍历字符数组,获取每一个字母
        for (int i = 0; i < chs.length; i++) {

            // 只对字母操作
            if (!(chs[i] >= 'a' && chs[i] <= 'z' || chs[i] >= 'A' && chs[i] <= 'Z')) {
                continue;
            }

            // 将遍历到的字母作为键去查表,获取值
            Integer value = map.get(chs[i]);
            int count = 0; // 用于记录次数
            // 如果次数存在,就用count记录该次数。如果次数不存在,就不记录,只对count自增变成1
            if (value != null) {
                count = value;
            }
            count++;
            map.put(chs[i], count);
            /*
            if (value == null) {
                map.put(chs[i], 1);
            } else {
                value++;
                map.put(chs[i], value);
            }
            */
        }
        return toString(map);
    }

    /*
     * 将Map集合中的元素转成指定格式的字符串。a(2)b(1)d(3)...
     */
    private static String toString(Map<Character, Integer> map) {
        // 1,数据多,无论类型是什么,最终都要变成字符串,所以可以使用StringBuilder
        StringBuilder sb = new StringBuilder();
        // 2,遍历集合map。keySet
        Set<Character> keySet = map.keySet();
        for (Iterator<Character> it = keySet.iterator(); it.hasNext();) {
            Character key = it.next();
            Integer value = map.get(key);
            // 将键值存储到sb中
            sb.append(key + "(" + value + ")"); 
        }
        return sb.toString();
    }

}

结论:

问:什么时候使用Map集合呢?
答:当需求中出现映射关系时,应该最先想到Map集合。

map集合扩展知识——一对多关系

一对多的关系:一个学校有多个教室,每一个教室都有名称。一个教室有多个学生。假设学生属性:学号和姓名。

  • 学生未封装成对象时

    import java.util.*;
    
    class MapDemo { 
        public static void main(String[] args) {
            // 首先定义一个学校
            HashMap<String, HashMap<String, String>> czbk = new HashMap<String, HashMap<String, String>>(); 
            // 接着再定义两个教室,预热班和就业班
            HashMap<String, String> yure = new HashMap<String, String>();
            HashMap<String, String> jiuye = new HashMap<String, String>();
            // 学校和教室建立对应关系
            czbk.put("yureban", yure);
            czbk.put("jiuyeban", jiuye);
    
            // 教室里面装有学生
            yure.put("01", "zhangsan");
            yure.put("02", "lisi");
            jiuye.put("01", "zhaoliu");
            jiuye.put("02", "wangwu");
            // 遍历czbk集合,获取所有的教室
            Iterator<String> it = czbk.keySet().iterator();
            while(it.hasNext()) {
                String roomName = it.next();
                HashMap<String, String> room = czbk.get(roomName);
                System.out.println(roomName);
                getStudentInfo(room);
            }
        }
        public static void getStudentInfo(HashMap<String, String> roomMap) {
            Iterator<String> it = roomMap.keySet().iterator();
            while(it.hasNext()) {
                String id = it.next();
                String name = roomMap.get(id);
                System.out.println(id+"::"+name);
            }
        }
    }
  • 学生封装成对象,这个是经常用到的。将学生封装成对象:

    class Student {
        private String id;
        private String name;
    
        public Student(String id, String name) {
            this.id = id;
            this.name = name;
        }
    
        public String toString() {
            return id + ":::" + name;
        }
    }
    class MapDemo {
        public static void demo() {
            // 首先定义一个学校
            HashMap<String, List<Student>> czbk = new HashMap<String, List<Student>>();
            // 接着再定义两个教室,预热班和就业班,注意此时使用的是List集合
            List<Student> yure = new ArrayList<Student>();
            List<Student> jiuye = new ArrayList<Student>();
            // 学校和教室建立对应关系
            czbk.put("yureban", yure);
            czbk.put("jiuyeban", jiuye);
            // 教室里面装有学生
            yure.add(new Student("01", "zhangsan"));
            yure.add(new Student("04", "wangwu"));
            jiuye.add(new Student("01", "zhouqi"));
            jiuye.add(new Student("02", "zhaoliu"));
    
            Iterator<String> it = czbk.keySet().iterator();
            while(it.hasNext()) {
                String roomName = it.next();
                List<Student> room = czbk.get(roomName);
                System.out.println(roomName);
                getInfos(room);
            }
        }
        public static void getInfos(List<Student> list) {
            Iterator<Student> it = list.iterator();
            while(it.hasNext()) {
                Student s = it.next();
                System.out.println(s);
            }
        }
    
        public static void main(String[] args) {
            demo();
        }
    }

集合框架中的工具类

Collections

Collections类中定义的都是操作Collection的静态方法。

Collections常见方法

  • sort()

    • public static <T extends Comparable<? super T>> void sort(List<T> list):根据元素的自然顺序对指定列表按升序进行排序,列表中的所有元素都必须实现Comparable接口。

      public static void methodDemo1() {
          List<String> list = new ArrayList<String>();
      
          list.add("abce");
          list.add("z");
          list.add("hehe");
          list.add("nba");
      
          System.out.println(list);
          // 对list排序,自然排序使用的是元素的compareTo方法
          Collections.sort(list);
          System.out.println(list);
      }

      以上是使用Collections类中的sort()方法对List集合进行排序,而且使用的是自然排序。但现在我们想要按照字符串的长度进行排序,那又该如何做呢?这时需要用到下面的方法。

    • public static <T> void sort(List<T> list, Comparator<? super T> c):根据指定比较器产生的顺序对指定列表进行排序。此列表内的所有元素都必须可使用指定比较器相互比较。
      很显然,这时我们需要自定义一个比较器,使得比较是按照字符串的长度来排序的。

      public class ComparatorByLength implements Comparator<String> {
      
          @Override
          public int compare(String o1, String o2) {
              int temp = o1.length() - o2.length();
              return temp == 0 ? o1.compareTo(o2) : temp;
          }
      
      }

      测试方法为:

      public static void methodDemo1() {
          List<String> list = new ArrayList<String>();
      
          list.add("abce");
          list.add("z");
          list.add("hehe");
          list.add("nba");
      
          System.out.println(list);
          // 想按照长度排序
          Collections.sort(list, new ComparatorByLength());
          System.out.println(list);
      }
  • max()

    • public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll):根据元素的自然顺序,返回给定Collection的最大元素。Collection中的所有元素都必须实现Comparable接口。
      示例代码如下:

      public static void maxDemo() {
          List<String> list = new ArrayList<String>();
          list.add("abcd");
          list.add("aaa");
          list.add("zz");
          list.add("kkkkk");
          list.add("qq");
          list.add("z");
          Collections.sort(list); // 可以看出元素的自然顺序结果
          System.out.println(list);
          String max = Collections.max(list);
          System.out.println("max="+max);
      }

      但现在要让我们自己模拟一个获取集合最大值的功能,那又该怎么做呢?直接贴出代码如下:

      /**
       * 模拟一个获取集合最大值的功能
       */
      public static <T extends Object & Comparable<? super T>> T getMax(Collection<? extends T> coll) {
          Iterator<? extends T> it = coll.iterator();
          // 1,定义变量记录容器中的其中一个。
          T max = it.next();
      
          // 2,遍历容器所有的元素
          while (it.hasNext()) {
              T temp = it.next();
              // 3,在遍历的过程中进行比较,只要比变量中的值大,就用变量记录下来。
              if (temp.compareTo(max) > 0) {
                  max = temp;
              }
          }
          return max;
      }

      我们查看Java源代码,发现它写的和我们写的一模一样。

    • public static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp):根据指定比较器产生的顺序,返回给定Collection的最大元素。Collection中的所有元素都必须可通过指定比较器相互比较。
      首先,我们先自定义一个比较器——ComparatorByLength.java。

      public class ComparatorByLength implements Comparator<String> {
      
          @Override
          public int compare(String o1, String o2) {
              int temp = o1.length() - o2.length();
              return temp == 0 ? o1.compareTo(o2) : temp;
          }
      
      }

      我们的测试代码为:

      public class CollectionsDemo {
      
          public static void main(String[] args) {
              Collection<String> coll = new ArrayList<String>();
      
              coll.add("abcd");
              coll.add("aa");
              coll.add("z");
              coll.add("nba");
      
              String max = Collections.max(coll, new ComparatorByLength());
              System.out.println("max = " + max);
          }
      
      }
  • binarySearch()

    • public static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key):使用二分搜索法搜索指定列表,以获得指定对象。在进行此调用之前,必须根据列表元素的自然顺序对列表进行升序排序(通过sort(List)方法)。如果搜索键包含在列表中,则返回搜索键的索引;否则返回 (-(插入点) - 1)。
      示例代码如下:

      public static void binarySearchDemo() {
          List<String> list = new ArrayList<String>();
          list.add("abcd");
          list.add("aaa");
          list.add("zz");
          list.add("kkkkk");
          list.add("qq");
          list.add("z");
          Collections.sort(list);
          System.out.println(list);
          int index = Collections.binarySearch(list, "aaa"); 
          System.out.println("index="+index); // index=0
      }

      其内部原理是:

      public static int halfSearch(List<String> list, String key) {
          int max, min, mid;
          max = list.size() - 1;
          min = 0;
          while(min <= max) {
              mid = (max + min) >> 1; // /2
              String str = list.get(mid);
              int num = str.compareTo(key);
              if(num > 0)
                  max = mid - 1;
              else if(num < 0)
                  min = mid + 1;
              else 
                  return mid;
          }
          return -min-1;
      }

      现在我们调用halfSearch方法:

      public static void binarySearchDemo() {
          List<String> list = new ArrayList<String>();
          list.add("abcd");
          list.add("aaa");
          list.add("zz");
          list.add("kkkkk");
          list.add("qq");
          list.add("z");
          Collections.sort(list);
          System.out.println(list);
          int index = halfSearch(list, "aaaa"); 
          System.out.println("index="+index); // index=-2
      }
    • public static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c):使用二分搜索法搜索指定列表,以获得指定对象。在进行此调用之前,必须根据指定的比较器对列表进行升序排序(通过sort(List, Comparator) 方法)。
      首先,我们先自定义一个比较器——ComparatorByLength.java。

      public class ComparatorByLength implements Comparator<String> {
      
          @Override
          public int compare(String o1, String o2) {
              int temp = o1.length() - o2.length();
              return temp == 0 ? o1.compareTo(o2) : temp;
          }
      
      }

      然后编写我们的测试方法:

      public static void binarySearchDemo() {
          List<String> list = new ArrayList<String>();
          list.add("abcd");
          list.add("aaa");
          list.add("zz");
          list.add("kkkkk");
          list.add("qq");
          list.add("z");
          Collections.sort(list, new StrLenComparator());
          System.out.println(list);
          int index = Collections.binarySearch(list, "aaa", new StrLenComparator()); 
          System.out.println("index="+index); // index=3
      }

      其内部原理是:

      public static int halfSearch(List<String> list, String key, Comparator<String> cmp) {
          int max, min, mid;
          max = list.size() - 1;
          min = 0;
          while(min <= max) {
              mid = (max + min) >> 1; // /2
              String str = list.get(mid);
              int num = cmp.compare(str, key);
              if(num > 0)
                  max = mid - 1;
              else if(num < 0)
                  min = mid + 1;
              else 
                  return mid;
          }
          return -min-1;
      }

      现在来测试我们编写的方法,调用halfSearch方法的代码:

      public static void binarySearchDemo() {
          List<String> list = new ArrayList<String>();
          list.add("abcd");
          list.add("aaa");
          list.add("zz");
          list.add("kkkkk");
          list.add("qq");
          list.add("z");
          Collections.sort(list, new ComparatorByLength());
          System.out.println(list);
          int index = halfSearch(list, "cc", new ComparatorByLength());
          System.out.println("index="+index); // index=-2
      }
  • public static <T> void fill(List<? super T> list, T obj):使用指定元素替换指定列表中的所有元素。
    示例代码如下:

    public static void fillDemo() {
        List<String> list = new ArrayList<String>();
        list.add("abcd");
        list.add("aaa");
        list.add("zz");
        list.add("kkkkk");
        System.out.println(list);
        Collections.fill(list,"pp");
        System.out.println(list);
    }

    练习、复写fill(),将list集合中部分元素替换成指定元素。

    public static void fillDemo() {
        List<String> list = new ArrayList<String>();
        list.add("abcd");
        list.add("aaa");
        list.add("zz");
        list.add("kkkkk");
        System.out.println(list);
        fill(list, 1, 3, "hello");
        System.out.println(list);
    }
    public static void fill(List<String> list, int start, int end, String str) {
           for(int i = start; i < end; i++) {
               list.set(i, str);
           }
       }
  • public static <T> boolean replaceAll(List<T> list, T oldVal, T newVal):使用另一个值替换列表中出现的所有某一指定值。
    示例代码如下:

    public static void replaceAllDemo() {
        List<String> list = new ArrayList<String>();
        list.add("abcd");
        list.add("aaa");
        list.add("zz");
        list.add("kkkkk");
        System.out.println(list);
        Collections.replaceAll(list, "aaa", "pp");
        System.out.println(list);
    }
  • public static void reverse(List<?> list):反转指定列表中元素的顺序。
    示例代码如下:

    public static void replaceAllDemo() {
        List<String> list = new ArrayList<String>();
        list.add("abcd");
        list.add("aaa");
        list.add("zz");
        list.add("kkkkk");
        System.out.println(list);
        Collections.reverse(list);
        System.out.println(list);
    }
  • reverseOrder()

    • public static <T> Comparator<T> reverseOrder():返回一个比较器,它强行逆转实现了Comparable接口的对象Collection的自然顺序。
      我们首先按照字符串的自然顺序排序:

      public static void orderDemo() {
          TreeSet<String> ts = new TreeSet<String>();
          ts.add("abcde");
          ts.add("aaa");
          ts.add("k");
          ts.add("cc");
          Iterator it = ts.iterator();
          while(it.hasNext()) {
              System.out.println(it.next());
          }
      }

      接下来,我们要反转其自然排序:

      public static void orderDemo() {
          TreeSet<String> ts = new TreeSet<String>(Collections.reverseOrder());
          ts.add("abcde");
          ts.add("aaa");
          ts.add("k");
          ts.add("cc");
          Iterator it = ts.iterator();
          while(it.hasNext()) {
              System.out.println(it.next());
          }
      }

      当然了,我们可以使用比较器来做。首先我们定义一个反转字符串的自然顺序的比较器:

      public class StrComparator implements Comparator<String> {
          public int compare(String s1, String s2) {
              // 这样写太繁琐
              /*
              int num = s1.compareTo(s2);
              if(num > 0)
                  return -1;
              if(num < 0)
                  return 1;
              return num;
              */
              return s2.compareTo(s1); // 简写
          }
      }

      然后我们调用此比较器:

      public static void orderDemo() {
          TreeSet<String> ts = new TreeSet<String>(new StrComparator());
          ts.add("abcde");
          ts.add("aaa");
          ts.add("k");
          ts.add("cc");
          Iterator it = ts.iterator();
          while(it.hasNext()) {
              System.out.println(it.next());
          }
      }
    • public static <T> Comparator<T> reverseOrder(Comparator<T> cmp):返回一个比较器,它强行逆转指定比较器的顺序。
      我们首先定义一个按照字符串的长度排序的比较器:

      public class ComparatorByLength implements Comparator<String> {
      
          @Override
          public int compare(String o1, String o2) {
              int temp = o1.length() - o2.length();
              return temp == 0 ? o1.compareTo(o2) : temp;
          }
      
      }

      接着,我们按照字符串的长度排序:

      public static void orderDemo() {
          TreeSet<String> ts = new TreeSet<String>(new ComparatorByLength());
          ts.add("abcde");
          ts.add("aaa");
          ts.add("k");
          ts.add("cc");
          Iterator it = ts.iterator();
          while(it.hasNext()) {
              System.out.println(it.next());
          }
      }

      最后,我们反转其顺序:

      public static void orderDemo() {
          TreeSet<String> ts = new TreeSet<String>(Collections.reverseOrder(new StrLenComparator()));
          ts.add("abcde");
          ts.add("aaa");
          ts.add("k");
          ts.add("cc");
          Iterator it = ts.iterator();
          while(it.hasNext()) {
              System.out.println(it.next());
          }
      }
  • public static void shuffle(List<?> list):使用默认随机源对指定列表进行置换。所有置换发生的可能性都是大致相等的。
    示例代码为:

    public static void shuffleDemo() {
        List<String> list = new ArrayList<String>();
        list.add("abcd");
        list.add("aaa");
        list.add("zz");
        list.add("kkkkk");
        list.add("qq");
        list.add("z");
        System.out.println(list);
        Collections.shuffle(list);
        System.out.println(list);
    }
  • swap(List<?> list, int i, int j):在指定列表的指定位置处交换元素。(如果指定位置相同,则调用此方法不会更改列表。)
    示例代码:

    public static void sortDemo() {
        List<String> list = new ArrayList<String>();
        list.add("abcd");
        list.add("aaa");
        list.add("zz");
        list.add("kkkkk");
        list.add("qq");
        list.add("z");
        System.out.println(list);
        Collections.swap(list, 1, 2);
        System.out.println(list);
    }
  • Collections还有一个可以将非同步集合转成同步集合的方法
    • 同步集合 synchronized集合(非同步的集合)

Arrays

Arrays:用于操作数组的工具类。里面都是静态方法。

Arrays的2个常见方法

  • toString():返回指定数组内容的字符串表示形式。

    int[] arr = {2, 4, 5};
    System.out.println(Arrays.toString(arr));
  • public static <T> List<T> asList(T... a):返回一个受指定数组支持的固定大小的列表。

    String[] strs = {"abc", "haha", "nba", "zz"};
    List<String> list = Arrays.asList(strs);
    // list.add("qq"); // 会发生java.lang.UnsupportedOperationException,注意不能使用集合的增删方法,即不能改变长度。
    System.out.println(list.contains("nba"));

    那么问题就来了,把数组变成List集合有什么好处?
    答:可以使用集合的思想和方法来操作数组中的元素。但要注意:将数组变成集合,不可以使用集合的增删方法。因为数组的长度是固定的。可以使用集合的诸如contains()get()indexOf()subList()等等方法,如果你增删,那么会生成UnsupportedOperationException

    再看下面一段代码:

    int[] nums = {2, 4, 5};
    List li = Arrays.asList(nums);
    System.out.println(li);

    此时将会输出[[I@139a55]类似结果,以上代码没有使用泛型,使用泛型应是如此:

    int[] nums = {2, 4, 5};
    List<int[]> li = Arrays.asList(nums);
    System.out.println(li);

    如果代码修改为:

    Integer[] nums = {2,4,5};
    List<Integer> li = Arrays.asList(nums);
    System.out.println(li);

    此时输出[2, 4, 5]。所以,得出结论:如果数组中的元素都是引用数据类型,那么变成集合时,数组中的元素就直接转成集合中的元素。如果数组中的元素都是基本数据类型,那么会将该数组([I@139a55)对象作为集合中的元素存在。

集合变数组:Collections接口中的toArray()方法

例,Collections接口中的toArray()方法的应用。

import java.util.*;

class CollectionToArray {
    public static void main(String[] args) {
        ArrayList<String> al = new ArrayList<String>();
        al.add("abc1");
        al.add("abc2");
        al.add("abc3");
        String[] arr = al.toArray(new String[al.size()]);
        System.out.println(Arrays.toString(arr));
    }
}
  1. 指定类型的数组到底要定义多长呢?
    当指定类型的数组长度小于了集合的size,那么该方法内部会创建一个新的数组,长度为集合的size。当指定类型的数组长度大于了集合的size,那么就不会新创建数组,而是使用传递进来的数组。所以创建一个刚刚好的数组最优。
  2. 为什么要将集合变数组?
    为了限定对元素的操作,不需要进行增删了。

JDK1.5新特性

高级for循环

高级for循环的作用:用于遍历Collection集合或数组。
格式:

for(数据类型(一般是泛型类型) 变量名 : 被遍历的集合(Collection)或者数组) {

}

例,遍历集合。

ArrayList<String> al = new ArrayList<String>();
al.add("abc1");
al.add("abc2");
al.add("abc3");

之前我们使用迭代器遍历:

Iterator<String> it = al.iterator();
while(it.hasNext()) {
    System.out.println(it.next());
}

现在我们可使用高级For循环了,主要就是为了简化书写。

for(String s : al) {
    System.out.println(s);
}

结论:

高级for循环对集合进行遍历,只能获取集合中的元素,但是不能对集合进行操作。迭代器除了遍历,还可以进行remove()集合中元素的动作,如果使用ListIterator,还可以在遍历过程中对集合进行增删改查的操作。

对于数组int[] arr = {3,5,1};,我们可使用传统for循环:

for (int x = 0; x < arr.length; x++) {
    System.out.println(arr[x]);
}

还可使用高级for循环:

for (int i : arr) {
    System.out.println("i:"+i);
}

问:传统for和高级for有什么区别呢?
答:高级for有一个局限性,必须有被遍历的目标,而该目标只能是Collection或数组。
建议:在遍历数组的时候还是希望使用传统for,因为传统for可以定义角标。

对于Map集合,我们也可以使用高级for循环。

HashMap<Integer, String> hm = new HashMap<Integer, String>();
hm.put(1, "a");
hm.put(2, "b");
hm.put(3, "c");

第一种遍历方式:

Set<Integer> keySet = hm.keySet();
for (Integer i : keySet) {
    System.out.println(i+"::"+hm.get(i));
}

第二种遍历方式:

for (Map.Entry<Integer, String> me : hm.entrySet()) {
    System.out.println(me.getKey()+"---"+me.getValue());
}

可变参数

JDK1.5版本出现的新特性。可变参数其实就是一种数组参数的简写形式,不用每一次都手动的建立数组对象,只要将要操作的元素作为参数传递即可,隐式地将这些参数封装成了数组。

class ParamMethodDemo {
    public static void main(String[] args) {
        show("gaga",2,3,4,5,6);
    }
    public static void show(String str, int... arr) {
        System.out.println(arr.length);
    }
}

注意:方法的可变参数在使用时,可变参数一定要定义在参数列表的最后面。

静态导入

例,

import java.util.*;
import static java.util.Arrays.*; // 导入的是Arrays类中的所有静态成员。
import static java.lang.System.*; // 导入了System类中所有静态成员。

class StaticImport extends Object {
    public static void main(String[] args) {
        out.println("hello");
        int[] arr = {3,1,5};
        sort(arr);
        int index = binarySearch(arr, 1);
        System.out.println(Arrays.toString(arr)); // 此时不能省略Arrays,方法重名时,指定具备所属的对象或类。
        System.out.println("index="+index);
    }
}

注意:

  1. 当类名重名时,需要指定具体的包名。
  2. 当方法重名时,指定具备所属的对象或类。
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
回答: Java集合框架Map是一种键值对的数据结构,可以存储一组具有唯一键和对应值的元素。使用泛型可以在编译时进行类型检查,确保集合只能存储指定类型的元素,提高代码的可读性和安全性。 在引用的示例代码,List<String>定义了一个只能存储字符串类型元素的列表。在引用的示例代码,Collection<String>和Collection<Integer>分别定义了只能存储字符串类型和整数类型元素的集合。使用泛型通配符可以增加集合的灵活性。比如在coll.removeAll(c)方法,传入的Collection对象的泛型可以是任意类型。另外,泛型还可以应用于Map集合,如引用的示例代码,Set<Map.Entry<String,String>>定义了只能存储键值对类型为String的元素的集合。 综上所述,Java集合框架Map和泛型可以一起使用,通过泛型可以指定集合存储的元素类型,增加代码的可读性和类型安全性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [java基础的--------Map+泛型](https://blog.csdn.net/liutaiwu/article/details/107915445)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李阿昀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值