Java之三大集合

本文深入探讨了Java中的三大集合——List、Map和Set,重点关注ArrayList、HashMap和HashSet。ArrayList基于可变数组,适合快速查找;Vector线程安全但速度较慢;LinkedList适用于频繁插入和删除。此外,文章还介绍了集合与数组的转换、堆栈和队列的用法,以及Map中的ConcurrentLinkedQueue和LinkedBlockingQueue。在Set集合中,HashSet通过hashCode()和equals()确保元素唯一性,TreeSet则根据比较器进行排序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


①  List集合

1.1  ArrayList

ArrayList 集合: 可变数组,通过角标操作

增删改查数据

泛型的使用

迭代器的遍历

 

集合特点 :

大小不确定、可以存放多种类型、移除数据方便

public class ArrayListDemo {
    public static void main(String[] args) {
        List list = new ArrayList();

        // 增
        list.add(123);
        list.add('A');
        list.add(true);

        // 删
        list.remove(true);
        //当所删除的内容为int类型值时
        Integer i = 123;
        list.remove(i);

        // 改
        list.set(0, "BBBBB");
        System.out.println(list);

        // 查
        System.out.println(list.size());
        System.out.println(list.contains(123));
        System.out.println(list.get(0));

        //查找某个元素在集合中的位置,找不到-1
        System.out.println(list.lastIndexOf('A'));
        System.out.println("---------------------");

        // 遍历
        List list1 = new ArrayList();
        list1.add(123);
        list1.add('A');
        list1.add(true);
        list1.add("我擦..");
        System.out.println(list1);
        System.out.println("--------------");

        for (int j = 0; j < list1.size(); j++) {
            System.out.println(list1.get(j));
        }
        System.out.println("--------------");

        for (Object o : list1) {
            System.out.println(o);
        }
        System.out.println("--------------");

        // 迭代遍历
        Iterator it = list1.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}


public class Demo_Array {
    public static void main(String[] args) {
        List
         
           list = new ArrayList
          
           (); list.add("数据111"); list.add("数据222"); list.add("hello"); list.add("hello"); list.add("hello"); list.add("hello"); list.add("hello"); list.add("数据444"); list.add("数据555"); list.add("hello"); list.add("hello"); list.add("hello"); System.out.println(list); // 一边遍历,一边移除 /* * for循环不行 */ for (int i = 0; i < list.size(); i++) { System.out.println(list.size()); if (list.get(i).equals("hello")) { list.remove(i); } } // 一边遍历,一边移除必须使用迭代器,而且必须使用迭代器的remove() Iterator
           
             it = list.iterator(); while (it.hasNext()) { // 记得先next,再remove String s = it.next(); // 迭代器的remove---移除当前遍历出的元素 if (s.equals("hello")) { it.remove(); } } System.out.println(list); } } public class ListTest { //ArrayList添加对象时去重 对象必须复写equals()方法 public static void main(String[] args) { List
            
              persons=new ArrayList<>(); Person xiaoming=new Person("小明", 11); for (int i = 0; i < 10; i++) { if (!persons.contains(xiaoming)) { persons.add(new Person("小明", 11)); } } System.out.println(persons.toString()); } public class Person { private String name; private int age; public Person(String name, int age) { super(); this.name = name; this.age = age; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) 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 String toString() { return "Person [name=" + name + ", age=" + age + "]"; } } }   
             

 

1.2  Vector

/*

*

* ArrayList:速度快    安全性低

*           ArrayList动态增加数组大小为原来数组大小的1.5倍+1

*           加1是为了当数组大小为1时也能根据该流程将数组大小扩充到2

*

* Vector:   速度慢   安全性高

*          vector当增加数组大小时是默认扩展一倍

*          多一种遍历方式--枚举器

*/

 

    public static void main(String[] args) {
        Vector
                  
                    v = new Vector
                   
                    ();
        v.add("xxxx1");
        v.add("xxxx2");
        v.add("xxxx3");
        v.add("xxxx4");
        v.add("xxxx5");
        v.set(2, "qqq");
        v.remove(2);

        System.out.println(v);
        System.out.println(v.size());
        System.out.println(v.isEmpty());
        System.out.println(v.indexOf("xxxx1"));
        System.out.println(v.get(3));
        System.out.println(v.contains("xxxx51"));
        v.clear();
        System.out.println(v);

    }


    public static void main(String[] args) {

        Vector
                    
                      v = new Vector
                     
                      ();
        v.add("xxxx1");
        v.add("xxxx2");
        v.add("xxxx3");
        v.add("xxxx4");
        v.add("xxxx5");

        //遍历方式

        for (int i = 0; i < v.size(); i++) {
            System.out.println(v.get(i));
        }

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

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

        //枚举器
        Enumeration
                       
                         en = v.elements(); while (en.hasMoreElements()) { System.out.println(en.nextElement()); } }
                       
                      
                     
                    
                   
                  

  

 

1.3  LinkedList

/*

 * LinkedList:底层结构是双向链表 

*            插入,删除很快,查找很慢

*

*ArrayList:底层结构是数组

*          插入,删除较慢,查找很快

*

*/

 

    public static void main1(String[] args) {
        LinkedList
                  
                    list = new LinkedList
                   
                    ();
        list.add("xxx1");
        list.add("xxx2");
        list.add("xxx3");
        list.add("xxx4");
        list.add("xxx5");
        list.set(2, "qqqq");
        list.remove(2);
        System.out.println(list);

        System.out.println(list.size());
        System.out.println(list.isEmpty());
        System.out.println(list.get(3));
        System.out.println(list.indexOf("xxx2"));
        System.out.println(list.contains("xxx2"));

    }


    public static void main2(String[] args) {
        //LinkedList
                    
                     list=new LinkedList
                     
                      ();
        ArrayList
                      
                        list = new ArrayList
                       
                        (); list.add("xxx1"); list.add("xxx2"); list.add("xxx3"); list.add("xxx4"); list.add("xxx5"); //测试简单操作100W条数据所需的时间(可与LinkedList比较 ) long begin = System.currentTimeMillis(); for (int i = 0; i < 1000000; i++) { list.add(1, "new" + i); } long end = System.currentTimeMillis(); System.out.println(end - begin); } public static void main3(String[] args) { LinkedList
                        
                          list = new LinkedList
                         
                          (); //倒着往里压入 list.push("xxx1"); list.push("xxx2"); list.push("xxx3"); list.push("xxx4"); list.push("xxx5"); list.addFirst("大头"); list.addLast("尾巴"); list.push("xx6"); //移除头节点 list.pop(); list.pop(); System.out.println(list); } public static void main(String[] args) { LinkedList
                          
                            list = new LinkedList
                           
                            (); list.push("xxx1"); list.push("xxx2"); list.push("xxx3"); list.push("xxx4"); list.push("xxx5"); while (list.size() != 0) { System.out.println(list.pop()); } }
                           
                          
                         
                        
                       
                      
                     
                    
                   
                  


 

1.4  集合与数组的转换

//集合---->数组

List<String>list1 = new ArrayList<String>();

list1.add("数据111");

list1.add("数据222");

list1.add("数据333");

list1.add("数据444");

list1.add("数据555");

 

String[] strs=new String[list1.size()];

list1.toArray(strs);

System.out.println(Arrays.toString(strs));

 

//数组---->集合

public static void main(String[]  args) {

String[] strs={"数据111","数据222","数据333","数据444","数据555"};

List<String>list=Arrays.asList(strs);

      

ArrayList<String> as=new ArrayList<String>(list);

System.out.println(list);

System.out.println(list.size());

list.set(3,"qqqq");

      

System.out.println(list.get(3));

System.out.println(as);

as.add("xxx");

System.out.println(as);

as.remove("xxx");

System.out.println(as);

}

 

数组-->集合  转换完能查能改不能加、删  想要能加能删必须再用一个ArrayList集合去承接

ArrayList<String> as=new ArrayList<String>(list);

 

1.5  堆栈类

 /*

* 堆栈:FILO 先进后出

*

* 队列:FIFO 先进先出

*

 */

public class DemoStack{

public static void main(String[] args) {

Stack<String> stack=new Stack<String>();//创建一个空stack

stack.push("aaa1");       //把项压入栈顶

stack.push("aaa2");

stack.push("aaa3");

stack.push("aaa4");


while(stack.size()>0){

System.out.println(stack.pop());    //移除栈顶对象,并作为函数的值返回该对象

}

}

}



1.6  队列

Queue接口与List、Set同一级别,都是继承了Collection接口。LinkedList实现了Queue接口。Queue接口窄化了对LinkedList的方法的访问权限即在方法中的参数类型如果是Queue时,就完全只能访问Queue接口所定义的方法了,而不能直接访问 LinkedList的非Queue的方法。


public class QueueTest {
	public static void main(String[] args) {
		Queue
                 
                   queue=new LinkedList<>();
		//offer 添加一个元素并返回true 如果队列已满,则返回false
		queue.offer("1");
		queue.offer("2");
		queue.offer("3");
		queue.offer("4");
		queue.offer("5");
		System.out.println(queue.toString());
		
		//element 返回队列头部的元素    如果队列为空,则抛出一个NoSuchElementException异常
		String aaa=queue.element();
		
		//peek 返回队列头部的元素   如果队列为空,则返回null
		String bbb=queue.peek();
		
		//poll 移除并返问队列头部的元素    如果队列为空,则返回null
		String ccc=queue.poll();
		
		//remove 移除并返回队列头部的元素    如果队列为空,则抛出一个NoSuchElementException
		String ddd=queue.remove();
		
		System.out.println("aaa:"+aaa +"\tbbb:"+bbb+"\tccc:"+ccc+"\tddd:"+ddd);
		
		System.out.println(queue.toString());
		
		//阻塞操作
		//put  添加一个元素     如果队列满,则阻塞
		//take 移除并返回队列头部的元素    如果队列为空,则阻塞
	}
	
}
                 


并发队列ConcurrentLinkedQueue和阻塞队列LinkedBlockingQueue用法

Java提供的线程安全的Queue可以分为阻塞队列和非阻塞队列,其中阻塞队列的典型例子是BlockingQueue,非阻塞队列的典型例子是ConcurrentLinkedQueue,在实际应用中要根据实际需要选用阻塞队列或者非阻塞队列。
由于LinkedBlockingQueue实现是线程安全的,实现了先进先出等特性,是作为生产者消费者的首选,LinkedBlockingQueue 可以指定容量,也可以不指定,不指定的话,默认最大是Integer.MAX_VALUE,其中主要用到put和take方法,put方法在队列满的时候会阻塞直到有队列成员被消费,take方法在队列空的时候会阻塞,直到有队列成员被放进来。

/**
 * 多线程模拟实现生产者/消费者模型
 */
public class BlockingQueueTest{
    /**
     * 
     * 定义装苹果的篮子
     * 
     */
    public class Basket {
        // 篮子,能够容纳3个苹果
        BlockingQueue
                 
                   basket = new LinkedBlockingQueue
                  
                   (3);

        // 生产苹果,放入篮子
        public void produce() throws InterruptedException {
            // put方法放入一个苹果,若basket满了,等到basket有位置
            basket.put("An apple");
        }

        // 消费苹果,从篮子中取走
        public String consume() throws InterruptedException {
            // take方法取出一个苹果,若basket为空,等到basket有苹果为止(获取并移除此队列的头部)
            return basket.take();
        }
    }

    // 定义苹果生产者
    class Producer implements Runnable {
        private String instance;
        private Basket basket;

        public Producer(String instance, Basket basket) {
            this.instance = instance;
            this.basket = basket;
        }

        public void run() {
            try {
                while (true) {
                    // 生产苹果
                    System.out.println("生产者准备生产苹果:" + instance);
                    basket.produce();
                    System.out.println("!生产者生产苹果完毕:" + instance);
                    // 休眠300ms
                    Thread.sleep(300);
                }
            } catch (InterruptedException ex) {
                System.out.println("Producer Interrupted");
            }
        }
    }

    // 定义苹果消费者
    class Consumer implements Runnable {
        private String instance;
        private Basket basket;

        public Consumer(String instance, Basket basket) {
            this.instance = instance;
            this.basket = basket;
        }

        public void run() {
            try {
                while (true) {
                    // 消费苹果
                    System.out.println("消费者准备消费苹果:" + instance);
                    System.out.println(basket.consume());
                    System.out.println("!消费者消费苹果完毕:" + instance);
                    // 休眠1000ms
                    Thread.sleep(1000);
                }
            } catch (InterruptedException ex) {
                System.out.println("Consumer Interrupted");
            }
        }
    }

    public static void main(String[] args) {
        BlockingQueueTest test = new BlockingQueueTest();

        // 建立一个装苹果的篮子
        Basket basket = test.new Basket();

        ExecutorService service = Executors.newCachedThreadPool();
        Producer producer = test.new Producer("生产者001", basket);
        Producer producer2 = test.new Producer("生产者002", basket);
        Consumer consumer = test.new Consumer("消费者001", basket);
        service.submit(producer);
        service.submit(producer2);
        service.submit(consumer);

    }

}
                  
                 

ConcurrentLinkedQueue
ConcurrentLinkedQueue是Queue的一个安全实现.Queue中元素按FIFO原则进行排序.采用CAS操作,来保证元素的一致性。

public class ConcurrentLinkedQueueTest {
    private static ConcurrentLinkedQueue
                   
                     queue = new ConcurrentLinkedQueue
                    
                     ();
    private static int count = 2; // 线程个数
    //CountDownLatch,一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
    private static CountDownLatch latch = new CountDownLatch(count);

    public static void main(String[] args) throws InterruptedException {
        long timeStart = System.currentTimeMillis();
        ExecutorService es = Executors.newFixedThreadPool(4);
        ConcurrentLinkedQueueTest.offer();
        for (int i = 0; i < count; i++) {
            es.submit(new Poll());
        }
        latch.await(); //使得主线程(main)阻塞直到latch.countDown()为零才继续执行
        System.out.println("cost time " + (System.currentTimeMillis() - timeStart) + "ms");
        es.shutdown();
    }
    
    /**
     * 生产
     */
    public static void offer() {
        for (int i = 0; i < 100000; i++) {
            queue.offer(i);
        }
    }


    /**
     * 消费
     */
    static class Poll implements Runnable {
        public void run() {
            // while (queue.size()>0) {
            while (!queue.isEmpty()) {
                System.out.println(queue.poll());
            }
            latch.countDown();
        }
    }
}

ConcurrentLinkedQueue的API中.size()是要遍历一遍集合的,非常慢,所以尽量要避免用size而改用isEmpty().
                    
                   

 

②   Map集合

HashMap Hashtable TreeMap区别
1、HashMap:允许null键null值Hashtable:不允许null键null值TreeMap:允许null值不允许null键
2、TreeMap的输出会按照key默认排序(根据ASCII码表)
3、HashMap非同步处理  速度快 不安全     Hashtable   同步处理速度慢 安全
Hashtable并发性不如ConcurrentHashMap,不需要线程安全的场合可以用HashMap替换,需要线程安全的场合可以用ConcurrentHashMap替换。
4、HashMap底层结构是数组+链表+红黑树(JDK1.8加入),存入键值对时会先算出一个hashcode(),存进去,当另外一个键值对的hashcode()值与它相同时,就会用Entry(壳子)对它们进行封装加以区别,新添加进来的放在旧的前面<Entry2,Entry1>---通过链表索引来取,当链表长度太长(默认超过8)时,链表就转换为红黑树,利用红黑树快速增删改查的特点提高HashMap的性能。HashMap初始化长度length(默认值是16),Loadfactor为负载因子(默认值是0.75),其扩充原理请自行查看源码。
5、HashMap的输出是无序的,被存入到 HashMap 中的元素,在遍历 HashMap 时,其输出是无序的。如果希望元素保持输入的顺序,可以使用LinkedHashMap替代。LinkedHashMap 继承自 HashMap,具有高效性,同时在 HashMap 的基础上,又在内部增加了一个链表,用以存放元素的顺序。LinkedHashMap是根据元素增加或者访问的先后顺序进行排序,而 TreeMap 则根据 元素的 Key 进行排序。

    public class Demo1 {
        public static void main(String[] args) {
            Map
                  
                    map = new HashMap
                   
                    ();
            //添加
            map.put("a1", 123);
            map.put("a2", "中");
            map.put("a3", 'A');
            map.put("a3", 'B');//新值会覆盖旧值
            map.put("a4", true);
            //允许null键null值
            map.put("a5", null);
            map.put(null, 666);

            System.out.println(map);
            System.out.println(map.size());
            System.out.println(map.isEmpty());
            System.out.println(map.containsKey("a1"));

            //移除
            map.remove("a2");
            map.clear();//全清
            System.out.println(map);

            //遍历
            //1、通过keyset()方法获取所有的key,再通过key去获取value;
            Set
                    
                      keys = map.keySet();
            Iterator
                     
                       it = keys.iterator();
            while (it.hasNext()) {
                String key = it.next();
                System.out.println("key----->" + key);
                System.out.println("value--->" + map.get(key));
                System.out.println("---------");
            }

            //2、增强for循环keyset();
            Set
                      
                        keys = map.keySet();
            for (String k : keys) {
                System.out.println("key----->" + k);
                System.out.println("value--->" + map.get(k));
                System.out.println("#########");
            }

            //3、通过Entry(相当于一层外壳)获取封装好的key和value
            Set
                       
                        
                         > entrys = map.entrySet(); Iterator
                         
                          
                           > it1 = entrys.iterator(); while (it1.hasNext()) { Map.Entry
                           
                             en = it1.next(); System.out.println("key----->" + en.getKey()); System.out.println("value--->" + en.getValue()); System.out.println("========="); } //4、增强for循环Entry Set
                            
                             
                              > entrys1 = map.entrySet(); for (Map.Entry
                              
                                e : entrys1) { System.out.println("key----->" + e.getKey()); System.out.println("value--->" + e.getValue()); System.out.println("+++++++++"); } } }
                              
                             
                            
                           
                          
                         
                        
                       
                      
                     
                    
                   
                  



③   Set集合

3.1  HashSet

/*

* Set :

*1.set中没有角标

*2.set中的元素不允许重复,相同元素只会输出1个

*3.TreeSet有自己的排序,当放的是对象时,排序对象要实现比较器Comparable

*4.HashSet的底层数据结构是HashMap,HashSet中插入数据时的顺序和输出数据时的顺序无关。

*/

  public class SetDemo {
        public static void main(String[] args) {
            Set set = new HashSet();
            set.add("第一个");
            set.add("No2");
            set.add(3333);
            set.add(3333);    //set中不允许重复元素 输出只有一个
            set.add('4');
            set.add(true);
            set.add(666.666);
            System.out.println(set);
            System.out.println("-------------------");

            System.out.println(set.size());
            System.out.println(set.contains(666));
            System.out.println(set.isEmpty());
            set.clear();
            System.out.println(set);

            // 遍历
            for (Object s : set) {
                System.out.println(s);
            }

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

        }
    }


    //Set中也可以存放对象
    public static void main(String[] args) {
        Set
                  
                    set = new HashSet
                   
                    ();
        set.add(new Person("张三", 12));
        set.add(new Person("张三", 12));
        set.add(new Person("张三", 12));
        set.add(new Person("张四", 13));
        set.add(new Person("张五", 14));
        set.add(new Person("张六", 16));
        set.add(new Person("张七", 17));

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


    public static class Person{
        private String name;
        private int age;

        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }

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

 

覆写toString后的输出结果

Person [name=张四, age=13]

Person [name=张七, age=17]

Person [name=张六, age=16]

Person [name=张三, age=12]

Person [name=张三, age=12]

Person [name=张三, age=12]

Person [name=张五, age=14]

 

Set中的元素是不允许重复的,输出时只会出现一个,此时出现三个[name=张三,age=12],是因为集合里放的是对象,HashSet没有覆写hashCode()equals()。

 

HashSet的查重规则

1、hashCode():通常会拿着对象的各个属性值运算出一个得数 ---如果2个对象得数不相等,那么2个对象的属性必然不全部相同。

先使用hashCode()方法进行比较,如果hashCode()值不相等,则2个对象可以直接判定不等但是,如果hashCode()值相等,却不能判断2个对象一定相等,进而再去用equals方法比较各个属性值

2、equals():比较2个对象相等----复写之后就是在比较2个对象的各个属性值是否全都相等,如果equals()判定为不等,即为不等,判定为相等,最终才相等。


之所以这样做,是为了提高集合的使用效率

 

public class Person  {
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.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;
		Person other = (Person) 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 String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

    public static void main(String[] args) {
        Set
                
                  set = new HashSet
                 
                  ();
        set.add(new Person("张三", 12));
        set.add(new Person("张三", 12));
        set.add(new Person("张三", 12));
        set.add(new Person("张四", 13));
        set.add(new Person("张五", 14));
        set.add(new Person("张六", 16));
        set.add(new Person("张七", 17));

        Iterator
                  
                    it = set.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
    
    
    输出结果:
    	Person{name='张四', age=13}
    	Person{name='张三', age=12}
    	Person{name='张六', age=16}
    	Person{name='张七', age=17}
    	Person{name='张五', age=14}
                  
                 
                

 

 

3.2  TreeSet

public static void main(String[] args) {
    /*
     * TreeSet中的元素为对象时,排序必须实现比较器接口
     */
    TreeSet
                  
                    ts = new TreeSet
                   
                    ();
    ts.add(newPerson("aaa", 20));
    ts.add(newPerson("aaa", 27));
    ts.add(newPerson("aaa", 35));
    ts.add(newPerson("bbb", 20));
    ts.add(newPerson("aaa", 22));
    System.out.println(ts);
   }

public class Person implements Comparable
                    
                     {
   String name;
   int age;
   public Person(String name, int age) {
    super();
    this.name= name;
    this.age= age;
   }
  
   @Override
   public String toString() {
    return"Person [name=" + name + ", age=" + age + "]";
   }

   @Override
   public int compareTo(Person o) {
    if(this.age>o.age){
        return 123;
       
    }else if(this.age
                     
                    
                   
                  


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值