集合能够看做是一种容器,用来存储对象信息。全部集合类都位于java.util包下,但支持多线程的集合类位于java.util.concurrent包下。java
数组与集合的区别node
Collection 接口数组
序号 | 接口描述 |
---|---|
1 | Collection 接口 Collection 是最基本的集合接口。一个 Collection 表明一组 Object ,即 Collection 元素 Java不提供直接继承Collection的类,只提供继承于它的子接口(如List,Set) Collection接口存储一组不惟一,无序的对象 |
2 | List 接口 List 是一个有序的 Collection,此接口可以精准的控制每一个元素插入的位置,能够经过索引来访问 List 中的元素 第一个元素索引为0,并且容许有相同的元素 List 接口存储一组不惟一,有序(插入顺序)的对象 |
3 | Set 接口 Set 具备与 Collection 彻底同样的接口,只是行为上不一样,Set 不保存重复的元素 |
4 | SortedSet 接口 继承于 Set 的子接口,可是它保存的是惟一有序的集合 |
5 | Queue 接口 Queue 就是队列,队列的特色是先进先出,一般,不容许随机访问元素。 |
Set 与 List 的区别安全
Collection 实现类(经常使用集合类)数据结构
序号 | 类描述 |
---|---|
1 | List |➡ArrayList 该类实现了可变大小的数组,为随机访问和遍历元素提供了更好的性能 线程不安全的(异步),多线程状况下不要使用。 ArrayList 扩容长度 50%,插入删除效率低。 底层为数组结构 |
2 | List |➡Vector 该类与ArrayList 类很是类似 线程安全的(同步),多线程状况下,该类容许默认扩容长度100% 底层为数组结构 |
3 | List/Queue |➡LinkedList 该类没有同步方法,若是多线程同时访问要给List,则必须本身实现访问同步 解决方法就是建立List时创造一个同步的List = Collections.synchronizedList(newLinkedList(...)); 底层为双向链表结构 |
4 | Queue |➡ArrayQueue 该类是一个循环队列,继承了AbstractList 抽象类 底层为数组结构 |
5 | Set |➡HashSet 该类不容许出现重复元素,不保证集合中元素的顺序,容许包含值为Null的元素,但最多一个 底层为哈希表结构(一个HashMap实例)由数组+链表/红黑树构成(链表长度大于8切换为红黑树) |
6 | Set |➡TreeSet 该类实现排序等功能 底层为红黑树结构 |
7 | Set |➡HashSet |➡LinkedHashSet 该类不只保证了元素的惟一,同时元素存放是由顺序的 底层为链表+哈希表结构 |
特别说明多线程
JDK 1.8以前,哈希表是由 数组+链表 实现,即便用链表处理冲突,同一hash值的链表都存储在一个链表里。可是当位于一个桶中的元素较多,即hash值相等的元素较多时,经过key值依次查找的效率较低。框架
而 JDK 1.8,哈希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减小了查找时间。异步
代码表现ide
public class LinkedListDemo{ public static void main(String[] args) { LinkedList<String> link = new LinkedList<String>(); //添加元素 link.addFirst("abc1"); link.addFirst("abc2"); link.addFirst("abc3"); System.out.println(link); // 获取元素 System.out.println(link.getFirst()); System.out.println(link.getLast()); // 删除元素 System.out.println(link.removeFirst()); System.out.println(link.removeLast()); while (!link.isEmpty()) { //判断集合是否为空 System.out.println(link.pop()); //弹出集合中的栈顶元素(弹栈) } System.out.println(link); } } /* 输出结果: [abc3, abc2, abc1] abc3 abc1 abc3 abc1 abc2 [] */
public class HashSetDemo { public static void main(String[] args) { //建立 Set集合 HashSet<String> set = new HashSet<String>(); //添加元素 set.add(new String("cba")); set.add("abc"); set.add("bac"); set.add("cba"); //遍历 for (String name : set) { System.out.println(name); } System.out.println("==========") //建立集合对象 该集合中存储 Student类型对象 HashSet<Student> stuSet = new HashSet<Student>(); //存储 Student stu = new Student("于谦", 43); stuSet.add(stu); stuSet.add(new Student("郭德纲", 44)); stuSet.add(new Student("于谦", 43)); stuSet.add(new Student("郭麒麟", 23)); stuSet.add(stu); for (Student stu2 : stuSet) { System.out.println(stu2); } } } /* 输出结果: cba abc bac ========== Student [name=郭德纲, age=44] Student [name=于谦, age=43] Student [name=郭麒麟, age=23] tips: 根据结果咱们发现字符串“cba”与Student [name=于谦, age=43]只存储了一个,也就是说重复的元素set集合不存储 */
public class LinkedHashSetDemo { public static void main(String[] args) { Set<String> set = new LinkedHashSet<String>(); set.add("bbb"); set.add("aaa"); set.add("abc"); set.add("bbc"); Iterator<String> it = set.iterator(); while (it.hasNext()) { System.out.println(it.next()); } } } /* 运行结果 bbb aaa abc bbc */
java.utils.Collections 是集合工具类,用来对集合进行操做工具
public class CollectionsDemo { public static void main(String[] args) { ArrayList<Integer> list = new ArrayList<Integer>(); //原来写法 //list.add(12); //list.add(14); //list.add(15); //list.add(1000); //采用工具类 完成 往集合中添加元素 Collections.addAll(list, 5, 222, 1,2); System.out.println(list); //排序方法 Collections.sort(list); System.out.println(list); } } /* 运行结果: [5, 222, 1, 2] [1, 2, 5, 222] */
Comparator比较器
public class CollectionsDemo3 { public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); list.add("cba"); list.add("aba"); list.add("sba"); list.add("nba"); //排序方法 按照第一个单词的降序 Collections.sort(list, new Comparator<String>() { @Override public int compare(String o1, String o2) { /* 两个对象比较的结果有三种:大于,等于,小于。 若是要按照升序排序,则 o1 小于 o2 若是要按照降序排序,则 o1 小于 o2 */ return o2.charAt(0) - o1.charAt(0); } }); System.out.println(list); } } public int compare(String o1, String o2)
//实体 public class Student{ private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } } //测试类 public class Demo { public static void main(String[] args) { // 建立四个学生对象 存储到集合中 ArrayList<Student> list = new ArrayList<Student>(); list.add(new Student("rose",18)); list.add(new Student("jack",16)); list.add(new Student("abc",16)); list.add(new Student("ace",17)); list.add(new Student("mark",16)); /* 让学生 按照年龄排序 升序 */ // Collections.sort(list);//要求 该list中元素类型 必须实现比较器Comparable接口 for (Student student : list) { System.out.println(student); } } } /* 发现 当咱们调用Collections.sort()方法的时候程序报错 缘由 若是想要集合中的元素完成排序,那么必需要实现比较器Comparable接口、 */ public class Student implements Comparable<Student>{ .... @Override public int compareTo(Student o) { return this.age-o.age;//升序 } } /* 运行结果 Student{name='jack', age=16} Student{name='abc', age=16} Student{name='mark', age=16} Student{name='ace', age=17} Student{name='rose', age=18} */
Collections.sort(list, new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { return o2.getAge()-o1.getAge();//以学生的年龄降序 } }); /* 上列代码,效果与上边输出同样 下列代码,为多规则断定 */ Collections.sort(list, new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { // 年龄降序 int result = o2.getAge()-o1.getAge(); //年龄降序 if(result==0){//第一个规则判断完了 下一个规则 姓名的首字母 升序 result = o1.getName().charAt(0)-o2.getName().charAt(0); } return result; } });
Map接口,存储一组键值对对象,提供key/value的映射
序号 | 描述类 |
---|---|
1 | Map |➡HashMap 该类是一个散列表,它存储的内容是键值对(key/value)映射 根据键的HashCode值存储数据,具备很快的访问速度,最多记录一条为Null的键,不支持线程同步 底层为哈希表结构(数组+链表/红黑树) |
2 | Map |➡HashMap |➡LinkedHashMap 底层为 链表 + 哈希表 |
经常使用方法
public class MapDemo { public static void main(String[] args) { //建立 map对象 HashMap<String, String> map = new HashMap<String, String>(); //添加元素到集合 map.put("黄晓明", "杨颖"); map.put("文章", "马伊琍"); map.put("邓超", "孙俪"); System.out.println(map); //String remove(String key) System.out.println(map.remove("邓超")); System.out.println(map); // 想要查看 黄晓明的媳妇 是谁 System.out.println(map.get("黄晓明")); System.out.println(map.get("邓超")); } } /* tips: 使用put方法时,若指定的键(key)在集合中没有,则没有这个键对应的值,返回null,并把指定的键值添加到集合中 若指定的键(key)在集合中存在,则返回值为集合中键对应的值(该值为替换前的值),并把指定键所对应的值,替换成指定的新值 */
Map遍历键找值
public class MapDemo01 { /* 1.获取Map中全部的键,因为键是惟一的,因此返回一个Set集合存储全部的键。方法提示: keyset() 2.遍历键的Set集合,获得每个键。 3.根据键,获取键所对应的值。方法提示: get(K key) */ public static void main(String[] args) { //建立Map集合对象 HashMap<String, String> map = new HashMap<String,String>(); //添加元素到集合 map.put("胡歌", "霍建华"); map.put("郭德纲", "于谦"); map.put("薛之谦", "大张伟"); //获取全部的键 获取键集 Set<String> keys = map.keySet(); // 遍历键集 获得 每个键 for (String key : keys) { //key 就是键 //获取对应值 String value = map.get(key); System.out.println(key+"的CP是:"+value); } } }
Entry键值对对象以及遍历
/* Map中存放的是两种对象 key(键)/value(值),它们在在Map中是一一对应关系。一组K/V又称作Map中的一个Entry(项),Entry将键值对的对应关系封装成了对象,即键值对对象。这样咱们在遍历Map集合时,就能够从每个键值对(Entry)对象中获取对应的键与对应的值。 方法: public K getKey() :获取Entry对象中的键。 public V getValue() :获取Entry对象中的值。 操做步骤: 1. 获取Map集合中,全部的键值对(Entry)对象,以Set集合形式返回。方法提示: map.entrySet() 。 2. 遍历包含键值对(Entry)对象的Set集合,获得每个键值对(Entry)对象。 */ public class MapDemo02 { public static void main(String[] args) { // 建立Map集合对象 HashMap<String, String> map = new HashMap<String,String>(); // 添加元素到集合 map.put("胡歌", "霍建华"); map.put("郭德纲", "于谦"); map.put("薛之谦", "大张伟"); // 获取 全部的 entry对象 entrySet Set<Entry<String,String>> entrySet = map.entrySet(); // 遍历获得每个entry对象 for (Entry<String, String> entry : entrySet) { // 解析 String key = entry.getKey(); String value = entry.getValue(); System.out.println(key+"的CP是:"+value); } } } /* tips:Map集合不能直接使用迭代器或者foreach进行遍历。可是转成Set以后就可使用了。 */
HashMap存储自定义类型键值,使用map.keySet()方法
/* 每位学生(姓名,年龄)都有本身的家庭住址。则将学生对象和家庭住址存储到 map集合中。学生做为键, 家庭住址做为值 */ public class Student { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; return age == student.age && Objects.equals(name, student.name); } @Override public int hashCode() { return Objects.hash(name, age); } } /*map.keySet()方法*/ public class HashMapTest { public static void main(String[] args) { //1,建立Hashmap集合对象。 Map<Student,String>map = new HashMap<Student,String>(); //2,添加元素。 map.put(newStudent("lisi",28), "上海"); map.put(newStudent("wangwu",22), "北京"); map.put(newStudent("zhaoliu",24), "成都"); map.put(newStudent("zhouqi",25), "广州"); map.put(newStudent("wangwu",22), "南京"); //3,取出元素。键找值方式 Set<Student> keySet = map.keySet(); for(Student key: keySet){ Stringvalue = map.get(key); System.out.println(key.toString()+"....."+value); } } }
Java 9 ,添加了几重集合工厂方法,更方便建立少许元素的集合
public class HelloJDK9 { public static void main(String[] args) { Set<String> str1=Set.of("a","b","c"); //str1.add("c");这里编译的时候不会错,可是执行的时候会报错,由于是不可变的集合 System.out.println(str1); Map<String,Integer> str2=Map.of("a",1,"b",2); System.out.println(str2); List<String> str3=List.of("a","b"); System.out.println(str3); } } /* 两点注意: 1.of()方法只是Map,List,Set这三个接口的静态方法,其父类接口和子类实现并无这类方法,好比 HashSet,ArrayList等待; 2.返回的集合是不可变的; */
数据存储的经常使用结构有:栈、队列、数组、链表和红黑树。咱们分别来了解一下:
栈(stack) ,又称堆栈,它是运算受限的线性表,其限制是仅容许在标的一端进行插入和删除操做,不容许在其余任何位置进行添加、查找、删除等操做。
简单的说,采用该结构的集合,对元素的存取有以下的特色
这里两个名词须要注意
数组(Array),是有序的元素序列,数组是在内存中开辟一段连续的空间,并在此空间存放元素。就像是一排出租屋,有100个房间,从001到100每一个房间都有固定编号,经过编号就能够快速找到租房子的人。
简单的说,采用该结构的集合,对元素的存取有以下的特色
链表(linked list),由一系列结点node组成(链表中每个元素称为结点),结点能够在运行时动态生成。每一个结点包括两个部分:一个是存储数据元素的数据域,另外一个是存储下一个结点地址的指针域。咱们常说的链表结构有单向链表与双向链表,那么这里给你们介绍的是单向链表。
简单的说,采用该结构的集合,对元素的存取有以下的特色
二叉树(binary tree) 是每一个结点不超过2的有序树(tree)。简单的理解,就是一种相似于咱们生活中树的结构,只不过每一个结点上都最多只能有两个子结点。二叉树是每一个节点最多有两个子树的树结构。顶上的叫根结点,两边被称做"左子树"和"右子树"
咱们要说的是二叉树的一种比较有意思的叫作红黑树,红黑树自己就是一颗二叉查找树,将节点插入后,该树仍然是一颗二叉查找树。也就意味着,树的键值仍然是有序的。
红黑树的约束
红黑树的特色,速度特别快,趋近平衡树,查找叶子元素最少和最屡次数很少于二倍