首页 文章 精选 留言 我的

精选列表

搜索[快速入门],共10000篇文章
优秀的个人博客,低调大师

Python爬虫入门教程 24-100 微医挂号网医生数据抓取

1. 微医挂号网医生数据写在前面 今天要抓取的一个网站叫做微医网站,地址为 https://www.guahao.com ,我们将通过python3爬虫抓取这个网址,然后数据存储到CSV里面,为后面的一些分析类的教程做准备。本篇文章主要使用的库为pyppeteer 和 pyquery 首先找到 医生列表页 https://www.guahao.com/expert/all/全国/all/不限/p5 这个页面显示有 75952 条数据 ,实际测试中,翻页到第38页,数据就加载不出来了,目测后台程序猿没有把数据返回,不过为了学习,我们忍了。 2. 微医挂号网医生数据页面URL https://www.guahao.com/expert/all/全国/all/不限/p1 https://www.guahao.com/expert/all/

优秀的个人博客,低调大师

Python爬虫入门教程 23-100 石家庄链家租房数据抓取

1. 石家庄链家租房数据-写在前面 作为一个活跃在京津冀地区的开发者,要闲着没事就看看石家庄这个国际化大都市的一些数据,这篇博客爬取了链家网的租房信息,爬取到的数据在后面的博客中可以作为一些数据分析的素材。我们需要爬取的网址为:https://sjz.lianjia.com/zufang/ 2. 石家庄链家租房数据-分析网址 首先确定一下,哪些数据是我们需要的 可以看到,黄色框就是我们需要的数据。 接下来,确定一下翻页规律 https://sjz.lianjia.com/zufang/pg1/ https://sjz.lianjia.com/zufang/pg2/ https://sjz.lianjia.com/zufang/pg3/ https://sjz.lianjia.com/zufang/pg4/ https://sjz.lianji

优秀的个人博客,低调大师

Python爬虫入门教程 12-100 半次元COS图爬取

写在前面 今天在浏览网站的时候,忽然一个莫名的链接指引着我跳转到了半次元网站 https://bcy.net/ 打开之后,发现也没有什么有意思的内容,职业的敏感让我瞬间联想到了 cosplay ,这种网站必然会有这个的存在啊,于是乎,我准备好我的大爬虫了。 把上面的链接打开之后,被我发现了吧,就知道我的第八感不错滴。接下来就是找入口,一定要找到图片链接的入口才可以做下面的操作 这个页面不断往下拖拽,页面会一直加载,当时当你拖拽一会,就停下来了,就是这个时机 发现入口,在我实际的操作中,其实还发现了很多其他的入口,这个就不一一的解释了,赶紧上车,进入 view more 之后,发现了页面依旧是一个下拉刷新的布局方式,专业术语 瀑布流 。 python爬虫第一步 打开开发者工具,切换到network之后,发现 很多xhr请求,发现这个,就代表这个网站很容易爬取了 提取待爬取的链接,分析规律 https://bcy.net/circle/timeline/loadtag?since=0&grid_type=timeline&tag_id=1482&sort=hot https://bcy.net/circle/timeline/loadtag?since=26499.779&grid_type=timeline&tag_id=1482&sort=hot https://bcy.net/circle/timeline/loadtag?since=26497.945&grid_type=timeline&tag_id=1482&sort=hot 发现只有一个参数在变,而且这变化好像没有任何规律可以寻找,没事,看数据,你就可以发现其中的奥妙了 这个网站的原理很简单,就是通过不断获取每次数据的最后一条的since然后获取接下来的数据,那么我们按照它的规律实现代码就可以了,不要多线程了,这种规律是没有办法进行实操的。这次的数据我把它存储到mongodb里面,因为没有办法一次全部获取到,所以可能需要下次在继续使用 if __name__ == '__main__': ### mongodb 的一些基本操作 DATABASE_IP = '127.0.0.1' DATABASE_PORT = 27017 DATABASE_NAME = 'sun' start_url = "https://bcy.net/circle/timeline/loadtag?since={}&grid_type=timeline&tag_id=399&sort=recent" client = MongoClient(DATABASE_IP, DATABASE_PORT) db = client.sun db.authenticate("dba", "dba") collection = db.bcy # 准备插入数据 #####################################3333 get_data(start_url,collection) 获取网页数据这个地方,由我们前面的经验就变得很简单了 # 获取数据函数 def get_data(start_url,collection): since = 0 while 1: try: with requests.Session() as s: response = s.get(start_url.format(str(since)),headers=headers,timeout=3) res_data = response.json() if res_data["status"] == 1: data = res_data["data"] # 获取Data数组 time.sleep(0.5) ## 数据处理 since = data[-1]["since"] # 获取20条数据的最后一条json数据中的since ret = json_handle(data) # 代码实现在下面 try: print(ret) collection.insert_many(ret) # 批量出入数据库 print("上述数据插入成功!!!!!!!!") except Exception as e: print("插入失败") print(ret) ## except Exception as e: print("!",end="异常,请注意") print(e,end=" ") else: print("循环完毕") 网页解析代码 # 对JSON数据进行处理 def json_handle(data): # 提取关键数据 list_infos = [] for item in data: item = item["item_detail"] try: avatar = item["avatar"] # 用户头像 item_id = item["item_id"] # 图片详情页面 like_count = item["like_count"] # 喜欢数目 pic_num = item["pic_num"] if "pic_num" in item else 0 # 图片总数 reply_count =item["reply_count"] share_count =item["share_count"] uid = item["uid"] plain = item["plain"] uname = item["uname"] list_infos.append({"avatar":avatar, "item_id":item_id, "like_count":like_count, "pic_num":pic_num, "reply_count":reply_count, "share_count":share_count, "uid":uid, "plain":plain, "uname":uname}) except Exception as e: print(e) continue return list_infos 到现在就实现了,代码跑起来 她专科学历27岁从零开始学习c,c++,python编程语言29岁编写百例教程30岁掌握10种编程语言,用自学的经历告诉你,学编程就找梦想橡皮擦

优秀的个人博客,低调大师

《Python编程:从入门到实践》 第二章 动手试一试

#动手试一试 ##2-1 简单消息: 将一条消息存储到变量中,再将其打印出来。 message = ''Hello!" print(message) ##2-2 多条简单消息: 将一条消息存储到变量中,将其打印出来;再将变量的值修改为一条新消息,并将其打印出来。 message = "Hello~" print(message) message = "Hello bro~" print(message) ##2-3 个性化消息: 将用户的姓名存到一个变量中,并向该用户显示一条消息。显示的消息应非常简单,如“Hello Eric, would you like to learn some Python today?”。 name = "fiona" message = "Hello " + name.title() + ',' + "would you like to learn some Python today?" print(message) ##2-4 调整名字的大小写: 将一个人名存储到一个变量中,再以小写、大写和首字母大写的方式显示这个人名。 name = "FIona" print(name.lower()) print(name.upper()) print(name.title()) ##2-5 名言: 找一句你钦佩的名人说的名言,将这个名人的姓名和他的名言打印出来。输出应类似于下面这样(包括引号):Albert Einstein once said, “A person who never made a mistake never tried anything new.” ##2-6 名言2: 重复练习2-5,但将名人的姓名存储在变量famous_person 中,再创建要显示的消息,并将其存储在变量message 中,然后打印这条消息。 ##2-7 剔除人名中的空白: 存储一个人名,并在其开头和末尾都包含一些空白字符。务必至少使用字符组合"\t" 和"\n" 各一次。打印这个人名,以显示其开头和末尾的空白。然后,分别使用剔除函数lstrip() 、rstrip() 和strip() 对人名进行处理,并将结果打印出来。 name = " fiona " print("\n" + name) print("\n\t" + name) print(name.lstrip()) print(name.rstrip()) print(name.strip()) ##2-8 数字8: 编写4个表达式,它们分别使用加法、减法、乘法和除法运算,但结果都是数字8。为使用print 语句来显示结果,务必将这些表达式用括号括起来,也 就是说,你应该编写4行类似于下面的代码: print(5 + 3) 输出应为4行,其中每行都只包含数字8。 print(4 + 4) print(9 - 1) print(2 * 4) print(8 / 1) ##2-9 最喜欢的数字: 将你最喜欢的数字存储在一个变量中,再使用这个变量创建一条消息,指出你最喜欢的数字,然后将这条消息打印出来。 number = 7 message = "My favorite number is, " print(message + str(number) + '.') ##2-10 添加注释: 选择你编写的两个程序,在每个程序中都至少添加一条注释。如果程序太简单,实在没有什么需要说明的,就在程序文件开头加上你的姓名和当前日 期,再用一句话阐述程序的功能。 ##2-11 Python之禅: 在Python终端会话中执行命令import this ,并粗略地浏览一下其他的指导原则。 import this

优秀的个人博客,低调大师

Python爬虫入门教程 10-100 图虫网多线程爬取

1.图虫网多线程爬取-写在前面 经历了一顿噼里啪啦的操作之后,终于我把博客写到了第10篇,后面,慢慢的会涉及到更多的爬虫模块,有人问scrapy 啥时候开始用,这个我预计要在30篇以后了吧,后面的套路依旧慢节奏的,所以莫着急了,100篇呢,预计4~5个月写完,常见的反反爬后面也会写的,还有fuck login类的内容。 2.图虫网多线程爬取-爬取图虫网 为什么要爬取这个网站,不知道哎~ 莫名奇妙的收到了,感觉图片质量不错,不是那些妖艳贱货 可以比的,所以就开始爬了,搜了一下网上有人也在爬,但是基本都是py2,py3的还没有人写,所以顺手写一篇吧。 3.图虫网多线程爬取-起始页面 https://tuchong.com/explore/这个页面中有很多的标签,每个标签下面都有很多图片,为了和谐,我选择了一个非常好的标签花卉 你可以选择其他的,

优秀的个人博客,低调大师

GO从入门到进阶教程系列 - 研发高性能ORM框架操作mysql篇

上一篇教程我们了解到了基础的GO语法,今天我们来学习如何使用GO操作mysq,下面就直接进入步骤操作环节 技术版权归属 广州市金狮网络科技有限公司 (https://kingc.cn/) ,如需商用请联系公司 1. 先获取mysql驱动,类似Java加载驱动jar, 通过window cmd命令或者Linux控制台执行 go get github.com/go-sql-driver/mysql 2. 我们先写个数据库连接对象方便扩展 // 数据库配置 type DBConfig struct { Host string // 地址IP Port int // 数据库端口 Database string // 数据库名称 Username string // 账号 Password string // 密码 } 3. 通过数据库配置进行mysql实例化 func NewMysql() (*sql.DB, error) { // 初始化mysql连接参数 conf := DBConfig{ Host: "127.0.0.1", Port: 3306, Database: "test", Username: "root", Password: "123456", } // 定义占位符字符串,使用配置值替换%s和%d link := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8", conf.Username, conf.Password, conf.Host, conf.Port, conf.Database) // 打开mysql获得实例对象 db, err := sql.Open("mysql", link) // 打开mysql失败,返回nil对象,以及返回err对象 if err != nil { panic(util.AddStr("mysql初始化失败: ", err.Error())) return nil, errors.New("mysql初始化失败: " + err.Error()) } // 打开mysql成功返回db对象,err=nil return db, nil } 4. 获得mysql操作实例后,进行增删查改操作(由于时间关系,暂时只写了新增示例,包含事务和非事务) func CRUD(db *sql.DB) error { // 编写需要执行的sql createSql := "insert test_user(username, password, age, sex) values(?,?,?,?)" // 预编译sql stmt, err := db.Prepare(createSql) if err != nil { panic("预编译失败: " + err.Error()) } // 提交编译sql对应参数 ret, err := stmt.Exec("zhangsan", "123456", 18, 1) if err != nil { panic("提交数据失败: " + err.Error()) } // 保存成功后获取自增ID fmt.Println(ret.LastInsertId()) return nil } func CRUD1(db *sql.DB) error { // 编写需要执行的sql createSql := "insert test_user(username, password, age, sex) values(?,?,?,?)" // 预编译sql,事务模式 tx, err := db.Begin() if err != nil { panic("开启事务失败: " + err.Error()) } stmt, err := tx.Prepare(createSql) if err != nil { tx.Rollback() // 回滚事务 panic("预编译失败: " + err.Error()) } // 提交编译sql对应参数 ret, err := stmt.Exec("zhangsan", "123456", 18, 1) if err != nil { tx.Rollback() // 回滚事务 panic("提交数据失败: " + err.Error()) } // 保存成功后获取自增ID fmt.Println(ret.LastInsertId()) tx.Commit() // 提交事务 return nil } 5. 最后执行测试用例test func TestMysql1(t *testing.T) { db, err := NewMysql() if err != nil { panic(err) } CRUD(db) } func TestMysql2(t *testing.T) { db, err := NewMysql() if err != nil { panic(err) } CRUD1(db) } 上面的几个示例基本涵盖GO操作mysql的用法,下一篇文章我会讲解如何封装可扩展性强的mysql底层,敬请期待!

优秀的个人博客,低调大师

云数据库POLARDB优势解读系列文章之①——10分钟入门

什么是POLARDB POLARDB 是阿里云自研的下一代关系型分布式数据库,100%兼容MySQL,之前使用MySQL的应用程序不需要修改一行代码,即可使用POLARDB。 POLARDB在运行形态上是一个多节点集群,集群中有一个Writer节点(主节点)和多个Reader节点,他们之间节点间通过分布式文件系统(PolarFileSystem)共享底层的同一份存储(PolarStore)。 POLARDB通过内部的代理层(Proxy)对外提供服务,也就是说所有的应用程序都先经过这层代理,然后才访问到具体的数据库节点。Proxy不仅可以做安全认证(Authorization)和保护(Protection),还可以解析SQL,把写操作(比如事务、Update、Insert、Delete、DDL等)发送到Writer节点,把读操作(比如Se

优秀的个人博客,低调大师

Java入门系列-23-NIO(使用缓冲区和通道对文件操作)

NIO 是什么 java.nio全称java non-blocking(非阻塞) IO(实际上是 new io),是指jdk1.4 及以上版本里提供的新api(New IO) ,为所有的原始类型(boolean类型除外)提供缓存支持的数据容器,使用它可以提供非阻塞式的高伸缩性网络。 NIO与IO的区别 IO NIO 面向流(Stream Oriented) 面向缓冲区(Buffer Oriented) 阻塞IO(Blocking IO) 非阻塞(Non Blocking IO) 无 选择器(Selectors) NIO系统的核心是:通道(Channel)和缓冲区(Buffer) 缓冲区(Buffer) 位于 java.nio 包,所有缓冲区都是 Buffer 抽象类的子类,使用数组对数据进行缓冲。 除了 boolean 类型,Buffer 对每种基本数据类型都有针对的实现类: ByteBuffer CharBuffer ShortBuffer IntBuffer LongBuffer FloatBuffer DoubleBuffer 创建缓冲区通过 xxxBuffer.allocate(int capacity)方法 ByteBuffer buf1 = ByteBuffer.allocate(512); LongBuffer buf2 = LongBuffer.allocate(1024); …… 缓冲区的属性 容量(capacity):表示缓冲区存储数据的最大容量,不能为负数,创建后不可修改。 限制:第一个不可以读取或写入的数据的索引,即位于 limit 后的数据不能读写。不能为负数,不能大于容量。 位置(position):下一个要读取或写入的数据的索引,位置不能为负数,不能大于 limit 标记(mark):标记是一个索引,通过 Buffer 中的 mark() 方法指 Buffer 中一个特定的 position,之后可以通过 reset() 方法回到这个 postion。 Buffer 的常用方法 方法名称 说明 Buffer clear() 清空缓冲区并返回对缓冲区的引用 Buffer flip() 将缓冲区的 limit 设置为当前位置,并将当前位置重置为0 int capacity() 返回 Buffer 的容量大小 boolean hasRemaining() 判断缓冲区是否还有元素 int limit() 返回 限制的位置 Buffer limit(int n) 将设置缓冲区界限为 n,并返回一个具有新 limit 的缓冲区对象 Buffer mark() 对缓冲区设置标记 int position() 返回缓冲区的当前位置 position Buffer position(int n) 将设置缓冲区的当前位置为 n,并返回修改后的 Buffer 对象 int remaining() 返回 position 和 limit 之间的元素个数 Buffer reset() 将位置 position 转到以前设置的 mark 所在的位置 Buffer rewind() 将位置设置为 0,取消设置的 mark Buffer 所有子类提供了两个操作的数据的方法:get() 方法和 put() 方法 缓冲区存取数据操作 package testnio; import java.nio.ByteBuffer; public class TestBuffer1 { public static void main(String[] args) { testuse(); } public static void testuse() { //1.分配一个指定大小的缓冲区 ByteBuffer buf=ByteBuffer.allocate(1024); System.out.println("---------------allocate()----------------"); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); //2.利用 put() 存入数据到缓冲区中 String str="hello"; //将字符串转为 byte 数组存入缓冲区 buf.put(str.getBytes()); System.out.println("---------------put()----------------"); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); //3.切换读取数据模式 buf.flip(); System.out.println("---------------flip()----------------"); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); //4.利用get() 读取缓冲区中的数据 byte[] data=new byte[buf.limit()]; System.out.println("---------------get()----------------"); buf.get(data); System.out.println(new String(data,0,data.length)); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); //5.rewind() 重复读 buf.rewind(); System.out.println("---------------rewind()----------------"); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); //6.clear() 清空缓冲区,但缓冲区中的数据依然存在 buf.clear(); System.out.println("---------------clear()----------------"); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); System.out.println((char)buf.get()); } } 使用 mark()方法标记 package testnio; import java.nio.ByteBuffer; public class TestBuffer2 { public static void main(String[] args) { testmark(); } public static void testmark() { String str="jikedaquan.com"; //创建缓冲区 ByteBuffer buf=ByteBuffer.allocate(1024); //存入数据 buf.put(str.getBytes()); //切换模式 buf.flip(); //临时数组用于接收缓冲区获取的数据,长度与缓冲区 limit 一值 byte[] data=new byte[buf.limit()]; //获取缓冲区的数据从0开始获取4个,存入 data 数组中 buf.get(data, 0, 4); //将数组转为字符串打印 System.out.println(new String(data,0,4)); //打印 position System.out.println(buf.position()); //标记 buf.mark(); System.out.println("---------------再次获取----------------"); //从索引4开始,获取6个字节(余下数据) buf.get(data, 4, 6); System.out.println(new String(data,4,6)); System.out.println(buf.position()); //恢复到标记位置 buf.reset(); System.out.println("---------------reset()----------------"); System.out.println(buf.position()); //判断缓冲区是是有还有剩余数据 if (buf.hasRemaining()) { //获取缓冲区中可以操作的数量 System.out.println("可操作数量:"+buf.remaining()); } } } mark <= position <= limit <= capacity 虽然使用了缓冲区提高了一定的IO速度,但这样的效率仍然不是最高的。非直接缓冲区在与物理磁盘操作中需要经过内核地址空间copy操作,直接缓冲区不经过copy操作,直接操作物理内存映射文件,使用直接缓冲区将大大提高效率。 直接缓冲区进行分配和取消分配所需成本工厂高于非直接缓冲区,一般情况下,最好仅在直接缓冲区能在程序性能方面带来明显好处时分配它们。 直接缓冲区可以通过调用此类的 allocateDirect()工厂方法创建 创建直接缓冲区 package testnio; import java.nio.ByteBuffer; public class TestBuffer3 { public static void main(String[] args) { testAllocateDirect(); } public static void testAllocateDirect() { //创建直接缓冲区 ByteBuffer buf=ByteBuffer.allocateDirect(1024); //是否是直接缓冲区 System.out.println(buf.isDirect()); } } 通道(Channel) 缓冲区仅是运载数据的容器,需要对数据读写还需要有一条通道,这两者是密不可分的。 Channel 接口的主要实现类: FileChannel:用于读取、写入、映射和操作文件的通道 DatagramChannel:通过 UDP 读写网络中的数据通道 SocketChannel:通过 TCP 读写网络中的数据 ServerSocketChannel:可以监听新进来的 TCP 链接,对每一个新进来的连接都会创建一个 SocketChannel 如何获取通道? 1、通过支持通道的对象调用 getChannel() 方法 支持通道的类: FileInputStream FileOutputStream RandomAccessFile DatagramScoket Socket ServerSocket 使用通道和缓冲区实现文件读和写 package testnio; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class TestChannel { public static void main(String[] args) { FileInputStream fis=null; FileOutputStream fos=null; FileChannel inChannel=null; FileChannel outChannel=null; try { //创建输入流 fis=new FileInputStream("F:/1.jpg"); //创建输出流 fos=new FileOutputStream("F:/2.jpg"); //获取通道 inChannel=fis.getChannel(); outChannel=fos.getChannel(); //分配指定大小的缓冲区 ByteBuffer buf=ByteBuffer.allocate(1024); //将通道中的数据存入缓存区 while(inChannel.read(buf)!=-1) { //切换读取数据的模式 buf.flip(); //将读入的缓冲区存入写数据的管道 outChannel.write(buf); //清空缓存区(清空才能再次读入) buf.clear(); } } catch (IOException e) { e.printStackTrace(); }finally { if (fis!=null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } if (fos!=null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } if(inChannel!=null) { try { inChannel.close(); } catch (IOException e) { e.printStackTrace(); } } if (outChannel!=null) { try { outChannel.close(); } catch (IOException e) { e.printStackTrace(); } } } } } 2、通过通道类的静态方法 open() package testnio; import java.io.IOException; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; public class TestOpenAndMapped { public static void main(String[] args) { FileChannel inChannel=null; FileChannel outChannel=null; try { //通过open创建通道 inChannel = FileChannel.open(Paths.get("F:/a.jpg"), StandardOpenOption.READ); outChannel = FileChannel.open(Paths.get("F:/b.jpg"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE); //内存映射文件 直接缓冲区 MappedByteBuffer inMappedBuf=inChannel.map(MapMode.READ_ONLY, 0, inChannel.size()); MappedByteBuffer outMappedBuf=outChannel.map(MapMode.READ_WRITE, 0, inChannel.size()); //直接对缓冲区进行数据的读写操作 byte[] data=new byte[inMappedBuf.limit()]; inMappedBuf.get(data);//读 outMappedBuf.put(data);//写 } catch (IOException e) { e.printStackTrace(); }finally { if (inChannel!=null) { try { inChannel.close(); } catch (IOException e) { e.printStackTrace(); } } if (outChannel!=null) { try { outChannel.close(); } catch (IOException e) { e.printStackTrace(); } } } } } JDK 1.7 新增的方法 open(),参数 path 通常代表一个依赖系统的文件路径,通过Paths.get()获取。 参数 StandardOpenOption 是一个枚举类型,常用值如下: READ :打开读访问 WRITE:打开写访问 APPEND:向后追加 CREATE:创建新文件,存在则覆盖 CREATE_NEW:创建新文件,存在则报错 MappedByteBuffer:直接字节缓冲区,其内容是文件的内存映射区域。 通道数据传输 将数据从源通道传输到其他 Channel 中,transferTo() 和 transferFrom() package testnio; import java.io.IOException; import java.nio.channels.FileChannel; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; public class TestChannelTransfer { public static void main(String[] args) { FileChannel inChannel=null; FileChannel outChannel=null; try { //通过open创建管道 inChannel = FileChannel.open(Paths.get("F:/a.jpg"), StandardOpenOption.READ); outChannel = FileChannel.open(Paths.get("F:/b.jpg"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE); //将inChannel中所有数据发送到outChannel //inChannel.transferTo(0, inChannel.size(), outChannel); outChannel.transferFrom(inChannel, 0, inChannel.size()); } catch (IOException e) { e.printStackTrace(); }finally { if (inChannel!=null) { try { inChannel.close(); } catch (IOException e) { e.printStackTrace(); } } if (outChannel!=null) { try { outChannel.close(); } catch (IOException e) { e.printStackTrace(); } } } } } Channel 负责传输,Buffer 负责存储

优秀的个人博客,低调大师

【Java入门提高篇】Day26 Java容器类详解(八)HashSet源码分析

前面花了好几篇的篇幅把HashMap里里外外说了个遍,大家可能对于源码分析篇已经讳莫如深了。别慌别慌,这一篇来说说集合框架里最偷懒的一个家伙——HashSet,为什么说它是最偷懒的呢,先留个悬念,看完本文之后,你就会知道所言不假了。 本篇将从以下几点来进行介绍: 1.HashSet的特点和使用场景 2.HashSet的示例 3.HashSet的继承结构图 4.HashSet的源码解析 HashSet的特点和使用场景 HashSet是Set家族的一员,所以也具有着Set的全部性质,比如元素无序,元素不可重复,但HashSet也有它自己的一些特性,比如它的查找效率很高,跟HashMap的查找效率一样高(滑稽,看完源码分析你就知道为什么了),它的基本操作如,add/remove/contains/size等都只需要常量时间的开销,并且允许存在null值。 所以HashSet的一个很重要的应用就是去重,把一堆存在重复的数据往HashSet里一丢,里面的元素因为是不会存在重复的,所以再取出来的时候就已经是去重过的数据了,这样HashSet就像一个筛子,把数据筛选了一次。另外HashSet由于其查找效率很高,所以也用于数据的查找,比如将处理过的数据往里面一丢,处理下一个数据的时候先到HashSet中查找一次,如果存在则说明已经处理过,不存在则继续处理。 HashSet的示例 接下来,看一个HashSet的小栗子吧。 1 public class Test { 2 3 public static void main(String[] args){ 4 //nameList中可以存在重复元素,且顺序跟插入的顺序相同 5 List<String> nameList = new ArrayList<>(); 6 nameList.add("Alice"); 7 nameList.add("Frank"); 8 nameList.add("Charles"); 9 nameList.add("Emma"); 10 nameList.add("Jessie"); 11 nameList.add("Frank"); 12 System.out.println(nameList); 13 14 //将列表里的元素插入到Set后,可以去除重复元素,但是没有顺序 15 System.out.println("====add===="); 16 Set<String> nameSet = new HashSet<>(); 17 for (String name : nameList){ 18 nameSet.add(name); 19 } 20 System.out.println(nameSet); 21 22 System.out.println("====contains===="); 23 System.out.println(nameSet.contains("Frank")); 24 System.out.println(nameSet.contains("Bob")); 25 26 System.out.println(nameSet.containsAll(nameList)); 27 28 System.out.println("====remove===="); 29 nameSet.remove("Frank"); 30 System.out.println(nameSet); 31 32 nameSet.forEach(System.out::println); 33 34 System.out.println("====Iterator===="); 35 Iterator iterator = nameSet.iterator(); 36 while (iterator.hasNext()){ 37 System.out.println(iterator.next()); 38 } 39 40 } 41 } 输出如下: [Alice, Frank, Charles, Emma, Jessie, Frank] ====add==== [Charles, Alice, Jessie, Frank, Emma] ====contains==== true false true ====remove==== [Charles, Alice, Jessie, Emma] Charles Alice Jessie Emma ====Iterator==== Charles Alice Jessie Emma HashSet的继承结构图 HashSet是继承自AbstractSet类的,同时实现了Set接口,Serializable接口,Cloneable接口,AbstractSet是Set的便利类,实现了Set接口的部分方法框架,AbstractSet里的方法其实很少,它继承自AbstractCollection类,同时实现了Set接口。 1 public abstract class AbstractSet<E> extends AbstractCollection<E> implements Set<E> { 2 3 protected AbstractSet() { 4 } 5 6 // 比较和哈希 7 8 /** 9 * 比较两个HashSet是否相等 10 * 先比较是否是同一个引用,然后判断o是否实现了Set接口 11 * 再比较它们的大小是否一致,最后判断是否包含了o中所有元素 12 */ 13 public boolean equals(Object o) { 14 if (o == this) 15 return true; 16 17 if (!(o instanceof Set)) 18 return false; 19 Collection<?> c = (Collection<?>) o; 20 if (c.size() != size()) 21 return false; 22 try { 23 return containsAll(c); 24 } catch (ClassCastException unused) { 25 return false; 26 } catch (NullPointerException unused) { 27 return false; 28 } 29 } 30 31 /** 32 * hashCode方法,计算所有元素的hashCode之和 33 */ 34 public int hashCode() { 35 int h = 0; 36 Iterator<E> i = iterator(); 37 while (i.hasNext()) { 38 E obj = i.next(); 39 if (obj != null) 40 h += obj.hashCode(); 41 } 42 return h; 43 } 44 45 /** 46 * 移除所有在集合c中的元素 47 * 如果c中元素个数小于该Set中的元素个数,则遍历c中的元素,使用Set中的remove方法进行移除。 48 * 相反,如果c中的元素个数大于Set中的元素个数,则遍历该Set中的元素,如果存在于c中,则调用迭代器的remove方法进行移除。 49 */ 50 public boolean removeAll(Collection<?> c) { 51 Objects.requireNonNull(c); 52 boolean modified = false; 53 54 if (size() > c.size()) { 55 for (Iterator<?> i = c.iterator(); i.hasNext(); ) 56 //这里其实用了一个很巧妙的位操作,只要remove有一次成功,则返回true,modified就会变成true,之后不管remove返回true还是false,与modified进行或操作的时候结果都是true,所以只要remove中有一次是成功的,modified变量就是true 57 modified |= remove(i.next()); 58 } else { 59 for (Iterator<?> i = iterator(); i.hasNext(); ) { 60 if (c.contains(i.next())) { 61 i.remove(); 62 modified = true; 63 } 64 } 65 } 66 return modified; 67 } 68 69 } HashSet的源码解析 先来看看源码的结构: 相比HashMap是不是方法少了很多很多,也许你会好奇,HashSet是如何用将精华操作塞在这么少的方法中的,嗯,一起来看看它是怎样实现的吧: public Iterator<E> iterator() { return map.keySet().iterator(); } public int size() { return map.size(); } public boolean isEmpty() { return map.isEmpty(); } public boolean contains(Object o) { return map.containsKey(o); } public boolean add(E e) { return map.put(e, PRESENT)==null; } public boolean remove(Object o) { return map.remove(o)==PRESENT; } public void clear() { map.clear(); } @SuppressWarnings("unchecked") public Object clone() { try { HashSet<E> newSet = (HashSet<E>) super.clone(); newSet.map = (HashMap<E, Object>) map.clone(); return newSet; } catch (CloneNotSupportedException e) { throw new InternalError(e); } } 看完你也许会觉得,WTF???主要方法全部是一行代码解决??来看看这个map到底是何方神圣: private transient HashMap<E,Object> map; 惊不惊喜,意不意外,HashSet其实就是里面放了一个HashMap实例,所有操作都是通过HashMap去完成的,至于它的元素不可重复特性,也是借助于HashMap的键值不可重复实现的。嗯,说它是最懒的容器类不过分吧。 再来看看它的几个构造函数: 1 /** 2 * 构造一个空的HashSet,其中的HashMap实例使用默认的初始容量(16)和默认的装载因子(0.75) 3 */ 4 public HashSet() { 5 map = new HashMap<>(); 6 } 7 8 /** 9 * 构建一个包含指定集合所有元素的HashSet,其中的HashMap实例使用默认的装载因子(0.75)和能够容纳下集合中所有元素的初始容量(别忘了,必须是2的幂次方) 10 */ 11 public HashSet(Collection<? extends E> c) { 12 map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16)); 13 addAll(c); 14 } 15 16 /** 17 * 构建一个空的HashSet,其中的HashMap实例使用指定的加载因子和初始容量 18 */ 19 public HashSet(int initialCapacity, float loadFactor) { 20 map = new HashMap<>(initialCapacity, loadFactor); 21 } 22 23 /** 24 * 构建一个空的HashSet,其中的HashMap使用默认的加载因子和指定的容量大小 25 */ 26 public HashSet(int initialCapacity) { 27 map = new HashMap<>(initialCapacity); 28 } 29 30 /** 31 * 构建一个空的LinkedHashSet,这是一个包私有的构造器,仅仅被LinkedHashSet使用,里面的HashMap是一个LinkedHashMap实例,使用指定的容量和装载因子 32 * dummy是一个没有意义的参数,目的是为了跟上面的构造函数区分开来,如果没有这个参数,将不能进行有效重载 33 */ 34 HashSet(int initialCapacity, float loadFactor, boolean dummy) { 35 map = new LinkedHashMap<>(initialCapacity, loadFactor); 36 } 37 38 所以,其实可以灵活定制HashSet的大小和装载因子,只是一般情况下,使用默认的即可,除非知道里面要放的元素个数,并且数量比较大时,才需要进行指定容量,这样可以减少扩容次数。 再来看看剩下的几个函数: /** * 序列化 */ private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { // Write out any hidden serialization magic s.defaultWriteObject(); // Write out HashMap capacity and load factor s.writeInt(map.capacity()); s.writeFloat(map.loadFactor()); // Write out size s.writeInt(map.size()); // Write out all elements in the proper order. for (E e : map.keySet()) s.writeObject(e); } /** * 反序列化 */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { // Read in any hidden serialization magic s.defaultReadObject(); // Read capacity and verify non-negative. int capacity = s.readInt(); if (capacity < 0) { throw new InvalidObjectException("Illegal capacity: " + capacity); } // Read load factor and verify positive and non NaN. float loadFactor = s.readFloat(); if (loadFactor <= 0 || Float.isNaN(loadFactor)) { throw new InvalidObjectException("Illegal load factor: " + loadFactor); } // Read size and verify non-negative. int size = s.readInt(); if (size < 0) { throw new InvalidObjectException("Illegal size: " + size); } // Set the capacity according to the size and load factor ensuring that // the HashMap is at least 25% full but clamping to maximum capacity. capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f), HashMap.MAXIMUM_CAPACITY); // Constructing the backing map will lazily create an array when the first element is // added, so check it before construction. Call HashMap.tableSizeFor to compute the // actual allocation size. Check Map.Entry[].class since it's the nearest public type to // what is actually created. SharedSecrets.getJavaOISAccess() .checkArray(s, Map.Entry[].class, HashMap.tableSizeFor(capacity)); // Create backing HashMap map = (((HashSet<?>)this) instanceof LinkedHashSet ? new LinkedHashMap<E,Object>(capacity, loadFactor) : new HashMap<E,Object>(capacity, loadFactor)); // Read in all elements in the proper order. for (int i=0; i<size; i++) { @SuppressWarnings("unchecked") E e = (E) s.readObject(); map.put(e, PRESENT); } } /** * 可分割式迭代器 */ public Spliterator<E> spliterator() { return new HashMap.KeySpliterator<E,Object>(map, 0, -1, 0, 0); } 嗯,HashSet的介绍就这么多了,很简单吧。 之后还会继续将容器家族中常见的容器类进行源码解析,如LinkedList,LinkedHashMap,LinkedHashSet,TreeMap,TreeSet。嗯,所以预计关于容器类还有至少五篇文章,希望大家能耐心看下去,当然,如果对于源码解析实在没什么兴趣,也可以先跳过这一部分,先知道怎么用就好了。 之后还会努力更新,欢迎大家继续关注。真正重要的东西,用眼睛是看不见的。

优秀的个人博客,低调大师

10小时大数据入门实战(四)-分布式资源调度YARN

1 YARN 产生背景 2 YARN 架构 3 YARN 执行流程 1.client向yarn提交job,首先找ResourceManager分配资源, 2.ResourceManager开启一个Container,在Container中运行一个Application manager 3.Application manager找一台nodemanager启动Application master,计算任务所需的计算 4.Application master向Application manager(Yarn)申请运行任务所需的资源 5.Resource scheduler将资源封装发给Application master 6.Application master将获取到的资源分配给各个nodemanager 7.各个nodemanager得到任务和资源开始执行map task 8.map task执行结束后,开始执行reduce task 9.map task和 reduce task将执行结果反馈给Application master 10.Application master将任务执行的结果反馈pplication manager。 4 YARN 环境搭建 官方文档指南http://archive.cloudera.com/cdh5/cdh/5/hadoop-2.6.0-cdh5.7.0/hadoop-project-dist/hadoop-common/SingleCluster.html 验证 5 提交 PI 的 MapReduce 作业到 TARN 上执行

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册