二万长文,概括Java中集合的大致用法, 做个字典就行!

本篇文章是一个学习java的笔记,概括集合中的 list ,Arraylist, HashMap, EnumMap, TreeMap, Properties, Set, Queue , PriorityQueue, Deque, Collections 的基本用法, 以及集合的各种重写方法手段,当个字典吧!


集合

声明:下面是整理的笔记, 想看原文点击链接


在Java中,如果一个Java对象可以在内部持有若干其他Java对象,并对外提供访问接口,我们把这种Java对象称为集合, 我们最开始了解的数组,其实也可以看作是一种集合。

java集合设计中存在二个特点,一是实现了接口和实现类相分离,例如,有序表的接口是List,具体的实现类有ArrayListLinkedList等,二是支持泛型,我们可以限制在一个集合中只能放入同一种数据类型的元素,例如:

public ArrayList<String> myList = new ArrayList<String>();  // 这个list只能存放string类型

因为数组存在一定的缺陷,并不能满足我们的编程要求。

  • 数组初始化后大小不可变;
  • 数组只能按索引顺序存取。

Java的java.util包主要提供了以下三种类型的集合:

  • List:一种有序列表的集合,例如,按索引排列的StudentList
  • Set:一种保证没有重复元素的集合,例如,所有无重复名称的StudentSet
  • Map:一种通过键值(key-value)查找的映射表集合,例如,根据Studentname查找对应StudentMap

集合list:

在集合类中, list作为最基础的一种集合, 他是有序列表,但不同于数组, 它可以进行内存的自动扩张和收缩, 并且list将添加和删除的操作封装起来了,使我们操作集合更简单,而不用去关心集合中的元素是如何移动的。

关于list的接口, 常见的接口方法如下:

  • 在末尾添加一个元素:boolean add(E e)
  • 在指定索引添加一个元素:boolean add(int index, E e)
  • 删除指定索引的元素:int remove(int index)
  • 删除某个元素:int remove(Object e)
  • 获取指定索引的元素:E get(int index)
  • 获取链表大小(包含元素的个数):int size()

常见的list子类, 有 ArrayList 和 LinkedList , 但是我们常用的还是ArrayList

比较如下:

扫描二维码关注公众号,回复: 11618051 查看本文章
描述 ArrayList LinkedList
获取指定元素 速度很快 需要从头开始查找元素
添加元素到末尾 速度很快 速度很快
在指定位置添加/删除 需要移动元素 不需要移动元素
内存占用 较大

关于list的Tip:

  • list可以允许我们加入相同的值

  • list可以允许们加入null值

  • 创建list可以使用list的接口of()方法, 根据指定元素快速生成, 但是不允许加入null值

  • 遍历list常用 for each 方法

public static void main(String[] args){
     // 常规方法创建list
    ArrayList<Integer> list = new ArrayList<Integer>();
    //  可以允许我们加入相同的值
    list.add('1');
    list.add('1'); 
    //  可以允许我们加入null值
    list.add(null);
    
    //  使用of()  方式快捷生成集合  但是这种方式不能加入null值
    ArrayList<String> mylist = mylist.of("hello", "world");
    
    //  遍历list
    for(Integer integer:list){
        System.out.println(integer);
     }
    //  或者像数组一样遍历
    for(int i=0;i<list.size(); i++){
        System.out.println(list.get(i));
    }
}

list集合与Array数组的相互转换

  • list转换array

    调用 toArray() 方式

    public class Main {
        public static void main(String[] args) {
            List<String> list = List.of("apple", "pear", "banana");
            // 转换如下
            String[] array = list.toArray(new String[list.size()]);
            for (String s : array) {
                System.out.println(s);
            }
            // 或者更简洁方式
            String[] array = list.toArray(String[]::new);
        }
    }
    
    
  • array转换为list

​ 调用 List.of() 方式

Integer[] array = {2, 2, 2};
ArrarList<Integer> list = ArrayList.of(arrar);
// 如果是用List 作为接口, 那么返回的list只是一个只读list, 操作它就会出错。

list中比较需重写equals方法:

list作为一个有序列表, 按照先后顺序将数值存放, 所以我们可以根据索引值去获取特定值。

List还提供了boolean contains(Object o)方法来判断List是否包含某个指定元素。此外,int indexOf(Object o)方法可以返回某个元素的索引,如果元素不存在,就返回-1

先看一个例子:

public class Main {
    public static void main(String[] args) {
        List<String> list = List.of("A", "B", "C");
        //  简单测试
        System.out.prinln(list.contains("A"));   	// True
        // 如果传入一个新的实例
        System.out.prinln(list.contains(new String("A")));  //True
        // 结果还是 True
    }
}

根据上面代码, 在不同实例中,却得到了相同的结果, 原因是list内部使用了equals() 去判断元素是否相同,因此,要正确使用Listcontains()indexOf()这些方法,放入的实例必须正确覆写equals()方法,否则,放进去的实例,查找不到。我们之所以能正常放入StringInteger这些对象,是因为Java标准库定义的这些类已经正确实现了equals()方法, 所以如果自己创建了一个对象,那么就需要重写equals() 方法。

比如:

public class Main {
    public static void main(String[] args) {
        List<Person> list = List.of(
            new Person("Xiao Ming", 90),
            new Person("Xiao Hong", 98),
            new Person("Bob", 50)
        );
        System.out.println(list.contains("Bob")); 
    }
}

class Person {
    String name;
    Integer score;
    public Person(String name, Integer score) {
        this.name = name;
        this.score = score;
    }
}

// 结果输出是false

如何正确的重写equals() 方法:

我们要注意这个方法要达到什么要求返回True, 比如上面代码, 当名字和分数相同,就返回True

所以我们这样编写:

public boolean equals(Object o) {
    if (o instanceof Person) {
        Person p = (Person) o;
        return Objects.equals(this.name, p.name) && this.score == p.score;
    }
    return false;
}

把这个代码加到上面,就是这样的效果:

public class Main {
    public static void main(String[] args) {
        List<Person> list = List.of(
            new Person("Xiao Ming", 90),
            new Person("Xiao Hong", 98),
            new Person("Bob", 50)
        );
        System.out.println(list.contains(new Person("Bob", 50));  // 比较的时候,传入一个泛型对象
    }
}

class Person {
    String name;
    Integer score;
    public Person(String name, Integer score) {
        this.name = name;
        this.score = score;
    }
    @Override
    public boolean equals(Object o) {
    if (o instanceof Person) { // 首先判断类型 确保不是null类型
        Person p = (Person) o;
        return Objects.equals(this.name, p.name) && this.score == p.score;
    }
    return false;
	}
}

List中查找元素时,List的实现类通过元素的equals()方法比较两个元素是否相等,因此,放入的元素必须正确覆写equals()方法,Java标准库提供的StringInteger等已经覆写了equals()方法

编写equals()方法可借助Objects.equals()判断。

如果不在List中查找元素,就不必覆写equals()方法。


映射表Map:

在python中,我们知道有字典这样的键值对来达到映射的效果, 可以更快的查找特定的值, 但是在java中,也有Map,Map这种键值(key-value)映射表的数据结构,作用就是能高效通过key快速查找value(元素)。

比如在如下情况,我们查找指定的信息

package listTest;

import java.util.HashMap;

public class mapiter {
    public HashMap<String, Student> map = new HashMap<>();

    public mapiter() {
        // 将"Xiao Ming"和Student实例映射并关联
        map.put("Xiao Ming", new Student("Xiao Ming", 99));
    }

    public static void main(String[] args) {
        mapiter m = new mapiter();
        // 通过key查找并返回映射的Student实例
        Student target = m.map.get("Xiao Ming");
        // true,同一个实例
        System.out.println(target.score);
        // 99
        Student another = m.map.get("Bob");
        // 通过另一个key查找
        System.out.println(another);
        // 未找到返回null
    }
}

class Student {
    public String name;
    public int score;

    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
}

关于map的tip

  • 如果要判断某个 key 是否存在, 我们可以使用 boolean containsKey(K key) 方法

  • 对map中put相同的key, 不会报错,但是后者会覆盖前者信息, 最好还是单个存在

  • map中不存在顺序之说, 没有顺序,只有映射关系

  • 对于map来说, 遍历存在二种方式。

package listTest;

import java.util.HashMap;
import java.util.Map;

public class mapiter {
    public HashMap<String, Student> map = new HashMap<>();

    public mapiter() {
        // 将"Xiao Ming"和Student实例映射并关联
        map.put("Xiao Ming", new Student("Xiao Ming", 99));
        map.put("Xiao Gang", new Student("Xiao Gang", 87));
        map.put("Xiao Hong", new Student("Xiao Hone", 99));
    }
    public void  fun(){
        // 正常遍历方法
        System.out.println("for each 遍历");
        for(String s: map.keySet()){
            String name = map.get(s).name;
            int score = map.get(s).score;
            System.out.println(name + " = " + score);
        }
    }

    public void fun2(){
        // 其他方式遍历

        System.out.println("其他方式遍历");
        for(Map.Entry<String, Student> entry:map.entrySet()){
            String name = entry.getValue().name;
            Integer score = entry.getValue().score;
            System.out.println(name + " = " + score);
        }
    }
    public static void main(String[] args) {
        mapiter m = new mapiter();
        m.fun();
        m.fun2();

    }
}

class Student {
    public String name;
    public int score;

    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
}

Map的内部,对key做比较是通过equals()实现的,这一点和List查找元素需要正确覆写equals()是一样的,即正确使用Map必须保证:作为key的对象必须正确覆写equals()方法。

我们经常使用String作为key,因为String已经正确覆写了equals()方法。但如果我们放入的key是一个自己写的类,就必须保证正确覆写了equals()方法,并且我们还要正确的编写 hashCode() 方法,因为 key计算索引的方式就是调用key对象的hashCode()方法,它返回一个int整数。HashMap正是通过这个方法直接定位key对应的value的索引,继而直接返回value,所以如果是自己写的类, 就必须重写这二个方法。

值得一提的是, 实际上HashMap初始化时默认的数组大小只有16, 当数据达到了上限, HashMap就会自动扩容, 我们也可以指定map容量

HashMap<String, Integer> map = new HashMap<>(10000);

重写方法

class Student {
    public String name;
    public int score;

    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
   // 重写equals()
    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Student){
            Student st = (Student) obj;
            return Objects.equals(this.name, st.name) && this.score == st.score;
            
        }
        return false;
    }
    // 重写hashCode()
    @Override
    public int hashCode() {
        return Objects.hash(name, score);
    }
    
    
    // 必要的时候,还可以重写tostring()
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", score=" + score +
                '}';
    }
}


枚举 EnumMap

HashMap是一种通过对key计算hashCode(),通过空间换时间的方式,直接定位到value所在的内部数组的索引,因此,查找效率非常高, 但是如果我们的key他是 enum 类型, 其实在这里,我们就可以使用 Java函数库中中的 EnumMap , 它在内部以一个非常紧凑的数组存储value,并且根据enum类型的key直接定位到内部数组的索引,并不需要计算hashCode(),不但效率最高,而且没有额外的空间浪费。

代码如下:

public void fun3() {
        // 枚举对象  泛型擦拭
        Map<DayOfWeek, String> map = new EnumMap<>(DayOfWeek.class);
    
        map.put(DayOfWeek.MONDAY, "星期一");
        map.put(DayOfWeek.TUESDAY, "星期二");
        map.put(DayOfWeek.WEDNESDAY, "星期三");
        map.put(DayOfWeek.THURSDAY, "星期四");
        map.put(DayOfWeek.FRIDAY, "星期五");
        map.put(DayOfWeek.SATURDAY, "星期六");
    	  map.put(DayOfWeek.SUNDAY, "星期日");
    
        for (DayOfWeek dayOfWeek : map.keySet()) {
            String day = map.get(dayOfWeek);
            System.out.println(dayOfWeek + "是" + day);
        }
      
        System.out.println(DayOfWeek.WEDNESDAY + "是" + map.get(DayOfWeek.WEDNESDAY));
    }

排序 TreeMap

我们知道对于map来说, 他是没有顺序而言的, 是一种空间换时间的映射表, 但实际上还有一个map它可以实现sort排序 功能的, 就是SorteMap , 但是实现类是TreeMap。

       ┌───┐
        |  Map  |
       └───┘
          ▲
    ┌────┴─────┐
    │              │
┌───────┐ ┌─────────┐
│HashMap	   | │   SortedMap    │
└───────┘ └─────────┘
               ▲
               │
          ┌─────────┐
          │ TreeMap       │
          └─────────┘

比如我们看一个例子:

当key是String 或者 Integer 类的时候, 这个时候默认是升序排列,因为StringInteger这些类已经实现了Comparable接口,因此可以直接作为Key使用

public class Main {
    public static void main(String[] args) {
        Map<String, Integer> map = new TreeMap<>();
        map.put("orange", 1);
        map.put("apple", 2);
        map.put("pear", 3);
        for (String key : map.keySet()) {
            System.out.println(key + map.get(key));
        }
        
    }
}
// 最后输出 apple 2, orange 1, pear 3

但是如果我们的类自己编写的, 或者我们想改变排序方式呢? 怎么重写?

要实现 Comparator<>() 接口 具体实现,如下:

// 关于 object.compareTo(object) 函数
//  如果前者大于后者  返回 大于0的数
//  如果前者等于后者  返回0
//  如果前者小于后者  返回小于0的数


public void fun4() {
        Map<Student, Integer> map4 = new TreeMap<>(new Comparator<Student>() {
//            @Override // 默认升序排列
//            public int compare(Student o1, Student o2) {
//                return o1.name.compareTo(o2.name);
//            }

            @Override // 重写 降序
            public int compare(Student o1, Student o2) {
                if (o1.name.equals(o2.name)) {
                    return 0;
                } else if (o1.name.compareTo(o2.name) > 0) {
                    return -1;
                } else {
                    return 1;
                }

            }
        });
        map4.put(new Student("Xiao Ming", 99), 1);
        map4.put(new Student("Xiao Gang", 87), 2);
        map4.put(new Student("Xiao Hone", 99), 3);

        //  遍历
        for (Student student : map4.keySet()) {
            String name = student.name;
            System.out.println(name + map4.get(student));
        }

    }

class Student {
    public String name;
    public int score;

    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
}


配置文件Properties:

很多时候,我们需要去读写配置文件,这些配置文件的特点就是 key-value 一般来说都是string-string 的类型, 所以我们完全是可以用 HashMap<String, String> 去实现这样功能

我们使用 Properties 去读取配置文件, 关于配置文件,

Java默认配置文件以.properties为扩展名,所以我们要在当前目录下新建一个 setting.properties 的文件, 然后每行以key=value表示,以#课开头的是注释。如下是最简单的配置文件:

# setting.properties

last_open_file=hello.txt
auto_save_interval=60
username=me

然后怎么读取这个配置文件 方法如下:(这个是调用文件流)


 String f = "D:\\java\\java代码\\package\\listTest\\setting.properties";
Properties props = new Properties();
 props.load(new java.io.FileInputStream(f)); //导入一个文件流
 String filepath = props.getProperty("last_open_file");
String interval = props.getProperty("auto_save_interval", "120");
 System.out.println(filepath);

具体步骤分三步:

  1. 创建Properties实例; as: Properties props = new Properties();
  2. 调用load()读取文件; as: props.load(inputSteam) 可以文件流 字节流 也可以资源流
  3. 调用getProperty()获取配置。

我们试试一个字节流的调用方法: (使用字节流,要注意每一个key-value 要有换行)

public void fun5() throws IOException {
       /*读取字节流*/

       String settings = "# test \n course=Java \n last_open_date=2019-08-07T12:35:01" +
               "\n username=shalouzaixyua";
       ByteArrayInputStream input = new ByteArrayInputStream(settings.getBytes("UTF-8"));
       // 创建一个Properties实例
       Properties props = new Properties();
       // 调用load读取一个字节流
       props.load(input);
       // 调用getProperty获得配置文件
       System.out.println("course: " + props.getProperty("course"));
       System.out.println("last_open_date: " + props.getProperty("last_open_date"));
       System.out.println("last_open_file: " + props.getProperty("last_open_file"));
       System.out.println("auto_save: " + props.getProperty("auto_save", "60"));
       System.out.println("The author:" + props.getProperty("username"));
       // 写入一个新的配置文件
       props.setProperty("id", "888888888");
       // 重新读取这个配置文件
       System.out.println("The id:" + props.getProperty("id"));


   }

集合Set:

我们知道map是用来存储不重复的key-value 映射对, 那如果我们只是想保存不重复的key呢? 我们可以直接用Set,它可以用于存储不重复的元素集合,类似python中的set集合。

主要方法如下:

  • 将元素添加进Set<E>boolean add(E e)
  • 将元素从Set<E>删除:boolean remove(Object e)
  • 判断是否包含元素:boolean contains(Object e)

简单例子:

Set<String> set = new HashSet<>();
set.add("abc"); 
set.remove("abc");
set.contains("abc");  // false

因为set只是存储key的一个集合,所以我们可以用这个达到去重的效果, 这里要注意一点,放入set的key类都必须正确实现equals() 方法 和 hashcode() 方法。

我们的set接口用一张图表示:

			Set
			|
	 ________________
	|			   	|
HashSet        SorteSet
					 |
				 TreeSet
  • HashSet是无序的,因为它实现了Set接口,并没有实现SortedSet接口;
  • TreeSet是有序的,因为它实现了SortedSet接口。

如果我们随意的输出Hashset中的元素

public class Main {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<>();
        set.add("apple");
        set.add("banana");
        set.add("pear");
        set.add("orange");
        for (String s : set) {
            System.out.println(s);
        }
    }
}
//  顺序是随意的  不确保
//  banana pear apple orange

如果我们输出TreeSet中的元素

public class Main {
    public static void main(String[] args) {
        Set<String> set = new TreeSet<>();
        set.add("apple");
        set.add("banana");
        set.add("pear");
        set.add("orange");
        for (String s : set) {
            System.out.println(s);
        }
    }
}


//  apple  banana orange pear  A-Z的升序
// 如果要更改排序 需要重写tree中的compare方法 

练习二种去重方法:

package listTest;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.*;

public class Set {
    public static void main(String[] args) throws IOException {
        List<Message> received = List.of(
                new Message(1, "Hello!"),
                new Message(2, "你在干嘛?"),
                new Message(2, "你在干嘛啊啊?"),
                new Message(3, "去哪吃饭?"),
                new Message(3, "去哪吃饭?"),
                new Message(4, "滚")
        );
        List<Message> displayMessages = process(received); // 调用函数
        for (Message message : displayMessages) {
            System.out.println(message.text);
        }
        fun();
    }

    static List<Message> process(List<Message> received) {
        // TODO:  第一种方式
//        HashSet<Integer> set = new HashSet<>();
//        ArrayList<Message> newList = new ArrayList<>();
//        for(Message message:received){
//            // 如果不存在 就放得进去
//            if(set.add(message.sequence)){
//                newList.add(message);
//            }
//        }
//
//        return newList;
//    }   
        // reveived 是一个包含重复sequence的集合list
        
        TreeSet<Message> set = new TreeSet<>(new Comparator<Message>() {
            @Override
            //TODO:  按照sequence 达到去重效果 第二种方式
            public int compare(Message o1, Message o2) {
                return o1.sequence > o2.sequence ? 1 : (o1.sequence < o2.sequence ?
                        -1 : 0);
            }
        });
        for(Message message:received){
            set.add(message);
        }
        return new ArrayList<Message>(set);
     }
    
}


class Message {
     public final int sequence;
     public final String text;

     public Message(int sequence, String text) {
         this.sequence = sequence;
         this.text = text;
     }

    @Override
    public String toString() {
         return sequence + ":" + text;
    }
}

队列Queue:

队列(Queue)是一种经常使用的集合。Queue实际上是实现了一个先进先出(FIFO:First In First Out)的有序表。它和List的区别在于,List可以在任意位置添加和删除元素,而Queue只有两个操作:

  • 把元素添加到队列末尾;
  • 从队列头部取出元素。

queue有如下方法:

  • int size():获取队列长度;
  • boolean add(E)/boolean offer(E):添加元素到队尾;
  • E remove()/E poll():获取队首元素并从队列中删除;
  • E element()/E peek():获取队首元素但并不从队列中删除。

除了size() 只有一种方式, 其他方法都有二种, 二种的操作都是一样的, 但是对异常的处理情况不一样。

方法 throw Exception 返回false或null
添加元素到队尾 add(E e) boolean offer(E e)
取队首元素并删除 E remove() E poll()
取队首元素但不删除 E element() E peek()
这三个都会抛出异常 不会抛出异常

这些我们根据源码得到理解,附上一段源码,理解一下

/**
     * Retrieves, but does not remove, the head of this queue.  This method
     * differs from {@link #peek peek} only in that it throws an exception
     * if this queue is empty.
     *
     * @return the head of this queue
     * @throws NoSuchElementException if this queue is empty
     */
    E element();  // 上面是对element 的描述

    /**
     * Retrieves, but does not remove, the head of this queue,
     * or returns {@code null} if this queue is empty.
     *
     * @return the head of this queue, or {@code null} if this queue is empty
     */
    E peek();  // 上面是对peek 的描述

简单的实现方法:

public class queue {
    public static void main(String[] args) {
        Queue<String> queue = new LinkedList<>();
        // 二种添加方法 向尾部添加
        queue.add("I am first means ");
        queue.offer("I am second means");
        // 二种取出但不删除 从头部取出 所以每次都是头部信息
        String s1 = queue.element();
        String s2 = queue.peek();
        System.out.println(s1 + "\n" + s2);
        // 二种取出删除方法, 从头部删除
        queue.remove();
        queue.poll();
        // 获取长度 全删掉了 没了
        System.out.println(queue.size());
    }
}

优先队列PriorityQueue:

之前使用的Queue队列。他是严格规定 “先进先出” 的规则,没有优先权限, 这会影响我们一些实际开发, 比如存在VIP用户的基础上, 我们就需要使用PriorityQueue 优先队列,来处理这件事情了。

先来看看一个简单的队列方法:

public class Main {
    public static void main(String[] args) {
        Queue<String> q = new PriorityQueue<>();
        // 添加3个元素到队列:
        q.offer("apple");
        q.offer("pear");
        q.offer("banana");
        System.out.println(q.poll()); // apple
        System.out.println(q.poll()); // banana
        System.out.println(q.poll()); // pear
        System.out.println(q.poll()); // null,因为队列为空
    }
}
//  默认String类实现了 Comparable 接口, 排序默认升序

那如果我们放入的元素没有实现 Comparable接口呢,PriorityQueue 允许我们提供一个 comparator 对象来判断优先级,这里有一个例子:

package listTest;

import java.util.*;

public class queue {
    public static void main(String[] args) {
        Queue<User> q = new PriorityQueue<>(new UserComparator());
        // 添加3个元素到队列:
        q.offer(new User("Bob", "A10"));
        q.offer(new User("Alice", "A2"));
        q.offer(new User("lucy", "A6"));
        q.offer(new User("Boss", "V1"));
        System.out.println(q.poll());
        System.out.println(q.poll());
        System.out.println(q.poll());
        System.out.println(q.poll());
        System.out.println(q.size());

    }
}


class UserComparator implements Comparator<User> {
    @Override
    public int compare(User u1, User u2) {
        if (u1.number.charAt(0) == u2.number.charAt(0)) {
            // 如果两人的号都是A开头或者都是V开头,比较号的大小:
            if (u1.number.length() == u2.number.length()) {
                return u1.number.compareTo(u2.number);
            } else {
                return (u1.number.length() > u2.number.length() ? 1 : -1); //  如果长度不相同 返回小号
            }
        }
        if (u1.number.charAt(0) == 'V') {
            return -1;
            // u1的号码是V开头,优先级高:
        } else {
            return 1;
        }
    }
}

class User {
    public final String name;
    public final String number;

    public User(String name, String number) {
        this.name = name;
        this.number = number;
    }

    @Override
    public String toString() {
        return "现在是客户:" + name + "// 权限为:" + number;
    }
}



小提示:

PriorityQueue实现了一个优先队列:从队首获取元素时,总是获取优先级最高的元素。

PriorityQueue默认按元素比较的顺序排序(必须实现Comparable接口),也可以通过Comparator自定义排序算法(元素就不必实现Comparable接口)。


双向队列 Deque:

不管是Queue队列,还是PriorityQueue 优先队列, 他们都遵循一个原则, 只能从尾部向队列进行增加,从头部取出元素,但是我们可以从Queue中派生出一个 Deque 双向队列,它可以允许我们从二端添加元素,并且可以从二段取出元素。

可以看一下Deque的简单使用:


public static void main(String[] args) {
        Deque<String> deque = new LinkedList<>();
        // 向二头加入元素
        deque.offerFirst("first");
        deque.offerLast("last");
        // 向二头删除元素
        System.out.println(deque.pollFirst());
        System.out.println(deque.pollLast());
		    //  队列长度   
        System.out.println(deque.size());

    }

我们来看一下 Deque 和 Queue的 一些区别:

Means Queue Deque
添加元素到队尾 add(E e) / offer(E e) addLast(E e) / offerLast(E e)
取队首元素并删除 E remove() / E poll() E removeFirst() / E pollFirst()
取队首元素但不删除 E element() / E peek() E getFirst() / E peekFirst()
添加元素到队首 addFirst(E e) / offerFirst(E e)
取队尾元素并删除 E removeLast() / E pollLast()
取队尾元素但不删除 E getLast() / E peekLast()

Collections类:

Collections 给我们提供了一些方法. 比如对集合的排序,扰乱等。

排序

public class Main {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("apple");
        list.add("pear");
        list.add("orange");
        // 排序方法
        Collections.sort(list);  
        // 排序后:
        System.out.println(list);
    }
}

提到排序,这里需要继续排序扩展,一般我们使用的是Arrays.sort() 对数组进行排序, 但是这个排序默认升序, 如果需要进行改写排序规则,怎么办呢?

  • 第一种 使用 Collections.reverseOrder() 方法

 public static void main(String[] args) {
     Integer[] mylist = {2, 10, 4, 1, 3, 5};
    Arrays.sort(mylist, Collections.reverseOrder());
     System.out.println(Arrays.toString(mylist));
   
 }
  • 第二种 重写 Comparator接口中的compare方法
   public static void main(String[] args) {
        Integer[] mylist = {2, 10, 4, 1, 3, 5};
       //  创建一个Comparator 类
        Comparator cmp = new CMP();
        Arrays.sort(mylist, cmp);
        System.out.println(Arrays.toString(mylist));
    }


}

class CMP implements Comparator<Integer> {

    @Override
    public int compare(Integer o1, Integer o2) {
        return o1 > o2 ? -1 : 1;
    }
}

洗牌打乱

Collections 提供了一个 洗牌算法 shuffle() 可以把集合类的顺序随机打乱。 如下:

public class Main {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        for (int i=0; i<10; i++) {
            list.add(i);
        }
        // shuffle 算法之后
        Collections.shuffle(list);
        // 洗牌后:
        System.out.println(list);
    }
}

本篇文章是一个学习java的笔记,概括集合中的 list ,Arraylist, HashMap, EnumMap, TreeMap, Properties, Set, Queue , PriorityQueue, Deque, Collections 的基本用法, 以及集合的各种重写方法手段,当个字典吧!

猜你喜欢

转载自blog.csdn.net/qq_45906219/article/details/108360403