① 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