集合

原文:https://blog.csdn.net/u012184326/article/details/80201954 

前言

    java的集合机制,本质上是一个数据容器,像之前学过的数组,字符串都是一种数据容器。但是集合它提供的功能更加强大,便捷。里面提供的方法的底层源代码采用的也是优秀的效率算法。其他数据容器能操作的,集合都能操作。而且代码更加简洁,思路更加清晰,运行的效率更加高。因此,完全掌握完集合。编程的技能会进一步提高。

    已经对集合的神奇之处迷恋到无法自拔。学完集合,才发觉我以前的代码都是垃圾的说。

一 集合

首先放一张图给出集合的层次体系结构:

     Java中的集合层次结构分为单列集合(Collection)和双列集合(Map)。单列和双列的直接理解就是,集合的每个项能存储多少个数据。Collection以及它的子类在每个项中能存储一个数据,因此是单列集合。Map以及它的子类一次性能存储两个数据,键和值,因此是双列集合。接下来逐个分析这两个体系。

一、Collection 体系

    1.特点:

     Collection是一个怎么样的集合呢?与Map相比,最大的特点是它是个单列集合。但我们不能说Collection是有序的,至少它的Set(散列)是一个无序的。注意了,我这里的有序不是指从小到大的顺序排列,而是指集合存进去和取出来是否一样,能保证一样就是有序,不能确保一样就是无序。我们也不能说它是由索引的。因为它的子类List由索引,而set没有索引。所以这里对       Collection的特征描述就是,

   一个具有迭代器,存储单列数据的顶层接口。

    在这里我要再提一个多态情况:父类的引用指向子类的对象。后面会经常出现这样的情况:   

 Collection c = new ArrayList<String>();
    
2.常用方法:    

boolean add(E e)  :添加一个元素到末尾
 
boolean removeAll(Collection A)  移除此 collection A中那些也包含在指定collection 中的所有元素
boolean contains(Object o)   判断是否包含某个元素
iterator() :返回迭代器
boolean remove(Object o)   如果存在元素则移除成功
Object[]  toArray()  :将元素数组化
int size()  返回此 collection 中的元素数。 
boolean isEmpty()  是否为空

3.应用:

   上面的每个方法都是较为常用的。例如:     

boolean add(E e) 添加元素到末尾,注意了。Collection没有添加指定索引的元素。这个是它子类List的独特方法      
boolean contains(Object o) 包含某个元素 这个必须好用,不需要再用for循环来遍历集合,会使代码简便很多
Object[]  toArray()  :将元素数组化  将集合返回里面的元素返回一个对象数组,是一种便捷的遍历方式和获取数据方式

4.遍历方法:

1.利用方法toArray(),可以把集合转换成数组,然后遍历数组即可 

2.使用迭代器Iterator():返回可以进行迭代器进行迭代的方法

Collection不能用for循环遍历,因为它还不是一个有序的容器,要想使用for循环,只能用toArray()方法,先转换成一个有序的数组,再用for循环遍历

  后面会讲到,for循环分为两种,基于索引值的称为普通for循环;基于底层实现为迭代器的称为增强for循环(也叫foreach);

 另外,在选择是用迭代器遍历还是普通for循环遍历。有个判断方法是看集合本身有没有索引值。Iterator能够遍历Collection所有集合。普通for循环只能遍历有序有索引的List体系的集合。Set体系无序的集合不能遍历。

5.总结:

     毕竟Collection是顶层接口,重点掌握它的应用:

    contain()方法,是否包含

    toArray() 转为数组

    Iterator() 迭代器

案例1:

/*
 * 第一题:分析以下需求,并用代码实现
    1.生成10个1至100之间的随机整数(不能重复),存入一个List集合
    2.然后利用迭代器和增强for循环分别遍历集合元素并输出
    3.如:15 18 20 40 46 60 65 70 75 91(可以无序)
 */
public class Test01 {
    public static void main(String[] args) {
        Random random = new Random();
        List<Integer> list = new ArrayList<>();
        int count = 0;
        while (count < 10) {
            int num = random.nextInt(100) + 1;
            int i = 0;
            for (; i < list.size(); i++) {
                if (list.get(i) == num) {
                    break;
                }
            }
            if (i == list.size()) {
                list.add(num);
                count++;
            }
 
        }
        Iterator<Integer> it = list.iterator();
        System.out.println("迭代器输出:");
        while (it.hasNext()) {
            System.out.println(it.next());
        }
        System.out.println("--------------");
        System.out.println("增强for循环输出:");
        for (int num : list) {
            System.out.println(num);
        }
        // System.out.println(list);
    }
        //使用list.contain()方法做出的效果
    public void getNoRepeatRandomNums(ArrayList<Integer> list) {
        Random r = new Random();
        while(list.size()<10)
        {
            int num = r.nextInt(100) + 1;
            if(!list.contains(num))
                list.add(num);
        }
        
    }
}
   上面的案例是我原先没注意到顶层接口Collection 有个contain方法,一直都是用for循环来解决,结果是正确的。但是算法的性能是差的,还有大量for 循环,代码是不美观的。这不是一个程序员的修养。
   

  二、Collection子类一:List集合

     1.特点List继承Collection的所有方法,但是独有地形成了一个有序,有索引,元素可以重复的集合体系。 

     2.独特方法(Collection的方法,List集合同样具备):        

void add(int index, E element)  在列表的指定位置插入指定元素。因为允许重复,因此会一定添加成功
E get(int index)  根据索引值获取元素
remove(int position) 根据索引值删除元素
     3.在这里List集合与父类相比,能够根据索引值来修改数据结构,和获取数据方法

     这里注意一点:add()方法总是能执行成功,因为List本身的特性是允许重复。所以总是能添加成功

     4.相比下面要讲的子类结构,List缺少一个修改方法set,所以不再多做延伸。直接看它的最终完善子类ArrayList和LinkedList

三、List子类一:ArrayList数组集合

     1,特点:ArrayList子类算是有序集合的最后子类了。里面提供了完整的获取,判断,增加,删除,修改,查询等功能。

     2.常用方法():

Object clone()  返回此 ArrayList 实例的浅表副本。 
indexOf(Object o)   返回此列表中首次出现的指定元素的索引,或如果此列表不包含元素,则返回 -1。
int lastIndexOf(Object o)  返回此列表中最后一次出现的指定元素的索引,或如果此列表不包含索引,则返回 -1。 
E remove(int index)  根据索引移除此列表中指定位置上的元素。并返回被移除的元素
boolean remove(Object o)  移除此列表中首次出现的指定元素(如果存在)。 
E set(int index, E element)    用指定的元素替代此列表中指定位置上的元素。 
void removeRange(int fromIndex, int toIndex)  移除列表中索引在 fromIndex(包括)和 toIndex(不包括)之间的所有元素 
     3.4种遍历方式:

      1).利用toArray()方法转换成对象数组Object[]。进行普通for循环遍历

      2).根据有索引的特性,利用get()和size()方法,直接用普通for循环遍历(适用于获取,查询,删除,修改等情况)

      3).用增强for循环进行遍历(只适用于获取元素;因为底层代码是迭代器实现,不能用来做删除和增加遍历)

          增强for由于底层也是使用的迭代器,无法在遍历的时候增删元素 

      4).用迭代器Iterator遍历(适用情况同增强for循环。)

      4.特点:查询快,增删慢(连续空间存储的数据结构共有的特点)   

      5.应用:

         ArryaList是个灵活强大的数组容器。相比普通的数组容器,它的size长度是灵活可变的。这样有了以下的使用特点,也是比较值得注意的:

         (1) 增删:在利用ArrayList的add()方法做添加元素的时候,当你在指定位置插入(添加)一个元素时,其后面的元素会自动向后移动一个单位空间。所以有时你用for(int i = 0;i<arrayList.size();i++)遍历,匹配到一个插入点A时,成功插入B,数组的结构由原来的AC变成了ABC 。这时候,如果想要下次遍历到C而不是B的话。在add()后面要补上一个i++;这样就不会做到重复遍历了。

      相同原理下,在remove()删除元素的时候,假设原来的数据结构为ABC。在遍历到删除点A,在删除了A以后,BC是自动向前移动了一个单位,B代替了A的位置。此时,你也应该在remove()下一句代码补上一个i--。这样子才不会错过B的遍历。

         以上分析,可以看出在插入和删除的情况下,数组型的集合发生了大规模的改动,在空间上的性能,比用链表做成的LinkedList是差了很多。

          (2)优点是ArrayList是个允许重复元素的集合。所以大部分情况下用起来是很顺手的。 

 

四、List子类二:LinkedList链表集合     

 1.理解 LinkedList集合的底层实现是一个链表的数据结构,但是它同样也是一个有序集合,也有索引值。上面ArrayList的遍历情况对于LinkedList同样试用。根据链表结构这个空间不连续存储的特点:增删快,查询慢。是和ArrayList的最大区别。所以在使用大量修改,增加,删除数据的时候,用LinkedList最为方便。一般情况下,还是想用ArrayList。比较熟悉吧。大数据大规模修改的也不是我们这种初学者所能经常接触的啦。

顺便理解一下链表这种数据结构:是通过结点来链接组成的一种数据结构,每个结点存储地址值,值,下一个节点地址值这三个。这样形成每个结点都和前后结点有互相关系。当你插入一个结点的时候,只需要改动两个结点:告诉前结点,你的下一个结点现在是我了,告诉插入的结点,你的下一个结点是被你占了位置的结点。三角关系自己处理。其他结点根本不晓得发生啥情况。

 

    LinkedList因为有一些特有的方法。可以用来模仿另外两个数据结构:栈,队列

     (1)栈的特点:先进后出。看两个现实案例:

        1).弹夹:往弹夹子弹里装子弹。当子弹全部装满的时候。第一个子弹想要发射,就必须等待前面的子弹都打光。
最后一个装进去的子弹,是打出去的第一发。
        2).小巷:在一个很窄的小巷里面,最里面是一堵墙。前天你第一个往这个小巷停车。今天想把车开出去。必须要等前天进来的车都开出去了,才有出口。
因此,栈的本质特点是一个出入口相同并且只有一个的容器。最先进去的排在最后面出来。最后出去的总能第一个出来

   (2)队列的特点:先进先出:

       当一个元素添加进来的时候,前面的元素都会自动从当前位置向出口方向移动一个单位。

 2.独特方法:

        void addFirst(E e)   插入第一个
        void addLast(E e)   插入最后一个
        E getFirst()             获得第一个
        E getLast()             获得最后一个
        E removeFirst()      移除第一个

        E removeLast()       移除最后一个

上面的独特方法阿。用得少,目前所知是用来模仿栈和队列是好用的。

综上,用自己的理解完成了对有序集合的总结。

接下来甩出几个案例:

案例2:

/*
 * 模仿一种纸牌规则像向每个玩家随机发两张牌,玩家两张牌的总点数达到最大时。就获得胜利
 */
public class CollectonsTest {
    public static void main(String[] args) {
        ArrayList<Integer> cardsList = new ArrayList<>();
        ArrayList<Player> playerList = new ArrayList<>();
 
        // 添加牌组到卡牌列表中
        for (int i = 0; i < 13; i++) {
            for (int j = 0; j < 4; j++)
                cardsList.add(i + 1);
        }
 
        // 创建玩家,存进玩家列表
        for (int i = 0; i < 4; i++) {
            Player p = new Player(new int[] {}, "玩家" + (i + 1));
            playerList.add(p);
        }
        // 洗牌:工具类Collections的shuffle(List list)方法:随机改变一次集合的顺序(打乱)
        Collections.shuffle(cardsList);
        // System.out.println(cardsList);
        // 开始发牌:每人发两张。从列表中去掉发出的牌
        for (int i = 0; i < playerList.size(); i++) {
            Player p = playerList.get(i);
            int cards[] = new int[2];
            for (int j = 0; j < 2; j++) {
                // 实际场景是总会发第一张牌。remove方法会返回被删除的元素。然后删除,后面的元素
                // 自动向前靠前1位
                cards[j] = cardsList.remove(0);
            }
            p.setCard(cards);
        }
 
        // 发牌完毕,开始比较每个玩家的牌面
        System.out.println("玩家名称" + "\t" + "牌面" + "\t" + "总和");
        int max = 0;
        for (int i = 0; i < playerList.size(); i++) {
            Player player = playerList.get(i);
            String name = player.getName();
            int cards[] = player.getCard();
            int sum = cards[0] + cards[1];
 
            max = Math.max(max, sum);
 
            // 打印信息
            System.out.print(name + "\t" + "(" + cards[0] + "," + cards[1] + ")" + "\t" + sum);
            System.out.println();
        }
 
        System.out.println("最大点数:" + max);
        System.out.println("剩余牌组情况:");
        System.out.println("还剩" + cardsList.size() + "张牌:");
        for (int i = 0; i < cardsList.size(); i++) {
            if (i % 4 == 0)
                System.out.println();
            System.out.print(cardsList.get(i) + "\t");
        }
    }
}
 
class Player {
    String name;
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public Player(String name) {
        super();
        this.name = name;
    }
 
    int[] cards;
 
    public int[] getCard() {
        return cards;
    }
 
    public void setCard(int[] card) {
        this.cards = card;
    }
 
    public Player(int[] card, String name) {
        super();
        this.cards = card;
        this.name = name;
    }
 
    public Player() {
        super();
    }
 
}
 
/*
 *输出结果: 玩家名称 牌面 总和 玩家1 (3,13) 16 玩家2 (9,7) 16 玩家3 (2,10) 12 玩家4 (13,12) 25 最大点数:25
 */
案例3:

/*
 * 第三题:分析以下需求,并用代码实现
    1.有如下代码:
        public static void main(String[] args) {
            List<String> list = new ArrayList<>();
            list.add("a");
            list.add("a");
            list.add("c");
            list.add("c");
            list.add("a");
            list.add("d");
            
            noRepeat(list);
            System.out.println(list);
        }
    2.定义一个noRepeat()方法,要求对传递过来集合中进行元素去重
        public static void noRepeat(List<String> list){
        }
 */
public class Test03 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("a");
        list.add("a");
        list.add("c");
        list.add("c");
        list.add("a");
        list.add("d");
 
        setNoRepeatList(list);
        System.out.println(list);
    }
 
    //垃圾写法
    public static void noRepeat(List<String> list) {
        for (int i = 0; i < list.size(); i++) {
            String iStr = list.get(i);
            for (int j = i + 1; j < list.size(); j++) {
                if (iStr == list.get(j)) {
                    list.remove(j);
                    j--;
                }
            }
        }
    }
 
    //高级写法
    
    public static void setNoRepeatList(List<String> srcList) {
        ArrayList<String> list = new ArrayList<>();
 
        for (int i = 0; i < srcList.size(); i++) {
            if (!list.contains(srcList.get(i)))
                list.add(srcList.get(i));
        }
        srcList.clear();
        srcList.addAll(list);
    }
}
案例3出现了垃圾写法和高级写法。再次提到了善于综合运用集合方法,来使自己的代码达到最简洁程度

有序集合体系总结完毕

五、Collection子类二:Set散列集合

       1.特点:无序,无索引,元素唯一的接口。

      Set是单列集合Collection下的无序,无索引,元素不重复的集合接口。可以说与List接口完全相反。在这里要提到set接口如何做到不重复的根本原因的话,是一个叫做哈希值的东东:hashCode。以我现在的程度理解,hashCode由所有java类的根接口Object开始就存在的,代表了所有对象的地址值。。set接口在底层代码实现是根据一个算法算出存入的每个数据的hash值。每个存入的hash值都不一样。然后调用equals方法比较数据之间的hash值。也就是比较对象的地址值而不是内容值

       set接口无序无索引的情景,就像是一锅粥,往里面扔东西。存进去遍历,与取出遍历的结果不保证一致。、

      要完全掌握Set接口,最重要理解底层实现hashCode 代表对象的地址值。和equals()方法比较对象的地址值而不是内容值

         2.常用方法:能使用Collection的所有方法

            添加元素add()做了重写。保证添加不重复

         3.应用场景:

            元素去重;根据boolean add(E e)判断是否添加成功。同样的元素添加进去会返回false;

六、hashSet

       HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持。它不保证set 的迭代顺序;特别是它不保证该顺序恒久不变,此类允许使用null元素。 

        运用HashSet要掌握它的无序,无索引,元素不重复的特点。在默认情况下,当存入hashSet的类型是自己自定义的对象,相同内容是不会被视作为相等的。因为默认的情况下。hashcode对于两个对象的地址值表示是不想等的。euqals通过对比对象之间的hashCode是否相等来判断是否重复

      要想让相同内容值的对象相等,要重写自定义对象中的hashCode()方法和equals方法。在Eclipse中的快捷键是shift+alt+s+h自动生成。 

       要是不理解。请查看hasSet的底层源代码。

案例1 :输入一串字符串,对该字符串进行去重

public class Test02 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("输入一行字符串:");
        String lines = sc.nextLine();
        HashSet<Character> set = new HashSet<>();
        for (int i = 0; i < lines.length(); i++) {
            Character ch = lines.charAt(i);
            set.add(ch);
        }
 
        for (Character ch : set)
            System.out.println(ch);
    }
}
案例2:重写对象的hashcode()和equals方法,来判定两个内容值相等的对象不重复。

import java.util.HashSet;
 
public class HashSetDemo2 {
    public static void main(String[] args) {
        // 集合使用方法
        // -创建集合对象
        HashSet<Student> hs = new HashSet<>();
        // -创建元素对象
        Student s1 = new Student("zhangsan", 18);
        Student s2 = new Student("lisi", 20);
        Student s3 = new Student("lisi", 20);
//        hs.add(new Student("lisi",22));
//        hs.add(new Student("wangwu",28));
        // -添加元素到集合
        hs.add(s1);
        hs.add(s2);
        hs.add(s3);// s2和s3是在内容上是相等的。因为对象本身已经重写hashCode()和equals()方法。所以能添加成功
 
        // -遍历集合
        for (Student s : hs) {
            System.out.println(s);
        }
    }
}
 
class Student {
    String name;
    int age;
 
    public Student() {
        super();
        // TODO Auto-generated constructor stub
    }
 
    public Student(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
 
    public int getAge() {
        return age;
    }
 
    public String getName() {
        return name;
    }
 
    public void setAge(String name) {
        this.name = name;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
 
    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + "]";
    }
 
    @Override
    public boolean equals(Object obj) {
        System.out.println("==========");
        Student s = (Student) obj;// 向下转型,可以获取子类特有的成员
 
        if (s.getAge() != this.getAge())//比较年龄,如果不相等则返回false;
            return false;
        if (s.getName() != this.getName())//比较姓名,如果不相等则返回false;
            return false;
        
        return true;
        // obj = (Student) obj;
    }
    @Override
    public int hashCode() {
        // TODO Auto-generated method stub
        return age+name.hashCode();
    }
}

七,集合的迭代器Iterator

    前面分析,我们直到凡是属于Collection体系下的子类都具有迭代器的功能。从List->ArrayList、LinkedList有序有索引元素可重复的集合,Set->hashSet无序无索引元素不唯一的集合,都可以使用迭代器来遍历。这里要注意三点情况

1.迭代器相当于集合的一个副本,要是在迭代器遍历的时候,集合发生了增删的情况。迭代器会引发并发异常,终止程序运行。唯一的解决办法是避免集合自己去增删,而让迭代器做增删功能。

2.增强for循环的底层是由迭代器实现的。因此,也要遵循迭代器的规则。在遍历获取元素或者修改元素值的时候好用。增删元素不好用。

3.普通for循环是通过索引值来遍历的。有索引值集合可以遍历。没有索引值的集合无法遍历。但是迭代器能够遍历有索引值,和没有索引值的集合

八、map

    map是双列集合的顶层接口。存储的特点是"key-value"(K-V泛型)两个数据。也称为"键-值"对。键的值是唯一的。有重复的键添加到map时。会覆盖前面的键值。

    1.常用方法:

      1).boolean containsKey(K key):查找是否包含关键值 key。是就返回true,否则就返回false;

      2).boolean containsValue(V value):查找是否有包含一个值,是就返回true,否则返回false;

      3).V get(K key):通过关键字key获取与它对应的值

      4).V put(K key,V value) 插入一组"键-值"对应的数据项。如果键已经存在,将返回被覆盖的值。特别注意的是,在map中,键是唯一的,但是当插入同样的键,也可以插入成功。能够覆盖前面的值。下面将印证这一个覆盖的结论。

案例1:印证HashMap的put()方法:

public class MapPutMethodTest {
    public static void main(String[] args) {
        HashMap<Integer, String>  hashMap = new HashMap<>();
        
        String s1 = hashMap.put(1,"我是1号");
        String s2 = hashMap.put(1, "我来覆盖1号");
        System.out.println("s1="+s1);
                System.out.println("s2="+s2);
    }
}
 
 
 
//输出结果
/*
s1=null
s2=我是1号
*/
上面第一次插入(1,"我是1号")  s1的结果是null,原因是1在map中没有找到重复,是唯一的。没有替代任何值。因此s1 = null;

第二次插入(1,"我来覆盖1号"); s2 的结果是“我是1号”,原因是1在map是已经存在的。再插入关键字 1 就返回了被替代的值"我是1号";

    5).remove(K key);根据关键字Key 删除它的数据

    6).Set(K) keySet(); 返回不重复的关键字Set集合。应用于遍历

    7).Set(Map.Entry(K,V)) entrySet():返回对象是map内部类的Entry实体 的Set集合,Entry包含了键和值。应用于遍历

  2.遍历方法:

     Map本身不具备迭代器。但是与具有迭代器的Set集合有方法关联。因此遍历Map的方法是,获取Map的Set集合,然后再用Set集合的迭代器遍历。

这里有两个方法获取map的Set集合:

    1).Set<K> keySet()方法

    2).Set(Map.Entry(K,V) 方法

通过案例来说明两种方法遍Map:

案例1:Set<K> keySet()

public class MapDemo4 {
 
    public static void main(String[] args) {
        Map<String,String> map = new HashMap<>();
        map.put("谢霆锋", "张柏芝");
        map.put("陈冠希", "钟欣桐");
        map.put("李亚鹏", "王菲");
        
        //获取所有的键值
        Set<String> keys = map.keySet();
        
        for(String key:keys)
        {
            String value = map.get(key);
            System.out.println("键:"+key +"---"+"值:"+value);
        }
    }
    /*
     *输出结果:
     * 
     * 键:谢霆锋---值:张柏芝
       键:陈冠希---值:钟欣桐
       键:李亚鹏---值:王菲
     */
}
案例2:Set(Map.Entry(K,V)

    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        
        map.put("杨过", "小龙女");
        map.put("张无忌", "周芷若");
        map.put("段誉", "王语嫣");
        
        //获取Entry对象
        Set<Map.Entry<String, String>> entrys = map.entrySet();
        
        for(Map.Entry<String, String> entry:entrys)
        {
            String hunsband = entry.getKey();
            String wife = entry.getValue();
            System.out.println(hunsband+"-----"+wife);
        }
    }
    /*输出结果:
     * 
     * 杨过-----小龙女
       段誉-----王语嫣
           张无忌-----周芷若
     */
 Entry实体是Map的内部类,有个很形象的证明是像是一张结婚证,通过key关键字是丈夫,找到value值是老婆。

     3.应用:

       1)统计类算法使用Map的集合插入覆盖特点,能够达到性能最优。

        举个例子:从字符串中"Hello,my name is Simons ,I Love Java"中统计各个字符出现的次数。

      普通算法:遍历字符串,取出每个字符。与前面的字符作比较,有出现重复就跳过该字符。没有出现重复就与后面的字符作比较,出现重复就加1。理论上算法的复杂度为(m*n);

      Map插入算法:以字符为键,统计次数为值 作为数据建立Map。遍历该字符串。对于每个字符,使用put方法插入。如果不重复。插入这个值。如果重复,也插入该数据,只是统计次数在原先字符的基础上+1;理论上的算法复杂度为(m);

/*
 * 第三题:分析以下需求,并用代码实现    
    1.利用键盘录入,输入一个字符串
    2.统计该字符串中各个字符的数量(提示:字符不用排序)
    3.如:
    用户输入字符串"wuyanzu-he^wo~shuo!ta@zui$shuai??"
        程序输出结果:
        !=1
        e=1
        @=1
        $=1
        a=3
        n=1
        o=2
        h=3
        i=2
        -=1
        w=2
        u=5
        t=1
        s=2
        ~=1
        ^=1
        ?=2
        z=2
        y=1
 */
public class Test03 {
    public static void main(String[] args) {
        //method1();
 
        method2();
    }
 
    //牛逼算法
    private static void method2() {
        Scanner scanner = new Scanner(System.in);
        String lines = scanner.nextLine();
        char[] chs = lines.toCharArray();
        Map<Character, Integer> map = new HashMap<>();
 
        for (Character ch : chs) {
            if (!map.containsKey(ch)) {
                map.put(ch, 1);
            } else {
                map.put(ch, map.get(ch)+1);
            }
        }
    }
    
    //垃圾算法
    private static void method1() {
        Scanner sc = new Scanner(System.in);
        String strs = sc.nextLine();
        HashSet<Character> set = new HashSet<>();
        for (int i = 0; i < strs.length(); i++)
            set.add(strs.charAt(i));
        HashMap<Character, Integer> hashMap = new HashMap<>();
 
        for (Character ch : set) {
            int count = 0;
            for (int i = 0; i < strs.length(); i++) {
                if (ch == strs.charAt(i))
                    count++;
            }
            hashMap.put(ch, count);
        }
 
        // 遍历map
        for (char key : hashMap.keySet()) {
            int value = hashMap.get(key);
            System.out.println(key + ":" + value);
        }
    }
}
    这个案例算法1:利用set先对集合去重。再遍历Set集合,每个元素与原先的重复数据集合比较。最后将统计结果放入Map中。过程复杂,代码复杂。差评

   算法2:直接用map来插入。性能好,代码简单

/*
 * 键盘录入一个字符串,分别统计出其中英文字母、空格、数字和其它字符的数量,输出结果:”
其他=1,     空格=2,     字母=12,     数字=6”
 */
public class Test03 {
    public static void main(String[] args) {
        System.out.println("请输入一个字符串:");
        Scanner sc = new Scanner(System.in);
        String line = sc.nextLine();
        System.out.println("line的长度:" + line.length());
        HashMap<String, Integer> map = new HashMap<>();
        String chTypeStr = "";
 
        for (int i = 0; i < line.length(); i++) {
            char ch = line.charAt(i);
            if (ch == ' ')
                chTypeStr = "空格";
            else if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))
                chTypeStr = "字母";
            else if (ch >= '0' && ch <= '9')
                chTypeStr = "数字";
            else
                chTypeStr = "其他";
 
            if (!map.containsKey(chTypeStr))
                map.put(chTypeStr, 1);
            else
                map.put(chTypeStr, map.get(chTypeStr) + 1);
        }
 
        int sumCount = 0;
 
        for (String typeKey : map.keySet()) {
            int typeCount = map.get(typeKey);
            System.out.println(typeKey + "=" + typeCount);
            sumCount += typeCount;
        }
        System.out.println("总和:" + sumCount);
    }
}
/*
 * 输出结果:
 * 
 * 请输入一个字符串:
   SDJKSAHLIAHSKJ!@@#!@1512451   33746ddd
   line的长度:38
   其他=6
   数字=12
   字母=17
   空格=3
   总和:38
 */
Map在这里先总结完毕。用最常用的HashMap来举例Map在程序中的优越使用情况。

九、集合工具类Collections 的使用

    注意了,Collections多了一个s,是一个工具类,像之前使用过的Arrays,Math工具类一样,用来操作集合。关于工具类的制作方法,首先是构造方法私有化,防止被创造对象。接着是成员(包括成员变量和成员方法)添加静态,可以直接使用"类名.成员"的格式调用。

    1.Collections用来操作集合。具体有以下常用方法:

       1).void sort(List<T> list):根据集合中的自然元素排序(从小到大);

       2).int binarySearch(List<T> list,T key):在排序完成的前提上, 用二分查找的算法在集合list中,根据关键字key查找它的索引值。

       3).void reserve(List<T> list):   反转集合的元素

       4).void shuffle(List<T> list):    打乱一次集合的元素原有的顺序。像洗牌一样的道理

       5).void swap(List<T> list,int i,int j):     在指定的集合中,交换两个元素的位置

       6).void copy(List<T> dest,List<T> src) 复制集合。
--------------------- 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值