集合
一、简介
动态保存任意多个对象
集合两组:单列集合,双列集合
Collection接口有两个重要的子接口 List Set,实现的子类都是单列集合
二、Collection 接口和常用方法
- Collection实现子类可以存放多个元素,每个元素可以是Object
- Collection的实现类,有些可以存放重复的元素,有些不可以
- Collection的实现类,有些事有序的(List),有些不是有序的(Set)
- Collection接口没有直接的实现子类,是通过它的子接口Set和List来实现的
接口方法,以ArrayList举例
public class Collection_ {@SuppressWarnings({"all"})public static void main(String[] args) {List list = new ArrayList();// add 添加单个元素list.add("fff");list.add(true);list.add(14.23);System.out.println(list); // [fff, true, 14.23]// remove 删除指定元素list.remove(0); // 删除第一个元素System.out.println(list); // [true, 14.23]list.remove(true); // 删除指定元素System.out.println(list); // [14.23]// contains 查找元素是否存在list.add("fff");list.add(true);System.out.println(list); // [14.23, fff, true]System.out.println(list.contains(14.23)); // true// size 获取元素个数System.out.println(list.size()); // 3// isEmpty 判断是否为空System.out.println(list.isEmpty()); // false// clear 清空list.clear();System.out.println(list); // []// addAll 添加多个元素ArrayList list1 = new ArrayList();list1.add("ggg");list1.add("1223");list.addAll(list1);System.out.println(list); // [ggg, 1223]// containsAll 查找多个元素是否都存在System.out.println(list.containsAll(list1)); // true// removeAll 删除多个元素list.add(1111);list.removeAll(list1);System.out.println(list); // [1111]}}
三、Collection接口遍历元素方式
- Iterator迭代器遍历
Iterator对象称为迭代器,用于遍历集合中的元素
所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象,即可以返回一个迭代器
Iterator仅用于遍历集合,本身不存放对象
- 执行原理
Iterator iterator = collection.iterator(); // 得到一个集合的迭代器
// hasNext() 判断是否还有下一个元素
while(iterator.hasNext()){
//next() 后移 将后移以后集合位置上的元素返回
}
System.out.println(iterator.next());
public class CollectionIterator {@SuppressWarnings({"all"})public static void main(String[] args) {Collection collection = new ArrayList();collection.add(new Book("三国" ,"罗贯中",10));collection.add(new Book("aaa","a",52));collection.add(new Book("tt","as",25.2));// 得到collection对应的迭代器Iterator iterator = collection.iterator();// 使用while循环遍历while(iterator.hasNext()){// 判断是否还有数据// 返回下一个元素,类型是ObjectObject obj = iterator.next();System.out.println(obj);}// 当退出while循环后,iterator迭代器,指向最后的元素// 如果再次遍历,重置迭代器iterator = collection.iterator();}}class Book{private String name;private String author;private double price;public Book(String name, String author, double price) {this.name = name;this.author = author;this.price = price;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAuthor() {return author;}public void setAuthor(String author) {this.author = author;}public double getPrice() {return price;}public void setPrice(double price) {this.price = price;}@Overridepublic String toString() {return "Book{" +"name='" + name + '\'' +", author='" + author + '\'' +", price=" + price +'}';}}
- 增强for循环
for (Object obj:collection){System.out.println(obj);}
四、List接口和常用方法
List接口Collection接口的子接口
- List集合类中元素有序,即添加顺序和取出顺序一致,且可重复
- List集合中的每个元素都有其对应的顺序索引,即支持索引
- List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素
- 常用的类有ArrayList、LinkedList和Vector
public class List_ {@SuppressWarnings({"all"})public static void main(String[] args) {List list = new ArrayList();list.add("aaa");list.add("ccc");list.add("cxx");System.out.println(list); // [aaa, ccc, cxx]System.out.println(list.get(1)); // ccc 索引从0开始}}
public class ListMethod {@SuppressWarnings({"all"})public static void main(String[] args) {List list = new ArrayList();list.add("苹果");list.add("鸡蛋");list.add("aaa");// void add(int index, Object ele):在 index 位置插入 ele 元素list.add(1,"abc"); // [苹果, abc, 鸡蛋, aaa] 在位置1加入abc// boolean addAll(int index, Collection eles):从 index 位置开始将 eles 中的所有元素添加进来List list1 = new ArrayList();list1.add("ooo");list1.add(222);list.addAll(list1);System.out.println(list); // [苹果, abc, 鸡蛋, aaa, ooo, 222]// Object get(int index):获取 index 位置的元素System.out.println(list.get(2)); // 鸡蛋// int indexOf(Object obj):返回 obj 在集合中首次出现的位置System.out.println(list.indexOf("ooo")); // 4// int lastIndexOf(Object obj):返回 obj 在当前集合中末次出现的位置list.add("ooo");System.out.println(list.lastIndexOf("ooo")); // 6// Object remove(int index):移除指定 index 位置的元素,并返回此元素list.remove(0);System.out.println(list); // [abc, 鸡蛋, aaa, ooo, 222, ooo]// Object set(int index, Object ele):设置指定 index 位置的元素为 elelist.set(2,345);System.out.println(list); // [abc, 鸡蛋, 345, ooo, 222, ooo]// List subList(int fromIndex, int toIndex):返回从 fromIndex <= < toIndex 位置的子集合List returnList = list.subList(1,3);System.out.println(returnList); // [鸡蛋, 345]}}
- List的三种遍历方式
使用iterator
Iterator iterator = list.iterator();while(iterator.hasNext()){Object o = iterator.next();}
使用增强for
```java for (Object o:list) {
}
3. 使用普通for```javafor (int i = 0; i < list.size(); i++) {Object object = list.get(i);System.out.println(object);}
五、ArrayList底层
null也可以加入ArrayList
ArrayList是由数组来实现数据存储的
ArrayList基本等同于Vector,但ArrayList是线程不安全的
ArrayList中维护了一个Object类型的数组elementData
当创建ArrayList对象时,如果使用无参构造器,初始elementData容量为0,第1次添加,扩容为10,再次扩容,扩容为elementDate的1.5倍
如果使用的是指定大小的构造器,初始elementDate容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍
六、Vector
底层是对象数组
线程安全的,方法右synchronized
扩容:如果是无参,默认10,满了按2倍;如果指定大小,每次按2倍扩容
七、LinkedList
底层实现了双向链表和双端队列,可以添加任意元素,可以重复,包括null
线程不安全,没有实现同步
底层维护了一个双向链表,first和last分别指向首节点和尾节点
每个节点(Node)维护了prev,next,item三个属性,prev指向前一个节点,next指向后一个节点
添加删除效率比较高
public class LinkedListCRUD {public static void main(String[] args) {LinkedList linkedList = new LinkedList();linkedList.add(1);linkedList.add(222);linkedList.add("sds");System.out.println(linkedList); // [1, 222, sds]linkedList.remove();// 删除第一个结点linkedList.set(1,777); // 修改System.out.println(linkedList); // [222, 777]Object o = linkedList.get(1);System.out.println(o); // 777}}
改查多选ArrayList
增删多选LinkedList
八、Set
无序,没有索引
不允许重复元素,最多包含一个null
遍历可以使用迭代器和增强for,不能用索引,即不能普通的循环
public class SetMethod {public static void main(String[] args) {Set set = new HashSet();set.add("dfg");set.add("ewq");set.add("dqwd");set.add("dfg");set.add(111);set.add(null);set.add(null);System.out.println(set); // [null, dfg, dqwd, ewq, 111]不能重复且无序// 添加和取出的顺序不一致,虽然不是添加的顺序,但是是固定的// 遍历// 1. 迭代器Iterator i = set.iterator();while (i.hasNext()){Object obj = i.next();System.out.println(obj);}// 2. 增强forfor (Object o: set) {System.out.println(o);}}}
HashSet
实现Set接口,底层是HashMap
public HashSet() {map = new HashMap<>();}
可以添加null,但仅一个
不保证元素有序,hash后再确定索引的结果
public class HashSet_ {public static void main(String[] args) {Set set = new HashSet();// add会返回一个boolean值,添加成功,返回true,否则返回falseSystem.out.println(set.add("dfg")); //trueSystem.out.println(set.add("ewq")); //trueSystem.out.println(set.add("dqwd")); //trueSystem.out.println(set.add("dfg")); // falseSystem.out.println(set.add(111)); //true// remove指定删除某个对象set.remove("dfg");System.out.println(set);}}
public class HashSet01 {public static void main(String[] args) {Set set = new HashSet();set.add("dfg");set.add("dfg"); // 加不进set.add(new Dog("t"));set.add(new Dog("t")); // 加进去了 两个Dog对象System.out.println(set); // [dfg, Dog{name='t'}, Dog{name='t'}]set.add(new String("ttt"));set.add(new String("ttt")); // 加不进去System.out.println(set); // [dfg, ttt, Dog{name='t'}, Dog{name='t'}]}}class Dog{private String name;public Dog(String name) {this.name = name;}@Overridepublic String toString() {return "Dog{" +"name='" + name + '\'' +'}';}}
- 底层
HashSet的底层是HashMap,HashMap的底层是数组+链表+红黑树 - 扩容
第一次添加时,table数组扩容到16(临界值*加载因子=16x0.75=12),达到12时扩容到16x2=32,此时临界值32x0.75=24(加入一个结点就算一个)
添加一个元素时,得到hash值,转成索引值
找到存储表table的这个索引是否已经有元素
没有元素,直接添加
如果有,调用equals比较,相同,不能添加,不同,添加到最后
一条链表的元素个数达到TREEIFY_THRESHOLD(默认8),table大小>=MIN_TREEIFY_CAPACITY(默认64)会进行数化,红黑树
public class HashSet02 {public static void main(String[] args) {HashSet hashSet = new HashSet();hashSet.add(new Employee("ml",45));hashSet.add(new Employee("kl",21));hashSet.add(new Employee("ml",45));System.out.println(hashSet); //[Employee{name='ml', age=45}, Employee{name='kl', age=21}, Employee{name='ml', age=45}]// 要求name和age的值相同,视为相同的Employee,即让它们hash值相同// equals() and hashCode()后 [Employee{name='kl', age=21}, Employee{name='ml', age=45}]}}class Employee{private String name;private int age;public Employee(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}// ALT+INSERT 选equals() and hashCode()@Overridepublic 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);}@Overridepublic int hashCode() {return Objects.hash(name, age);}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "Employee{" +"name='" + name + '\'' +", age=" + age +'}';}}
LinkedHashSet
LinkedHashSet时HashSet的子类,底层是一个LinkedHashMap,底层维护了一个数组+双向链表
LinkedHashSet根据元素的hashCode值来决定元素的存储位置,同时使用链表维护元素的次序,使元素以插入顺序保存
不允许添加重复元素
九、Map 接口和常用方法
- 特点
双列key-value
k、v时任何引用类型的数据,会封装到HashMap$Node对象中
不允许重复
k、v可以为null,但只能有一个k为null,可以有多个v为空
通过k能找到对应的value
一对k-v放在一个HashMap$Node中Node实现了Entry接口,为了方便遍历(Entry提供了getKey(),getValue()方法),创建EntrySet集合,集合存放的是Entry,一个Entry对象有k,v EntrySet> 即transient Set > entrySet;
entrySet中定义的类型是Map.Entry,实际存放的是HashMapNode`实现了Map.Entry
接口 常用方法
public class Map01 {public static void main(String[] args) {Map map = new HashMap();map.put("we","121");map.put("nfwkje",554);map.put("vfs","efs");map.put("cs","csf");map.put("cs",new Book1("书",9)); // 替换map.put("er","gerg");map.put(null,"hi");map.put("fwqw",null);System.out.println(map); // {cs=Book1{name='书', num=9}, null=hi, vfs=efs, fwqw=null, nfwkje=554, er=gerg, we=121}// remove 根据K删除map.remove("vfs");System.out.println(map); // {cs=Book1{name='书', num=9}, null=hi, fwqw=null, nfwkje=554, er=gerg, we=121}System.out.println(map.get("cs")); // Book1{name='书', num=9}// size 获取元素个数System.out.println(map.size()); // 6// isEmpty 判空System.out.println(map.isEmpty()); // false// 清空// map.clear();// containsKey 是否存在System.out.println(map.containsKey("fwqw")); // true}}class Book1{private String name;private int num;public Book1(String name, int num) {this.name = name;this.num = num;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getNum() {return num;}public void setNum(int num) {this.num = num;}@Overridepublic String toString() {return "Book1{" +"name='" + name + '\'' +", num=" + num +'}';}}
Map 遍历
// 遍历public class Map02 {public static void main(String[] args) {Map map = new HashMap();map.put("we","121");map.put("nfwkje",554);map.put("vfs","efs");map.put("cs","csf");map.put("cs",new Book1("书",9)); // 替换map.put("er","gerg");map.put(null,"hi");map.put("fwqw",null);// 1. 取出所有key,通过key得到valueSet keyset = map.keySet();// 1.1 增强forfor (Object key:keyset) {System.out.println(key+"-"+map.get(key));}// 1.2 迭代器Iterator iterator = keyset.iterator();while (iterator.hasNext()) {Object key = iterator.next();System.out.println(key+"-"+map.get(key));}// 2. 把所有的values取出 可以使用所有的Collection遍历Collection values = map.values();// 2.1 增强forfor (Object value:values) {System.out.println(value);}// 2.2 迭代器Iterator iterator1 = values.iterator();while (iterator1.hasNext()) {Object next = iterator1.next();}// 3. 通过EntrySet获取k-vSet set = map.entrySet();// 3.1 增强forfor (Object entry: set) {// 将entry转成Map.EntryMap.Entry m =(Map.Entry) entry;System.out.println(m.getKey() + "-" + m.getValue());}// 3.2 迭代器Iterator iterator2 = set.iterator();while (iterator2.hasNext()){Object next = iterator2.next();// 向下转型Map.Entry m = (Map.Entry) next;System.out.println(m.getKey() + "-" + m.getValue());}}}
HashMap(k,v)是一个Node,实现了Map.Entry
扩容机制和HashSet一样:- HashMap底层维护了Node类型的数组table,默认为null
- 创建对象时,加载因子初始化为0.75
- 添加k-v时,通过k的哈希值得到在table的索引,判断该索引处是否有元素,如果没有元素直接添加,如果有元素,继续判断该元素的k和准备加入的k是否相等,相等,直接替换v,不相等,判断是树结构还是链表结构,做出相应处理。添加时容量不够,扩容
- 第一次添加,table扩容至16,临界值为12
- 之后再扩容,table扩容至2倍32,临界值24
- 如果一条链表的元素个数超过EREEIFY_THRESHOLD(默认8),且table>=MIN_TREEIFY_CAPACITY(默认64),树化(红黑树)
HashTable
存放键值对 k-v
hashtable键和值都不能为null
使用方法基本上和HashMap一样
hashtable是线程安全的
扩容:>=临界值时,以*2+1的大小扩容
Properties
键值对保存数据
和HashTable类似
用于从xx.properties文件中加载数据到Properties类对象,进行读取和修改
public class Properties_ {public static void main(String[] args) {Properties properties = new Properties();// k,v不能为nullproperties.put("qww",100);properties.put("lls",100);properties.put("z",100);properties.put("z",90); // 如果有相同的k,v值被替换System.out.println(properties); // {qww=100, z=90, lls=100}// 用k获取vSystem.out.println(properties.get("z"));// 删除properties.remove("z");System.out.println(properties); // {qww=100, lls=100}// 修改properties.put("lls",110);}}
十、TreeSet、TreeMap
- TreeSet
public class TreeSet_ {public static void main(String[] args) {// 无参构造器创建TreeSet时,是无序的TreeSet treeSet = new TreeSet();treeSet.add("jis");treeSet.add("aaas");treeSet.add("aa");treeSet.add("cd");treeSet.add("111");System.out.println(treeSet); // [111, aa, aaas, cd, jis]// 按照字符串大小排序// 使用TreeSet提供一个构造器,传入一个比较器(匿名内部类) 并指定排序规则/*1. 构造器把传入的比较器对象赋给了TreeSet 的底层的 TreeMap 的属性 this.comparatorpublic TreeMap(Comparator<? super K> comparator) {this.comparator = comparator;}2. 调用addif (cpr != null) {//cpr 是匿名内部类(对象)do {parent = t;//动态绑定到匿名内部类(对象)compare cmp = cpr.compare(key, t.key);if (cmp < 0)t = t.left;else if (cmp > 0)t = t.right;else //如果相等,即返回 0,这个 Key 就没有加入return t.setValue(value);} while (t != null);}*/TreeSet treeSet1 = new TreeSet(new Comparator() {@Overridepublic int compare(Object o1, Object o2) {//使用String的compareTo方法进行字符串大小比较return ((String)o2).compareTo((String) o1);}});// 添加数据treeSet1.add("jis");treeSet1.add("aaas");treeSet1.add("aa");treeSet1.add("cd");treeSet1.add("111");System.out.println(treeSet1); // [jis, cd, aaas, aa, 111]// 按照字符长度大小排序TreeSet treeSet2 = new TreeSet(new Comparator() {@Overridepublic int compare(Object o1, Object o2) {return ((String)o2).length()-((String) o1).length();}});treeSet2.add("jis");treeSet2.add("aaas");treeSet2.add("aa");treeSet2.add("c");treeSet2.add("111");System.out.println(treeSet2); // [aaas, jis, aa, c] "111"没有被加入,因为规则是以长度大小排序,长度相同时被看作是同一个}}
- TreeMap
public class TreeMap_ {public static void main(String[] args) {// 使用默认的构造器创建,是无序的// 按照传入k的字符串大小进行排序/*1. 构造器. 把传入的实现了 Comparator 接口的匿名内部类(对象),传给给 TreeMap 的 comparatorpublic TreeMap(Comparator<? super K> comparator) { this.comparator = comparator; }2. put方法2.1 第一次添加,把k-v封装到Entry对象,放入rootEntry<K,V> t = root;if (t == null) {compare(key, key); // 第一次添加也会调用compare,这样判断是不是nullroot = new Entry<>(key, value, null);size = 1; modCount++;return null;}2.2 再次添加Comparator<? super K> cpr = comparator;if (cpr != null) {do { //遍历所有的 key , 给当前 key 找到适当位置parent = t;cmp = cpr.compare(key, t.key);//动态绑定到匿名内部类的compareif (cmp < 0)t = t.left;else if (cmp > 0)t = t.right;else //如果遍历过程中,发现准备添加 Key 和当前已有的 Key 相等,就不添加return t.setValue(value);} while (t != null);}*/TreeMap treeMap = new TreeMap(new Comparator() {@Overridepublic int compare(Object o1, Object o2) {return ((String) o2).compareTo((String) o1);}});treeMap.put("de","fwefee0");treeMap.put("vea",45);treeMap.put("aihia","edas");System.out.println(treeMap); // {vea=45, de=fwefee0, aihia=edas}}}
十一、Collections工具类
操作Set、List、Map等集合的工具类
提供了一系列静态的方法对集合元素进行排序、查询和修改等操作
- 都是static方法
public class Collections_ {public static void main(String[] args) {List list = new ArrayList();list.add("aaa");list.add("ccc");list.add("cxx");list.add("kql");System.out.println(list); // [aaa, ccc, cxx, kql]// reverse 反转List中元素的顺序Collections.reverse(list);System.out.println(list); // [kql, cxx, ccc, aaa]// shuffle 对集合元素进行随机排序for (int i = 0;i<5;i++){Collections.shuffle(list);System.out.println(list);}// sort(List) 根据元素的自然顺序对指定集合元素按升序排序Collections.sort(list);System.out.println(list);// sort(List, Comparator) 根据指定的顺序对集合元素排序// 按字符串的大小排序Collections.sort(list, new Comparator() {@Overridepublic int compare(Object o1,Object o2){return ((String)o2).compareTo((String)o1);}});System.out.println(list); // [kql, cxx, ccc, aaa]// swap(List,int,int) 将指定list集合中i处的元素和j处的元素进行交换Collections.swap(list, 0, 1);System.out.println(list); // [cxx, kql, ccc, aaa]// Object max(Collection) 根据元素的自然排序,返回给定集合中的最大元素System.out.println(Collections.max(list)); // kql// Object max(Collection, Comparator) 根据Comparator指定的顺序,返回给定集合中的最大元素Object max = Collections.max(list, new Comparator() {@Override public int compare(Object o1, Object o2) {return ((String)o2).compareTo((String)o1);}});System.out.println(max); // aaa// Object min(Collection)// Object min(Collection,Comparator) 跟max类似// int frequency(Collection,Object):返回指定集合中指定元素的出现次数System.out.println(Collections.frequency(list,"aaa")); // 1//void copy(List dest,List src):将 src 中的内容复制到 dest 中ArrayList dest = new ArrayList();for (int i = 0;i < list.size();i++){dest.add(""); // 要先给dest赋值}Collections.copy(dest,list);System.out.println(dest); // [cxx, kql, ccc, aaa]// boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换 List 对象的所有旧值Collections.replaceAll(list,"aaa","xxx");System.out.println(list); // [cxx, kql, ccc, xxx]}}
选择


