java集合

此文档为java集合的学习总结

集合框架

Collection接口:

    List接口:有序,可以重复
        ArrayList(主要实现类)
        LinkedList(频繁的插入、删除操作)
        Vector(古老的实现类,线程安全)

    Set接口:无序、不可重复
        HashSet(主要实现类)
        LinkedHashSet(使用链表维护了添加元素顺序,可以有序遍历,但底层存储元素还是hash无序的)
        TreeSet(可以按照添加进集合中的元素的指定顺序遍历)

Map接口:存储key-value对
        HashMap、LinkedHashMap、TreeMap、HashTable(-Properties)

两个排序接口:
    自然排序:实现Comparable接口重写compareTo()方法
    定制排序:创建TreeSet时传入Comparator对象

ArrayList

将Employee[]数组替换成ArrayList后:

不必指出数组的大小

使用add将任意多的元素添加到数组中

使用size()替代length计算元素的数目

使用a.get(i)替代a[i]访问元素

ArrayList<>尖括号中的类型参数不允许是基本类型。即不允许写成ArrayList,而只能用Integer对象包装器类ArrayList list = new ArrayList<>();

ArrayList<>与数组之间的转换

  • 1.数组转ArrayList–直接转
import java.util.ArrayList;

public class Test
{
    public static void main(String args[])
    {
        Employee[] staff = new Employee[2];
        staff[0] = new Employee("alice");
        staff[1] = new Employee("bob");

        ArrayList<Employee> list = new ArrayList<>();
        list.add(staff[0]);
        list.add(staff[1]);

        System.out.println(list.size());
        for(Employee e: list)
        {
            System.out.println(e.getName());
        }
    }
}

class Employee
{
    private String name;
    public Employee(String n)
    {
        name = n;
    }
    public String getName()
    {
        return name;
    }
}

output:

2
alice
bob
  • 2.ArrayList转数组–toArray
public class Test
{
    public static void main(String args[])
    {        
        ArrayList<Employee> list = new ArrayList<>();
        list.add(new Employee("alice"));
        list.add(new Employee("bob"));
        System.out.println(list.size());

        Employee[] staff = new Employee[list.size()];
        list.toArray(staff);//把list放进staff数组中

        for(Employee e: staff)
        {
            System.out.println(e.getName());
        }
    }
}

output:

2
alice
bob

ArrayList示例小程序

import java.util.*;

/**
 * This program demonstrates the ArrayList class.
 */
public class Test
{
    public static void main(String args[])
    {
        ArrayList<Employee> staff = new ArrayList<>();

        staff.add(new Employee("Alice", 75000, 1987, 12, 15));
        staff.add(new Employee("Bob", 50000, 1989, 10, 1));
        staff.add(new Employee("Carl", 40000, 1990, 3, 15));

        for(Employee e: staff)
        {
            e.raiseSalary(5);
        }

        for(Employee e: staff)
        {
            System.out.println("name="+e.getName()+", salary="+e.getSalary()
            +", hireDay="+e.getHireDay());
        }
    }
}

class Employee  
{  
    private String name;  
    private double salary;  
    private Date hireDay;  

    public Employee(String n, double s, int year, int month, int day)  
    {  
        name = n;  
        salary = s;  
        GregorianCalendar cal = new GregorianCalendar(year, month-1, day);  
        hireDay = cal.getTime();  
    }  

    public String getName()  
    {  
        return name;  
    }  

    public double getSalary()  
    {  
        return salary;  
    }  

    public Date getHireDay()  
    {  
        return hireDay;  
    }  

    public void raiseSalary(double percent)  
    {  
        salary *= (1 + percent/100);  
    }  

    public boolean equals(Object obj)  
    {  
        if(this == obj)  
            return true;  
        if(this == null)  
            return false;  
        if(this.getClass() != obj.getClass())  
            return false;  

        Employee tmp = (Employee)obj;  
        return Objects.equals(name, tmp.name) && this.salary==tmp.salary && hireDay.equals(tmp.hireDay);  
    }  

    public int hashCode()  
    {  
        return Objects.hash(name, salary, hireDay);  
    }  

    public String toString()  
    {  
        return getClass().getName()+"[name="+name+",salary="+salary+",hireDay="+hireDay+"]";  
    }  
}

output:

name=Alice, salary=78750.0, hireDay=Tue Dec 15 00:00:00 CST 1987
name=Bob, salary=52500.0, hireDay=Sun Oct 01 00:00:00 CST 1989
name=Carl, salary=42000.0, hireDay=Thu Mar 15 00:00:00 CST 1990

LinkedList

LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的get,remove,insert方法在LinkedList的首部或尾部。这些操作使LinkedList可被用作队列(queue)或双向队列(deque),因为LinkedList类也实现了Queue、Deque接口。

import java.util.LinkedList; //这是实现了List、Queue、Deque等接口的实现类
import java.util.Queue; //这是队列的接口

public class Main {
    public static void main(String[] args) {
        //add()和remove()方法在失败的时候会抛出异常(不推荐)
        Queue<String> queue = new LinkedList<String>();
        //队尾添加元素
        queue.offer("a");
        queue.offer("b");
        queue.offer("c");
        queue.offer("d");
        queue.offer("e");
        for(String q : queue){
            System.out.println(q);
        }
        System.out.println("===");
        System.out.println("poll="+queue.poll()); //返回并删除队头元素
        for(String q : queue){
            System.out.println(q);
        }
        System.out.println("===");
        System.out.println("element="+queue.element()); //返回第一个元素 
        for(String q : queue){
            System.out.println(q);
        }
        System.out.println("===");
        System.out.println("peek="+queue.peek()); //返回第一个元素 
        for(String q : queue){
            System.out.println(q);
        }
    }
}

out:

a
b
c
d
e
===
poll=a
b
c
d
e
===
element=b
b
c
d
e
===
peek=b
b
c
d
e

注意LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:

List list = Collections.synchronizedList(new LinkedList(...));

Set

Set中的元素是用hash算法来存储的。

hash算法:当向Set中添加对象时,首先调用此对象所在类的hashCode()方法,计算此对象的hash值,此hash值决定了此对象在Set中的存储位置。若此位置没有对象存储,则存储该对象,若已有对象,再通过equals()方法比较两个对象是否相等,如果相等则不再存储,如果不相等,则hash冲突往后进行存储。

hash算法使Set有不可重复性,因此若要往Set中添加元素时,该元素所在的类需要同时重写equals()和hashCode()方法。

TreeSet

与其他Set不同,TreeSet中的元素必须是同一类的,且涉及排序(自然排序、定制排序),TreeSet底层按照红黑树进行排序。

自然排序:需要TreeSet中元素所在的类实现Comparable接口,重写相应的compareTo()方法。
compareTo()、equals()、hashCode()三者须一致。如果不一致,判断set重复元素时先按照compareTO()中定义的来,如compareTO()定义的name比较,那么相同名字的就算重复元素。

定制排序:创建TreeSet时传入Comparator对象

import org.junit.Test;
import java.util.*;

/**
 * TreeSet自然排序、定制排序的演示例子
 * 自然排序:需要TreeSet中元素所在的类实现Comparable接口,重写相应的compareTo()方法
 * 定制排序:创建TreeSet时传入Comparator对象
 */
public class TestTreeSet{
    //自然排序:需要Employee实现Comparable接口。这里按照name进行排序
    @Test
    public void test1(){
        Employee e1 = new Employee("名1", 25, new MyDate(1990,10,13));
        Employee e6 = new Employee("名1", 24, new MyDate(1990,10,13));
        Employee e2 = new Employee("名2", 21, new MyDate(1993,10,13));
        Employee e3 = new Employee("学1", 21, new MyDate(1993,6,13));
        Employee e4 = new Employee("张2", 21, new MyDate(1993,6,20));
        Employee e5 = new Employee("名5", 26, new MyDate(1997,3,13));
        TreeSet set = new TreeSet();
        set.add(e1);
        set.add(e2);
        set.add(e3);
        set.add(e4);
        set.add(e5);
        set.add(e6);

        Iterator it = set.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }
    }

    //定制排序:创建TreeSet时传入Comparator对象。这里按照生日进行排序
    @Test
    public void test2(){
        Comparator com = new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                if(o1 instanceof Employee && o2 instanceof Employee){
                    Employee e1 = (Employee)o1;
                    Employee e2 = (Employee)o2;
                    MyDate bir1 = e1.getBirthday();
                    MyDate bir2 = e2.getBirthday();
                    if(bir1.getYear() == bir2.getYear()){
                        if(bir1.getMonth() == bir2.getMonth()){
                            return bir1.getDay() - bir2.getDay();
                        }
                        return bir1.getMonth() - bir2.getMonth();

                    }
                    return bir1.getYear() - bir2.getYear();
                }
                return 0;
            }
        };

        Employee e1 = new Employee("名1", 25, new MyDate(1990,10,13));
        Employee e6 = new Employee("名1", 24, new MyDate(1990,10,13));
        Employee e2 = new Employee("名2", 21, new MyDate(1993,10,13));
        Employee e3 = new Employee("学1", 21, new MyDate(1993,6,13));
        Employee e4 = new Employee("张2", 21, new MyDate(1993,6,20));
        Employee e5 = new Employee("名5", 26, new MyDate(1997,3,13));
        TreeSet set = new TreeSet(com);
        set.add(e1);
        set.add(e2);
        set.add(e3);
        set.add(e4);
        set.add(e5);
        set.add(e6);

        Iterator it = set.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }
    }
}

class Employee implements Comparable{
    private String name;
    private int age;
    private MyDate birthday;

    //自然排序:需要Employee实现Comparable接口。这里按照name进行排序
    public int compareTo(Object o){
        if(o instanceof Employee){
            Employee e = (Employee)o;
            return this.name.compareTo(e.getName());
        }
        return 0;
    }

    public Employee(String name, int age, MyDate birthday) {
        this.name = name;
        this.age = age;
        this.birthday = birthday;
    }

    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 MyDate getBirthday() {
        return birthday;
    }

    public void setBirthday(MyDate birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", birthday=" + birthday +
                '}';
    }

    //同时需要重写equals方法,因为TreeSet中如contains等方法都需要equals判断
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Employee employee = (Employee) o;
        return age == employee.age &&
                Objects.equals(name, employee.name) &&
                Objects.equals(birthday, employee.birthday);
    }

    @Override
    public int hashCode() {

        return Objects.hash(name, age, birthday);
    }
}

class MyDate{
    private int month;
    private int day;
    private int year;

    public MyDate(int year, int month, int day) {
        this.month = month;
        this.day = day;
        this.year = year;
    }

    public int getMonth() {
        return month;
    }

    public void setMonth(int month) {
        this.month = month;
    }

    public int getDay() {
        return day;
    }

    public void setDay(int day) {
        this.day = day;
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    @Override
    public String toString() {
        return "MyDate{" +
                "month=" + month +
                ", day=" + day +
                ", year=" + year +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MyDate myDate = (MyDate) o;
        return month == myDate.month &&
                day == myDate.day &&
                year == myDate.year;
    }

    @Override
    public int hashCode() {

        return Objects.hash(month, day, year);
    }
}

Map接口

HashMap:主要实现类

LinkedHashMap:使用链表维护添加进Map中的顺序,故遍历Map时,是按添加顺序遍历的

TreeMap:按照添加进Map中元素的key的指定属性进行排序。故key必须是同一类的对象

Hashtable(子类Properties):Hashtable古老实现类,线程安全

Map与Collection并列存在,用于保存具有映射关系的数据:key-value

Map中的key和value都可以是任何引用类型的数据

Map中的key用Set来存放,不可重复,即同一个Map对象所对应的类,须重写hashCode()、equals()方法

各类Set其实就是Map的特例,Map中的key-value的value=null即为Set

常用String类作为Map的key

key和value之间存在单项一对一关系,即通过指定的key总能找到唯一的、确定的value

Java遍历Map对象的四种方式

Java遍历Map对象的四种方式

文中讲了四种遍历方法,最快捷常用的是:

Map<Integer, Integer> map = new HashMap<Integer, Integer>(); 
for (Map.Entry<Integer, Integer> entry : map.entrySet()) { 
  System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue()); 
}

HashMap

向HashMap中添加元素时,会调用key所在类的equals方法,判断两个key是否相同,若相同则只能添加后面添加的那个元素(覆盖前面元素)

HashMap遍历key集、value集。key是Set,value是Collection、key-value对是一个Set entrySet()

public void test1(){
    HashMap hm = new HashMap();
    hm.put(123, "asdad");
    hm.put("AA", 12532);
    hm.put(1234, new SimpleDateFormat("mm:ss").format(new Date()));
    System.out.println(hm);

    //遍历key集
    Set set = hm.keySet();
    Iterator it = set.iterator();
    while(it.hasNext()){//迭代器遍历
        System.out.println(it.next());
    }

    for(Object o: set){//增强for循环遍历
        System.out.println(o);
    }

    //遍历value集
    for(Object o: set){//通过key遍历来遍历value
        System.out.println(hm.get(o));
    }

    Collection coll = hm.values();
    for(Object o: coll){//通过value集的增强for循环遍历
        System.out.println(o);
    }

    //遍历key-value对
    //通过上述key遍历来遍历value可以得到,另一种方法是key-value对是一个Set entrySet()
    Set entry = hm.entrySet();
    for(Object obj: entry){
        Map.Entry ety = (Map.Entry)obj;
        System.out.println(ety.getKey() + "-->" + ety.getValue());
    }
}

TreeMap

TreeSet是特殊的TreeMap,按照添加进Map中元素的key的指定属性进行排序。因此TreeMap的key必须是同一类的对象,且有自然排序、定制排序。

自然排序:key元素所在类要实现Comparable接口重写compareTo()、equals()、hashCode()方法

定制排序:创建TreeMap时传入Comparator对象

Hashtable

Hashtable古老实现类,线程安全

与HashMap不同,Hashtable不允许使用null作为key和value

与HashMap一样,Hashtable也不能保证其中key-value对的顺序

Hashtable判断两个key相等、两个value相等的标准,与HashMap一致

Hashtable的子类Properties:常用来处理属性文件,key、value都为String类型

import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.*;

public class TestHashMap{
    @Test
    public void test1() throws IOException{
        Properties pro = new Properties();
        pro.load(new FileInputStream(new File("abc.properties")));
        String user = pro.getProperty("user");
        String pass = pro.getProperty("password");
        System.out.println(user + ":" + pass);
    }
}
//abc.properties
//user=root
//pass=123456
//out:  root:123456

Collections:操作集合的工具类

Arrays是一个操作数组的工具类;Collections是一个操作Set、List、Map等集合的工具类

Collections提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制方法

1.排序操作:

Collections.reverse(list);//反转list中元素的顺序
Collections.shuffle(list);//对list集合元素进行随机排序
Collections.sort(list); //根据元素的自然顺序对list集合元素进行升序排序
Collections.sort(list, Comparator); //根据指定的Comparator产生的顺序对list集合元素进行排序
Collections.swap(list, int, int)//将list中i处、j处元素进行交换

2.查找、替换:

Collections.max(Collection) //根据自然顺序,返回集合中最大元素
Collections.max(Collection, Comparator) //根据指定顺序,返回集合中最大元素
Collections.frequency(Collection) //返回指定元素的出现次数
Collections.copy(List dest, List src) //将src的内容复制到dest中
Collections.replaceAll(List, Object oldVal, Object newVal) //替换

这里注意Collections.copy(dest list, src list)需要提前设置dest list大小:

public void test1(){
        List list = new ArrayList();
        list.add(123);
        list.add(13);
        list.add(12);
        list.add(12);
        list.add(4456);
        System.out.println(list);
        List list2 = Arrays.asList(new Object[list.size()]);//没有这句设置list2空间的话error
        Collections.copy(list2, list);
        System.out.println(list2);
}

3.同步控制:

Collections类中提供了多个synchronizedXxX()方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题(因为ArrayList、HashSet、HashMap等等全部都线程不安全,所以多线程时可使用Collections的同步控制来达到线程安全)

    public void test1(){
            List list = new ArrayList(); //线程不安全
            list.add(123);
            list.add(13);
            list.add(12);
            list.add(12);
            list.add(4456);

            List list2 = Collections.synchronizedList(list);
            System.out.println(list2); //list2是线程安全的
    }

HashMap与concurrentHashMap

1.HashMap:

1583508137

2.concurrentHashMap:

1583508181


欢迎转载,欢迎错误指正与技术交流,欢迎交友谈心

文章标题:java集合

文章字数:3.3k

本文作者:Brain Cao

发布时间:2018-12-16, 15:54:20

最后更新:2020-03-15, 16:26:00

原始链接:https://braincao.cn/2018/12/16/java-collection/

版权声明:本文为博主原创文章,遵循 BY-NC-SA 4.0 版权协议,转载请保留原文链接与作者。

目录
×

喜欢请收藏,疼爱就打赏