Swift 从字典数据构建树的最有效方法
我有一本结构如下的词典:Swift 从字典数据构建树的最有效方法,swift,dictionary,tree,Swift,Dictionary,Tree,我有一本结构如下的词典:[Point:[Line]],其中: 点-包含两个坐标(X,Y)的自定义数据结构 Line—包含直线的第一个点和最后一个点的元组(点,点) 关键点-线的第一个点 因此,它是一个按第一点分组的行字典,如下所示: [a: [(a,b),(a,c)], b: [(b,c), (b,d)], c: [(c,d)] ] import Foundation typealias ProcessedLine = (UUID, [LineModel]) typealias Line
[Point:[Line]]
,其中:
- 点-包含两个坐标(X,Y)的自定义数据结构
- Line—包含直线的第一个点和最后一个点的元组(点,点)
- 关键点-线的第一个点
[a: [(a,b),(a,c)], b: [(b,c), (b,d)], c: [(c,d)] ]
import Foundation
typealias ProcessedLine = (UUID, [LineModel])
typealias LinePointDict = [Point: [Line]]
class LineAggregator {
var builderStack: Stack<LineModel.LineModelBuilder>
let startPointMap: LinePointDict
var result: ProcessedLine
var currentBuilder: (Point, LineModel.LineModelBuilder)?
let startPoint: Point
init(LineUid: UUID, startPointMap: LinePointDict, startPoint: Point) {
self.builderStack = Stack<LineModel.LineModelBuilder>()
self.startPointMap = startPointMap
self.result = (LineUid, [])
self.startPoint = startPoint
self.currentBuilder = nil
}
func getLineAggregation() -> ProcessedLine {
for Line in startPointMap[startPoint]! {
var bldr = LineModel.LineModelBuilder(initialLineUuid: result.0)
bldr = bldr.addLine(Line: Line)
builderStack.push(bldr)
}
return aggregateModels()
}
func aggregateModels() -> ProcessedLine {
while !builderStack.isEmpty() {
takeBuilderFromStack()
aggregateLine()
}
return result
}
/**
* This functions pops Builder object from stack if the stack is not empty and sets it as a Current object to be processed.
* @param object
* @return
*/
private func takeBuilderFromStack() {
if(!builderStack.isEmpty()) {
let curBuilder = builderStack.pop()!
currentBuilder = (curBuilder.getLastElement(), curBuilder)
}
}
private func aggregateLine() {
if currentBuilder?.1.isLastAdded() ?? true {
//If there is only one Line in the Line model
if(currentBuilder!.1.isLastAddedLineLast()) {
result.1.append(currentBuilder!.1.build())
return
}
if(!builderStack.isEmpty()) {
print("ERROR: Empty builder stack! Such situation should not happen. Pay attention at it.");
builderStack.removeAll()
}
return
}
if currentBuilder != nil {
for Line in startPointMap[currentBuilder!.0]! {
var newBuilder = LineModel.LineModelBuilder(builder: currentBuilder!.1)
newBuilder = newBuilder.addLine(Line: Line)
if Line.isLast {
result.1.append(newBuilder.build())
} else {
builderStack.push(newBuilder)
}
}
}
}
}
目标是将此字典转换为数据结构列表,如下所示:
[a: [(a,b),(a,c)], b: [(b,c), (b,d)], c: [(c,d)] ]
import Foundation
typealias ProcessedLine = (UUID, [LineModel])
typealias LinePointDict = [Point: [Line]]
class LineAggregator {
var builderStack: Stack<LineModel.LineModelBuilder>
let startPointMap: LinePointDict
var result: ProcessedLine
var currentBuilder: (Point, LineModel.LineModelBuilder)?
let startPoint: Point
init(LineUid: UUID, startPointMap: LinePointDict, startPoint: Point) {
self.builderStack = Stack<LineModel.LineModelBuilder>()
self.startPointMap = startPointMap
self.result = (LineUid, [])
self.startPoint = startPoint
self.currentBuilder = nil
}
func getLineAggregation() -> ProcessedLine {
for Line in startPointMap[startPoint]! {
var bldr = LineModel.LineModelBuilder(initialLineUuid: result.0)
bldr = bldr.addLine(Line: Line)
builderStack.push(bldr)
}
return aggregateModels()
}
func aggregateModels() -> ProcessedLine {
while !builderStack.isEmpty() {
takeBuilderFromStack()
aggregateLine()
}
return result
}
/**
* This functions pops Builder object from stack if the stack is not empty and sets it as a Current object to be processed.
* @param object
* @return
*/
private func takeBuilderFromStack() {
if(!builderStack.isEmpty()) {
let curBuilder = builderStack.pop()!
currentBuilder = (curBuilder.getLastElement(), curBuilder)
}
}
private func aggregateLine() {
if currentBuilder?.1.isLastAdded() ?? true {
//If there is only one Line in the Line model
if(currentBuilder!.1.isLastAddedLineLast()) {
result.1.append(currentBuilder!.1.build())
return
}
if(!builderStack.isEmpty()) {
print("ERROR: Empty builder stack! Such situation should not happen. Pay attention at it.");
builderStack.removeAll()
}
return
}
if currentBuilder != nil {
for Line in startPointMap[currentBuilder!.0]! {
var newBuilder = LineModel.LineModelBuilder(builder: currentBuilder!.1)
newBuilder = newBuilder.addLine(Line: Line)
if Line.isLast {
result.1.append(newBuilder.build())
} else {
builderStack.push(newBuilder)
}
}
}
}
}
[a: [(a,b),(a,c)], b: [(b,c), (b,d)], c: [(c,d)] ]
import Foundation
typealias ProcessedLine = (UUID, [LineModel])
typealias LinePointDict = [Point: [Line]]
class LineAggregator {
var builderStack: Stack<LineModel.LineModelBuilder>
let startPointMap: LinePointDict
var result: ProcessedLine
var currentBuilder: (Point, LineModel.LineModelBuilder)?
let startPoint: Point
init(LineUid: UUID, startPointMap: LinePointDict, startPoint: Point) {
self.builderStack = Stack<LineModel.LineModelBuilder>()
self.startPointMap = startPointMap
self.result = (LineUid, [])
self.startPoint = startPoint
self.currentBuilder = nil
}
func getLineAggregation() -> ProcessedLine {
for Line in startPointMap[startPoint]! {
var bldr = LineModel.LineModelBuilder(initialLineUuid: result.0)
bldr = bldr.addLine(Line: Line)
builderStack.push(bldr)
}
return aggregateModels()
}
func aggregateModels() -> ProcessedLine {
while !builderStack.isEmpty() {
takeBuilderFromStack()
aggregateLine()
}
return result
}
/**
* This functions pops Builder object from stack if the stack is not empty and sets it as a Current object to be processed.
* @param object
* @return
*/
private func takeBuilderFromStack() {
if(!builderStack.isEmpty()) {
let curBuilder = builderStack.pop()!
currentBuilder = (curBuilder.getLastElement(), curBuilder)
}
}
private func aggregateLine() {
if currentBuilder?.1.isLastAdded() ?? true {
//If there is only one Line in the Line model
if(currentBuilder!.1.isLastAddedLineLast()) {
result.1.append(currentBuilder!.1.build())
return
}
if(!builderStack.isEmpty()) {
print("ERROR: Empty builder stack! Such situation should not happen. Pay attention at it.");
builderStack.removeAll()
}
return
}
if currentBuilder != nil {
for Line in startPointMap[currentBuilder!.0]! {
var newBuilder = LineModel.LineModelBuilder(builder: currentBuilder!.1)
newBuilder = newBuilder.addLine(Line: Line)
if Line.isLast {
result.1.append(newBuilder.build())
} else {
builderStack.push(newBuilder)
}
}
}
}
}
<代码>导入基础
typealias ProcessedLine=(UUID,[LineModel])
typealias LinePointDict=[Point:[Line]]
类线性集总器{
var builderStack:Stack
让startPointMap:LinePointDict
var结果:ProcessedLine
var currentBuilder:(点,LineModel.LineModelBuilder)?
让我们开始点:点
init(LineUid:UUID,startPointMap:LinePointDict,startPoint:Point){
self.builderStack=Stack()
self.startPointMap=startPointMap
self.result=(LineUid,[])
self.startPoint=startPoint
self.currentBuilder=nil
}
func getLineAggregation()->ProcessedLine{
对于startPointMap[startPoint]中的行{
var bldr=LineModel.LineModelBuilder(initialLineUuid:result.0)
bldr=bldr.addLine(行:行)
builderStack.push(bldr)
}
返回聚合模型()
}
func aggregateModels()->ProcessedLine{
while!builderStack.isEmpty(){
takeBuilderFromStack()
聚合线()
}
返回结果
}
/**
*如果堆栈不为空,此函数将从堆栈中弹出生成器对象,并将其设置为要处理的当前对象。
*@param对象
*@返回
*/
私有函数takeBuilderFromStack(){
如果(!builderStack.isEmpty()){
让curBuilder=builderStack.pop()!
currentBuilder=(currentBuilder.getLastElement(),currentBuilder)
}
}
私有函数聚合线(){
如果currentBuilder?.1.isLastAdded()??为真{
//如果线模型中只有一条线
如果(currentBuilder!.1.isLastAddedLineLast()){
结果.1.追加(currentBuilder!.1.build())
返回
}
如果(!builderStack.isEmpty()){
打印(“错误:生成器堆栈为空!不应发生这种情况。请注意。”);
builderStack.removeAll()
}
返回
}
如果currentBuilder!=nil{
对于startPointMap[currentBuilder!.0]中的行{
var newBuilder=LineModel.LineModelBuilder(生成器:currentBuilder!.1)
newBuilder=newBuilder.addLine(行:行)
if Line.isLast{
result.1.append(newBuilder.build())
}否则{
builderStack.push(新造船商)
}
}
}
}
}
这个解决方案非常简单。它是有效的,但是我的字典中有大量的数据,而且组合的数量更大,所以这个算法非常慢,而且内存效率也不高
主要的缓慢是由于向堆栈中添加数据和从堆栈中检索数据造成的,堆栈具有以下实现:
import Foundation
protocol Stackable {
associatedtype Element
func peek() -> Element?
mutating func push(_ element: Element)
@discardableResult mutating func pop() -> Element?
}
extension Stackable {
var isEmpty: Bool { peek() == nil }
}
struct Stack<Element>: Stackable where Element: Equatable {
private var storage = [Element]()
func peek() -> Element? { storage.first }
mutating func push(_ element: Element) { storage.append(element) }
mutating func pop() -> Element? { storage.popLast() }
func size() -> Int { storage.count }
func isEmpty() -> Bool { storage.isEmpty }
mutating func removeAll() { storage.removeAll() }
}
extension Stack: Equatable {
static func == (lhs: Stack<Element>, rhs: Stack<Element>) -> Bool { lhs.storage == rhs.storage }
}
extension Stack: CustomStringConvertible {
var description: String { "\(storage)" }
}
extension Stack: ExpressibleByArrayLiteral {
init(arrayLiteral elements: Self.Element...) { storage = elements }
}
<代码>导入基础
协议可堆叠{
关联类型元素
func peek()->元素?
变异函数推送(u元素:元素)
@discardableResult mutating func pop()->元素?
}
扩展可堆叠{
var isEmpty:Bool{peek()==nil}
}
结构堆栈:可堆叠,其中元素:Equatable{
私有变量存储=[Element]()
func peek()->元素?{storage.first}
mutating func push(u元素:元素){storage.append(元素)}
变异func pop()->元素?{storage.popLast()}
func size()->Int{storage.count}
func isEmpty()->Bool{storage.isEmpty}
变异func removeAll(){storage.removeAll()}
}
扩展堆栈:可均衡{
静态func==(lhs:Stack,rhs:Stack)->Bool{lhs.storage==rhs.storage}
}
扩展堆栈:CustomStringConvertible{
变量说明:字符串{“\(存储)”}
}
扩展堆栈:ExpressibleByArrayLiteral{
init(arrayliteralelements:Self.Element…{storage=elements}
}
另一个瓶颈是复制数据和deinit
方法
我试图找到一个更好的解决方案,但还是什么都找不到。如有任何建议,我将不胜感激。谢谢。虽然builder模式很有用,但我认为在这种情况下,它只会使直接的解决方案复杂化,尽管正如您所看到的,我将介绍一些更复杂的解决方案,但它们是基于对第一个简单解决方案的性能优化 正如您所注意到的,初始化和取消初始化类有点慢。实际上,最糟糕的部分是动态内存分配。类是强大的,肯定有其用途,但它们不是Swift工具箱中最快的工具。除非将方法设置为final,否则调用它们可能需要虚拟分派。这种情况也可能发生在协议中,这取决于他们声明的细节,尽管在这种情况下,它被称为“证人表重击”。但类最糟糕的地方是它们的实例几乎可以乱放在内存中的任何地方。这对处理器的片上cach来说简直是地狱