前言
java容器是前人为我们提供的一套用于存储数据和对象的工具。如果你学过C++的STL,可以与之类比。java容器又可以称为Java Collection Framework(JCF)。里面除了存储对象的容器之外,还提供了一套用于处理和操作容器里面的对象的一套工具类。 整体框架: 下面将介绍List、Set、Map以及工具类Collections和Arrays。
List
List:列表,是一个接口。它的实现类常用的有LinkedList、ArrayList和Vector。
LinkedList
LinkedList采用双向链表实现的列表,因此可以被用作队列、堆栈、双端队列;顺序访问高效,随机访问性能较差、适用于需要经常添加和删除的数据。LinkedList不支持同步
package 常用数据结构;
import java.util.Iterator;
import java.util.LinkedList;
/**
* @author pony
* @date 2020/4/28
*/
public class LinkedListTest {
public static void main(String[] args) {
LinkedList
for(int i=0;i<10;++i){
linked.add(i);
}
System.out.println(linked.size());
linked.addFirst(10);
linked.add(3,20);
linked.remove(3);
LinkedList
for(int i=0;i<100000;++i)
list.add(i);
traverseByIterator(list);
traverseByIndex(list);
traverseByFor(list);
}
private static void traverseByFor(LinkedList
long start = System.nanoTime();
for(Integer i:list)
;
long end = System.nanoTime();
System.out.println("for-each遍历:"+(end-start)+"ms");
}
private static void traverseByIndex(LinkedList
long start = System.nanoTime();
for(int i=0;i list.get(i); long end = System.nanoTime(); System.out.println("随机索引遍历:"+(end-start)+"ms"); } private static void traverseByIterator(LinkedList long start = System.nanoTime(); Iterator while (iterator.hasNext()) iterator.next(); long end = System.nanoTime(); System.out.println("迭代器遍历:"+(end-start)+"ms"); } } 运行结果: ArrayList ArrayList是采用数组实现的列表,因此它支持随机访问,不适合频繁删除和插入操作。对于需要经常进行查询的数据建议采用此结构。ArrayList与java数组的一个大的区别是ArrayList能够自动扩容ArrayList不支持同步 package 常用数据结构; import java.util.ArrayList; import java.util.Iterator; /** * @author pony * @date 2020/4/28 */ public class ArrayListTest { public static void main(String[] args) { ArrayList for(int i=0;i<5;++i){ arrayList.add(i); //这里会自动装箱 } arrayList.add(0); //允许有相同的元素 arrayList.remove(2); arrayList.add(3,9); //遍历测试 ArrayList for(int i=0;i<100000;++i){ arr1.add(i); } traverseByIterator(arr1); traverseByIndex(arr1); traverseByFor(arr1); } private static void traverseByFor(ArrayList long start = System.nanoTime(); for(Integer i:arr1) ; long end = System.nanoTime(); System.out.println("for遍历:"+(end-start)+"ms"); } private static void traverseByIndex(ArrayList long start = System.nanoTime(); for(int i=0;i arr1.get(i); } long end = System.nanoTime(); System.out.println("随机索引遍历:"+(end-start)+"ms"); } private static void traverseByIterator(ArrayList long start = System.nanoTime(); Iterator while (iterator.hasNext()) iterator.next(); long end = System.nanoTime(); System.out.println("迭代器遍历:"+(end-start)+"ms"); } } 运行结果: Vector Vector用法和ArrayList用法很相似,它们的区别在于Vector是线程同步的而且Vector有另外的遍历方式。这里将不再赘述。 总结 对于不同的数据我们应该根据其特点采用不同的list,而不是一中list用到底。要学会灵活使用。 Set Set:集合,和数学中的集合类似。 特点: 确定性:对任一对象都能判定它是否属于某一个集和互异性:一个集和中不会存在两个相同(内容相同)的对象无序性:集和里面的元素没有顺序 HashSet、LinkedHashSet、TreeSet里面存放的都要是对象,不能是基本数据类型。 HashSet 基于散列函数的集和,采用HashMap实现,可以容纳null元素,不支持同步(可以通过Collections.synchronizedSet(new HashSet<…>()来使它同步) package 常用数据结构; import java.util.HashSet; import java.util.Iterator; /** * @author pony * @date 2020/4/28 */ public class HashSetTest { public static void main(String[] args) { HashSet for(int i=0;i<5;++i) hs.add(i); hs.add(1); //无效的语句,因为集合中已经有1了。 hs.add(null); //可以容纳null元素 /** * 测试两种遍历 */ HashSet for(int i=0;i<100000;++i) hs1.add(i); long start = System.nanoTime(); Iterator while (iterator.hasNext()) iterator.next(); long end = System.nanoTime(); System.out.println("迭代器遍历:"+(end-start)+"ms"); start = System.nanoTime(); for(Integer item:hs1) ; end = System.nanoTime(); System.out.println("for-each遍历:"+(end-start)+"ms"); } } 运行结果: HashLinkedSet 继承HashSet ,基于散列函数和双向链表的集和,可以容纳null元素,通过双向链表维护了插入顺序,从而支持排序,但不支持同步(可以通过Collections.synchronizedSet(new HashSet<…>()来使它同步) 代码和上面类似。把HashSet改成LinkedHashSet即可 运行结果: TreeSet 基于树结构的集和,印次支持排序,不能容纳null元素,同样不支持同步 package 常用数据结构; import java.util.TreeSet; /** * @author pony * @date 2020/4/28 */ public class TreeSetTest { public static void main(String[] args) { TreeSet for(int i=0;i<10;++i) ts.add(i); ts.add(100); ts.add(20); ts.add(45); for(Integer item:ts) System.out.println(item); // ts.add(null); //错误,不支持添加null元素 ts.add(1); //重复 } } 总结 判定是否是重复元素的原则 HashSet、LinkedHashSet 判断两个对象的hashcode()是否相同,如果不同,返回flase如果hashcode()相同,则调用equals()方法判断内容是否相同。相同返回true,不同返回flase package 常用数据结构; import java.util.HashSet; import java.util.LinkedHashSet; /** * @author pony * @date 2020/4/28 */ public class HashSetEqualsRuleTest { public static void main(String[] args) { HashSet cats.add(new Cat(1)); cats.add(new Cat(2)); //这里将看成是两个对象,因为hashcode方法返回的值不一样 cats.add(new Cat(2)); System.out.println(cats.size()); //3 //重写了hashcode()和equals() HashSet dogs.add(new Dog(1)); dogs.add(new Dog(2)); //这里是一个对象,因为重写了它的hashcode方法使得返回值一样 dogs.add(new Dog(2)); System.out.println(dogs.size()); //2 LinkedHashSet cats1.add(new Cat(1)); cats1.add(new Cat(2)); cats1.add(new Cat(2)); System.out.println(cats1.size()); //3 //重写了hashcode()和equals() LinkedHashSet dogs1.add(new Dog(1)); dogs1.add(new Dog(2)); dogs1.add(new Dog(2)); System.out.println(dogs1.size()); //2 } } class Cat{ public int size; public Cat(int size) { this.size = size; } } class Dog { public int size; public Dog(int size) { this.size = size; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Dog dog = (Dog) o; return size == dog.size; } @Override public int hashCode() { return size; } @Override public String toString() { return "Dog{" + "size=" + size + '}'; } } TreeSet 添加到TreeSet里面的元素都必须实现comparable接口。因为在添加元素时会调用接口里面的compareTo()方法来比较是否是同一个元素。 package 常用数据结构; import java.util.TreeSet; /** * @author pony * @date 2020/4/28 */ public class TreeSetEqualsRuleTet { public static void main(String[] args) { // TreeSet // cats.add(new Cat(1)); //会报错,因为没有实现Compareable接口 // cats.add(new Cat(1)); // System.out.println(cats.size()); TreeSet tiggers.add(new Tigger(1)); tiggers.add(new Tigger(2)); tiggers.add(new Tigger(1)); System.out.println(tiggers.size()); } } class Tigger implements Comparable{ public int size; public Tigger(int size) { this.size = size; } public int getSize() { return size; } @Override public int compareTo(Object o) { System.out.println("in Tigger compareTo"); return size-((Tigger)o).getSize(); } } 运行结果: Map Map是一类重要的数据结构。类似于数学中的函数,key对应自变量x、value对应因变量y、散列函数对用f。 Hashtable key和value都不能为空,HashTable是线程安全的、同步的,但只适合用于小数据量 package 常用数据结构; import java.util.Hashtable; import java.util.Iterator; import java.util.Map; /** * @author pony * @date 2020/4/28 */ public class HashtableTest { public static void main(String[] args) { Hashtable // ht.put(1,null); 报错 // ht.put(null,"1"); 报错 // ht.put(null,null); 报错 ht.put(1,"111"); ht.put(2,"222"); ht.put(3,"3333"); ht.contains("111"); ht.containsValue("111"); ht.containsKey(0); /** * 三种遍历方式 */ ht.clear(); for(int i=0;i<100000;++i) ht.put(i,"aaaa"); //第一种:Entry迭代器遍历 long start = System.nanoTime(); Iterator while (iterator.hasNext()) { Map.Entry next.getKey(); next.getValue(); } long end = System.nanoTime(); System.out.println("Entry迭代器遍历:"+(end-start)); //第二种:keySet迭代器遍历 start=System.nanoTime(); Iterator while (iterator1.hasNext()){ Integer key = iterator1.next(); String value = ht.get(key); } end = System.nanoTime(); System.out.println("keySet迭代器遍历:"+(end-start)); } } 运行结果: HashMap HashMap允许有null,不支持同步(支持通过Collections.synchronizedMap(new Map<…,…>() 实现同步),所以线程不安全。但可以存储大量数据 package 常用数据结构; import java.util.HashMap; import java.util.Iterator; import java.util.Map; /** * @author pony * @date 2020/4/28 */ public class HashMapTest { public static void main(String[] args) { HashMap ht.put(1,null); ht.put(null,"1"); ht.put(null,null); ht.put(1,"111"); ht.put(2,"222"); ht.put(3,"3333"); ht.containsValue("111"); ht.containsKey(0); Iterator while (iterator2.hasNext()){ Integer key = iterator2.next(); System.out.println(key+":"+ht.get(key)); } /** * 三种遍历方式 */ ht.clear(); for(int i=0;i<100000;++i) ht.put(i,"aaaa"); //第一种:Entry迭代器遍历 long start = System.nanoTime(); Iterator while (iterator.hasNext()) { Map.Entry next.getKey(); next.getValue(); } long end = System.nanoTime(); System.out.println("Entry迭代器遍历:"+(end-start)); //第二种:keySet迭代器遍历 start=System.nanoTime(); Iterator while (iterator1.hasNext()){ Integer key = iterator1.next(); String value = ht.get(key); } end = System.nanoTime(); System.out.println("keySet迭代器遍历:"+(end-start)); } } 运行结果: LinkedHashMap 基于双向链表用于维持插入顺序的HashMap,继承自HashMap TreeMap 基于红黑树的Map,可以根据key的自然排序或者compareTo方法排序输出 Properties 继承自Hashtable。特有的方法有load()、store()等等。 总结 Map的种类有很多,根据实际情况选择合适的Map。 工具类 Arrays Arrays处理的对象是数组,常用的方法包括排序sort、查找binarySearch、拷贝copy等等 package 常用数据结构; import java.util.Arrays; import java.util.Random; import java.util.stream.IntStream; /** * @author pony * @date 2020/4/28 */ public class ArraysTest { public static void main(String[] args) { testSort(); testSearch(); testFill(); testCopy(); testEquality(); } private static void testEquality() { int[] a = new int[5]; int[] b = new int[5]; Arrays.fill(a,1); Arrays.fill(b,1); System.out.println(Arrays.equals(a,b)); b[1]=3; System.out.println(Arrays.equals(a,b)); } private static void testCopy() { Random random = new Random(); IntStream ints = random.ints(20,1,100); int[] a = ints.toArray(); for(int i:a) System.out.println(i); int[] ints1 = Arrays.copyOf(a, 4); for(int i:ints1) System.out.println(i); } private static void testFill() { int[] a = new int[10]; Arrays.fill(a,2,8,100); for(int i:a) System.out.println(i); } private static void testSearch() { Random random = new Random(); int[] ints = random.ints(5,1,100).toArray(); ints[ints.length-1]=100; int i = Arrays.binarySearch(ints, 100); System.out.println("100 in "+i); } private static void testSort() { Random random = new Random(); IntStream ints = random.ints(20,1,100); int[] a = ints.toArray(); System.out.println("排序前:"); for(int i:a) System.out.println(i); Arrays.sort(a); System.out.println("排序后:"); for(int i:a) System.out.println(i); } } Collections Collections可以操作collections接口及其所有子类、常用的用法和Arrays差不多。但它的sort方法要求被排序对象实现了compareable接口或者传入一个compactor对象(主要针对某些类不能去被修改) 总结 Arrays和Collections着两个工具类能够帮我们做很多事情,所以要熟练的应用。避免重复造轮子。 总结 写到这,容器的基本知识应该都了解的差不多了,但这只能算是对容器入门了。只是学会了如何去使用。建议以后多多去查阅API和源码。了解其内部是如何实现这些结构的。当你能够自己写出来这些容器的实现时,才算真正的掌握了java容器。