栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

JavaSE进阶04-集合

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JavaSE进阶04-集合

主要集合概述

集合实际上就是一个容器,可以容纳其它类型的数据。集合不能存储基本数据类型,也不能存储Java对象,集合当中存储的都是Java对象的内存地址。在Java中每一个不同的集合,底层会对应不同的数据结构。往不同的集合中存储元素,等于将数据放到了不同的数据结构当中。
Java集合主要有3中重要的类型:

  • List:是一个有序集合,可以放重复的数据
  • Set:是一个无序集合,不允许放重复的数据
  • Map:是一个无序集合,包含一个键值对,键对象不允许重复,值对象可以重复

在java中集合分为两大类:
一类是单个方式存储元素:
单个方式存储元素,这一类集合中超级父接口:java.util.Collection;
一类是以键值对儿的方式存储元素
以键值对的方式存储元素,这一类集合中超级父接口:java.util.Map;

集合这块最主要掌握的内容

  1. 每个集合对象的创建(new)
  2. 向集合中添加元素
  3. 从集合中取出某个元素
  4. 遍历集合
  5. 主要的集合类:ArrayList、LinkedList、HashSet (HashMap的key,存储在HashMap集合key的元素需要同时重写hashCode + equals)、TreeSet、HashMap、Properties、TreeMap
Collection和Iterator

Conllection是List和Set的父接口,在Collection中定义了一些主要方法:

booleanadd() 确保此collection包含指定的元素
booleanaddAll() 将指定collection中的所有元素都添加到此collection中
voidclear() 移除此collection中的所有元素
booleancontains() 如果此collection包含指定元素,则返回true
booleancontainAll() 如果此collection包含指定collection中的所有元素,则返回true
booleanequals() 比较此collection与指定对象是否相等
inthashCode() 返回此collection的哈希码值
booleanisEmpty() 返回此collection是否为空
Iteratoriterator() 返回在此collection的元素上进行迭代的迭代器
booleanremove() 从此collection中移除指定元素的单个实例
booleanremoveAll() 从此collection中移除指定元素的所有实例
booleanremainAll() 仅保留collection中那些也包含在指定collection的元素
intsize() 返回此collection中的元素数
Object[]toArray() 返回包含此collection中所有元素的数组
T[]toArray() 返回包含此collection中的所有元素的数组;返回数组的运行时类型与指定数组的运行时类型相同

关于Iterator接口说明,Iterator称为迭代接口,通过此接口可以遍历集合中的数据,此接口主要方法为:

booleanhasNext() 如果仍有元素可以迭代,则返回True
Enext() 返回迭代的下一个元素
List

List接口主要有两个实现ArrayList和LinkedList,都是有序的,为可变数组。

  • ArrayList:查询数据比较快,添加和删除数据比较慢(基于可变数据)
  • LinkedList:查询数据比较慢,添加和删除数据比较快(基于链表数据)
  • Vector:效率慢,不建议使用,已经被ArrayList取代
  • Stack:是继承Vector实现的一个栈,目前已经被LinkedList取代
    List是Collection接口的子接口。所以List接口中有一些特有的方法。
    void add(int index, Object element)
    Object set(int index, Object element)
    Object get(int index)
    int indexOf(Object o)
    int lastIndexOf(Object o)
    Object remove(int index)
ArrayList

ArrayList集合初始化容量10,扩容为原容量1.5倍。底层是数组。

public class ArrayListTest {
    public static void main(String[] args) {
        // 面向接口编程,使用Collection更加灵活
        Collection c=new ArrayList();
        List listt=new ArrayList();

        // 自动装箱
        listt.add(1);
        // jdk1.5之前,必须如下使用
        listt.add(new Integer(2));
        // 可以加入重复数据
        listt.add(1);

        // 采用get方法依次获取元素
        for (int i = 0; i < listt.size(); i++) {
            System.out.println((Integer)(listt.get(i)));
        }
        // 调用remove删除集合中的元素
        listt.remove(0);

        // 采用Iterator遍历数据(while)
        Iterator iter=listt.iterator();
        while (iter.hasNext()){
            System.out.println((Integer)iter.next());
        }
        // 采用Iterator遍历数据(for)
        for(Iterator iter1=listt.iterator();iter1.hasNext();){
            System.out.println((Integer)iter1.next());
        }

        // 判断集合中是否包含:3
        System.out.println(listt.contains(3));
        // 判断集合是否为空
        System.out.println(listt.isEmpty());

        // 转换成对象数据
         Object[] arr=listt.toArray();
        for (int i = 0; i < arr.length; i++) {
            System.out.println((Integer)arr[i]);
        }

        // 运行时自动创建相应类型的数组
        Integer[] iArr=new Integer[listt.size()];
        listt.toArray(iArr);
        for (int i = 0; i < iArr.length; i++) {
            System.out.println(iArr[i]);
        }
    }
}
LinkedList

用法同ArrayList,将上述代码中List listt=new ArrayList();替换为List listt=new LinkedList();即可。

Set HashSet

HashSet中的数据是无序,不可重复的。

public class HashSetTest {
    public static void main(String[] args) {
        Set set=new HashSet();
        set.add("c");
        set.add("r");
        set.add("y");
        // 输出是无序的
        for(Iterator iter=set.iterator();iter.hasNext();){
            System.out.println(iter.next());
        }
        // 加入重复数据
        set.add("c");
        for(Iterator iter=set.iterator();iter.hasNext();){
            System.out.println(iter.next());
        }

        String s1="abc";
        String s2="abc";
        System.out.println("s1 equals s2,"+s1.equals(s2));
        // equals相等,hashcode一定是相等的
        System.out.println("s1.hashCode"+s1.hashCode());
        System.out.println("s2.hashCode"+s2.hashCode());
        String s3="cry";
        System.out.println("s1 equals s3,"+s1.equals(s3));
        System.out.println("s3.hashCode"+s3.hashCode());
    }
}
equals和hashCode
public class HashSetTest {
    public static void main(String[] args) {
        Person p1=new Person();
        p1.name="张三";
        p1.age=21;

        Person p2=new Person();
        p2.name="李四";
        p2.age=24;

        Person p3=new Person();
        p3.name="张三";
        p3.age=23;

        Set set=new HashSet();
        set.add(p1);
        set.add(p2);
        set.add(p3);

        for(Iterator iter=set.iterator();iter.hasNext();){
            Person p=(Person)iter.next();
            System.out.println("name="+p.name+",age="+p.age);
        }

        System.out.println("p1.hashCode="+p1.hashCode());
        System.out.println("p2.hashCode="+p2.hashCode());
        System.out.println("p3.hashCode="+p2.hashCode());
    }
}

class Person{
    String name;
    int age;
}

输出结果:

name=李四,age=24
name=张三,age=21
name=张三,age=23
p1.hashCode=1163157884
p2.hashCode=1956725890
p3.hashCode=356573597

加入了重复的数据,因为hashCode不同,所以存储格式也不同。
只有重写equals和hashCode后,程序会在hashCode相同时,调用equals进行比较,如果equals相等将不把此元素加入到Set,如果equals比较不相等则会重新根据hashCode换算位置仍然将该元素加入进去。

class Person{
    String name;
    int age;

    // 重写equals
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }
}

特别强调:向HashSet或HashMap中加入数据时必须同时覆盖equals和hashCode方法!
Java要求:

  • 两个对象equals相等,那么它的hashcode相等
  • 两个对象equals不相等,那么它的hashcode并不要求它不相等,但一般建议不相等
  • hashcode相等不代表两个对象相等(需要采用equals比较)
TreeSet

TreeSet可以对Set集合进行排序,默认自然升序,也可以做客户化的排序

  • 对自然数进行排序
public class TreeSetTest {
    public static void main(String[] args) {
        Set set=new TreeSet();
        set.add(3);
        set.add(2);
        set.add(4);
        //不能存放重复数据
        set.add(3);
        // set.add("abc"); TreeSet只对一种类型排序,加入abc后将无法排序,
        for(Iterator iter = set.iterator();iter.hasNext();){
            System.out.println((Integer)iter.next());
        }
    }
}
  • 对Person进行排序
public class TreeSetTest {
    public static void main(String[] args) {
        Person p1=new Person();
        p1.name="张三";
        p1.age=21;

        Person p2=new Person();
        p2.name="李四";
        p2.age=24;

        Person p3=new Person();
        p3.name="张三";
        p3.age=23;

        Set set=new TreeSet();
        set.add(p1);
        set.add(p2);
        set.add(p3);

        for (Iterator iter=set.iterator(); iter.hasNext();) {
            Person p = (Person)iter.next();
            System.out.println("name=" + p.name + ", age=" + p.age);
        }

    }
}

class Person implements Comparable{
    String name;
    int age;
}

出现错误,提示必须完成Comparable接口。基本类型的包装类以及String类可以排序是因为它们都实现了Comparable接口。

Exception in thread "main" java.lang.ClassCastException: InterfaceTest.Person cannot be cast to java.lang.Comparable
	at java.util.TreeMap.compare(TreeMap.java:1294)
	at java.util.TreeMap.put(TreeMap.java:538)
	at java.util.TreeSet.add(TreeSet.java:255)
	at InterfaceTest.TreeSetTest.main(TreeSetTest.java:23)
实现Comparable接口完成排序
public class TreeSetTest {
    public static void main(String[] args) {
        Person p1=new Person();
        p1.name="张三";
        p1.age=21;

        Person p2=new Person();
        p2.name="李四";
        p2.age=24;

        Person p3=new Person();
        p3.name="张三";
        p3.age=23;

        Set set=new TreeSet();
        set.add(p1);
        set.add(p2);
        set.add(p3);

        for (Iterator iter=set.iterator(); iter.hasNext();) {
            Person p = (Person)iter.next();
            System.out.println("name=" + p.name + ", age=" + p.age);
        }

    }
}

class Person implements Comparable{
    String name;
    int age;

    // 如果重写了equals,最好保证equals和compareto比较规则保持一致
    public int compareTo(Object o){
        if(o instanceof Person){
            Person p=(Person)o;
            // 降序
            return (p.age-this.age);
        }
        throw new IllegalArgumentException("非法参数,O="+o);
    }
}
实现Comparator接口完成排序
public class TreeSetTest {
    public static void main(String[] args) {
        Person p1=new Person();
        p1.name="张三";
        p1.age=21;

        Person p2=new Person();
        p2.name="李四";
        p2.age=24;

        Person p3=new Person();
        p3.name="张三";
        p3.age=23;

        Comparator personComparator=new PersonComparator();

        Set set=new TreeSet(personComparator);
        set.add(p1);
        set.add(p2);
        set.add(p3);

        for (Iterator iter=set.iterator(); iter.hasNext();) {
            Person p = (Person)iter.next();
            System.out.println("name=" + p.name + ", age=" + p.age);
        }

    }
}

class Person{
    String name;
    int age;
}

// 实现Person比较器
class PersonComparator implements Comparator{
    @Override
    public int compare(Object o1, Object o2) {
        if(!(o1 instanceof Person)){
            throw new IllegalArgumentException("非法参数,o1="+o1);
        }
        if(!(o2 instanceof Person)){
            throw new IllegalArgumentException("非法参数,o2="+o2);
        }
        Person p1=(Person)o1;
        Person p2=(Person)o2;
        return p1.age-p2.age;
    }
}
采用匿名类完成Comparator的实现
public class TreeSetTest {
    public static void main(String[] args) {
        Person p1=new Person();
        p1.name="张三";
        p1.age=21;

        Person p2=new Person();
        p2.name="李四";
        p2.age=24;

        Person p3=new Person();
        p3.name="张三";
        p3.age=23;



        Set set=new TreeSet(new Comparator(){
            public int compare(Object o1, Object o2) {
                if(!(o1 instanceof Person)){
                    throw new IllegalArgumentException("非法参数,o1="+o1);
                }
                if(!(o2 instanceof Person)){
                    throw new IllegalArgumentException("非法参数,o2="+o2);
                }
                Person p1=(Person)o1;
                Person p2=(Person)o2;
                return p1.age-p2.age;
            }
        });
        set.add(p1);
        set.add(p2);
        set.add(p3);

        for (Iterator iter=set.iterator(); iter.hasNext();) {
            Person p = (Person)iter.next();
            System.out.println("name=" + p.name + ", age=" + p.age);
        }

    }
}

class Person{
    String name;
    int age;
}
Comparable与Comparator

Comparable是默认的比较接口,Comparator可以分离比较规则,更加灵活。
一个类实现了Comparable接口则表明该类的对象之间是可以相互比较的,这个类对象组成的集合可以直接使用sort方法排序。
Comparator可以看作是一种算法的实现,将算法与数据分离,Comparator还可以在以下两种环境使用:

  1. 类没有考虑到比较问题而没有实现Comparable,可以用过Comparator来实现排序并且不用改变对象本身
  2. 可以使用多种排序标准,如升序、降序等。
Map

Map中可以放置键值对,每个元素包含键对象和值对象,Map实现较常用的为HashMap,HashMap对键对象的存取和HashSet一样,仍然采用的是哈希算法,所以如果使用自定义类作为Map的键对象,必须重写equals和hashCode方法。
遍历Map集合的两种方式都要精通。
第一种:获取所有key,遍历每个key,通过key获取value.
第二种:获取Set即可,遍历Set集合中的Entry
调用entry.getKey() entry.getValue()

HashMap
public class HashMapTest {
    public static void main(String[] args) {
        Map map=new HashMap();
        map.put("101","张三");
        map.put("102","李四");
        map.put("103","王五");

        // 采用entrySet遍历Map
        Set entrySet=map.entrySet();
        for(Iterator iter=entrySet.iterator();((Iterator) iter).hasNext();){
            Map.Entry entry=(Map.Entry)iter.next();
            System.out.println(entry.getKey()+","+entry.getValue());
        }

        // 取得map中指定的key
        Object v=map.get("101");
        System.out.println("101-----"+v);

        // 如果存在相同的条目,会采用此条目替换
        // 但map中始终保持的是不可重复的数据
        // 主要依靠key值判断是否重复,和value无关
        map.put("103","cry");

        // 采用keySet和get获取map中所有的数据
        for(Iterator iter=map.keySet().iterator();iter.hasNext();){
            String k=(String)iter.next();
            System.out.println(k+","+map.get(k));
        }
    }
}
HashMap,采用自定义类作为key
public class HashMapTest02 {
    public static void main(String[] args) {
        IdCard idCard1=new IdCard();
        idCard1.cardNo=123456789L;
        Player player1=new Player();
        player1.name="张三";

        IdCard idCard2=new IdCard();
        idCard2.cardNo=123456789L;
        Player player2=new Player();
        player2.name="李四";

        IdCard idCard3=new IdCard();
        idCard3.cardNo=123456789L;
        Player player3=new Player();
        player3.name="张三";

        Map map=new HashMap();
        map.put(idCard1,player1);
        map.put(idCard2,player2);
        map.put(idCard3,player3);

        for(Iterator iter=map.entrySet().iterator();((Iterator) iter).hasNext();){
            Map.Entry entry=(Map.Entry)iter.next();
            IdCard idCard=(IdCard)entry.getKey();
            Player player=(Player)entry.getValue();
            System.out.println(idCard.cardNo+","+player.name);
        }
    }
}

class IdCard{
    long cardNo;
}
class Player{
    String name;
}

以上加入了重复的数据:

123456789,李四
123456789,张三
123456789,张三

因为 HashMap 的底层实现采用的是 hash 表,所以 Map 的 key 必须重写hashcode 和 equals 方法。

HashMap,重写IdCard的equals和hashCode方法
class IdCard{
    long cardNo;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        IdCard idCard = (IdCard) o;
        return cardNo == idCard.cardNo;
    }

    @Override
    public int hashCode() {
        return Objects.hash(cardNo);
    }
}
class Player{
    String name;
}

此处对IdCard的equals和hashCode进行了重写,原程序并没有加入重复的数据。

123456789,张三
TreeMap

TreeMap可以对Map的key进行排序,如果map中的key采用的是自定义类则需要实现Comparable或Comparator接口完成排序。

public class TreeMapTest {
    public static void main(String[] args) {
        Map map=new TreeMap();
        map.put("103","王五");
        map.put("101","张三");
        map.put("102","李四");

        for(Iterator iter=map.entrySet().iterator();((Iterator) iter).hasNext();){
            Map.Entry entry=(Map.Entry)iter.next();
            System.out.println(entry.getKey()+","+entry.getValue());
        }
    }
}
Collections工具类

Collections位于java.util包中,提供了一系列实用的方法,如:对集合排序,对集合的内容查找等。

public class CollectionsTest {
    public static void main(String[] args) {
        List listt=new ArrayList();
        listt.add(4);
        listt.add(1);
        listt.add(3);
        listt.add(2);
        for(Iterator iter=listt.iterator();((Iterator) iter).hasNext();){
            System.out.print(iter.next());
        }
        System.out.println("");

        Collections.sort(listt);
        for(Iterator iter=listt.iterator();iter.hasNext();){
            System.out.print(iter.next());
        }
        System.out.println("");

        Set set=new HashSet();
        set.add(9);
        set.add(6);
        set.add(8);
        set.add(7);
        // 不能直接对set排序
        // Collections.sort(set);
        List setList=new ArrayList(set);
        Collections.sort(setList);
        for(Iterator iter=setList.iterator();iter.hasNext();){
            System.out.print(iter.next());
        }
        System.out.println("");

        int index=Collections.binarySearch(setList,9);
        System.out.println("index="+index);

        Collections.reverse(setList);
        for(Iterator iter=setList.iterator();iter.hasNext();){
            System.out.print(iter.next());
        }
    }
}
泛型

泛型能更早的发现错误,如类型转换错误,通常在运行期才会发现,如果使用泛型则能够在编译期发现错误。通常错误发现的越早越容易调试,从而减少成本。

为什么使用泛型
public class GenericTest {
    public static void main(String[] args) {
        List listt=new ArrayList();
        listt.add(1);
        listt.add(2);
        listt.add(3);

        for(Iterator iter=listt.iterator();((Iterator) iter).hasNext();){
            String s=(String)iter.next();
            System.out.println(s);
        }
    }
}

出现java.lang.ClassCastException异常,这种转型错误最好能在编译期发现,那么就必须使用泛型。

案例一
public class GenericTest {
    public static void main(String[] args) {
        List listt=new ArrayList();
        listt.add(1);
        listt.add(2);
        listt.add(3);

        // 使用了泛型,abc无法放进集合中,会在编译期报错
        // listt.add("abc");
        for(Iterator iter=listt.iterator();iter.hasNext();){
            // 使用了泛型,编译器会报错
            // String s=(String)iter.next();

            // 使用了泛型,在编译器不用强制转换
            // Integer s=(Integer)iter.next();
            
            // 可以直接获取相应元素,使用泛型返回的是真正的类型
            Integer s=iter.next();
            System.out.println(s);
        }
    }
}
采用泛型改善自定义比较器
public class GenericTest {
    public static void main(String[] args) {
        Person p1=new Person();
        p1.name="张三";
        p1.age=21;

        Person p2=new Person();
        p2.name="李四";
        p2.age=24;

        Person p3=new Person();
        p3.name="张三";
        p3.age=23;



        Set set=new TreeSet();
        set.add(p1);
        set.add(p2);
        set.add(p3);

        for (Iterator iter=set.iterator(); iter.hasNext();) {
            Person p = (Person)iter.next();
            System.out.println("name=" + p.name + ", age=" + p.age);
        }
    }
}

class Person implements Comparable{
    String name;
    int age;

    @Override
    // 使用了泛型,instanceof就不用再写了
    public int compareTo(Person o) {
        return (this.age-o.age);
    }
}
采用泛型改造Map
public class HashMapTest02 {
    public static void main(String[] args) {
        IdCard idCard1=new IdCard();
        idCard1.cardNo=123456789L;
        Player player1=new Player();
        player1.name="张三";

        IdCard idCard2=new IdCard();
        idCard2.cardNo=123456789L;
        Player player2=new Player();
        player2.name="李四";

        IdCard idCard3=new IdCard();
        idCard3.cardNo=123456789L;
        Player player3=new Player();
        player3.name="张三";

        Map map=new HashMap();
        map.put(idCard1,player1);
        map.put(idCard2,player2);
        map.put(idCard3,player3);

        for(Iterator> iter=map.entrySet().iterator();((Iterator) iter).hasNext();){
//            Map.Entry entry=(Map.Entry)iter.next();
//            IdCard idCard=(IdCard)entry.getKey();
//            Player player=(Player)entry.getValue();
            Map.Entry entry=iter.next();
            IdCard idCard=entry.getKey();
            Player player=entry.getValue();
            System.out.println(idCard.cardNo+","+player.name);
        }
    }
}

class IdCard{
    long cardNo;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        IdCard idCard = (IdCard) o;
        return cardNo == idCard.cardNo;
    }

    @Override
    public int hashCode() {
        return Objects.hash(cardNo);
    }
}
class Player{
    String name;
}
自定义泛型
public class GenericTest {

    private T obj;

    public T getObj() {
        return obj;
    }

    public void setObj(T obj) {
        this.obj = obj;
    }

    public static void main(String[] args) {
        GenericTest g=new GenericTest();
        g.setObj("abc");
        String s=g.getObj();
    }
}

如上,泛型的标识符没有限制,只要符合Java标识符的命名规范即可,如。最好和JDK的泛型标识符一样。

JDK新特性
  • JDK5.0新特性
    foreach循环
对数组怎么遍历?
		for(int i : arr){
			System.out.println(i);
		}
	对集合怎么遍历?
		for(String s : list){
			System.out.println(s);
		}
  • JDK8.0新特性:钻石表达式
List list = new ArrayList<>();

类型自动推断!

遗留类对比表
遗留类缺点取代类
Vector方法是同步的,影响性能ArrayList和LinkedList
Hashtable方法是同步的,影响性能HashMap
Stack继承了Vector,影响性能LinkedList
Enumeration只能与历史集合使用Iterator
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/873774.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号