К сожалению, вы не можете легко переименовать одно вложенное поле, используя withFieldRenamed
, которого вы пытаетесь сделать. Единственный способ, которым я знаю переименовать вложенные поля, - сделать приведение в поле, предоставив тип с той же структурой и типами данных, но новыми именами полей. Это нужно сделать в поле верхнего уровня, поэтому вам нужно делать все поля за один раз. Вот пример:
Создание некоторых входных данных
case class InnerRecord(column1: String, column2: Int)
case class Record(field: InnerRecord)
val df = Seq(
Record(InnerRecord("a", 1)),
Record(InnerRecord("b", 2))
).toDF
df.printSchema
Входные данные выглядят следующим образом:
root
|-- field: struct (nullable = true)
| |-- column1: string (nullable = true)
| |-- column2: integer (nullable = false)
Это пример, используемый withColumnRenamed. Вы увидите, что на самом деле ничего не делает!
val updated = df.withColumnRenamed("field.column1", "field.newname")
updated.printSchema
root
|-- field: struct (nullable = true)
| |-- column1: string (nullable = true)
| |-- column2: integer (nullable = false)
Вот как вы можете сделать это вместо кастинга. Функция будет рекурсивно воссоздавать тип вложенного поля при обновлении имени. В моем случае я просто заменил «column» на «col_». Я также запускал его только в одном поле, но вы можете легко перебирать все поля в схеме.
import org.apache.spark.sql.types._
def rename(dataType: DataType): DataType = dataType match {
case StructType(fields) =>
StructType(fields.map {
case StructField(name, dtype, nullable, meta) =>
val newName = name.replace("column", "col_")
StructField(newName, rename(dtype), nullable, meta)
})
case _ => dataType
}
val fDataType = df.schema.filter(_.name == "field").head.dataType
val updated = df.withColumn("field", $"field".cast(rename(fDataType)))
updated.printSchema
Что печатает:
root
|-- field: struct (nullable = true)
| |-- col_1: string (nullable = true)
| |-- col_2: integer (nullable = false)
Я задам вам вопрос в 2 областях.
[1 133] 1. Во-первых, дизайн проекта
Хорошо иметь представление о том, чего вы хотите, прежде чем задавать вопрос или начинать писать свою программу. Исходя из этого, я предполагаю, что вы пытаетесь создать игру, в которой вы управляете птицей, которая должна избегать препятствий.
В настоящее время вы генерируете свою игру, рисуя компоненты на раме. Это хороший вариант, но я предпочитаю генерировать компоненты в виде объектов (обычно JLabel
, чтобы было легко дать им графику). Создавая объекты, он позволяет изменять объект только на экране, если вы хотите переместить его во время игры, а не перекрашивать все компоненты.
Java является объектно-ориентированным языком программирования, поэтому лучше, если вы разрабатываете свою программу с модульной структурой.
Вот ключевые функции вашей программы, которые необходимо создать:
JFrame
Я обращусь только к создателю препятствий, потому что это то, о чем вы спрашивали в своем вопросе.
При написании создателя препятствий вы хотите написать его таким образом, чтобы он был надежным и простым в использовании несколько раз. В идеале это придет в форме класса или функции, которую можно вызывать и возвращать объект на вашем экране. Это позволит обеспечить максимальную гибкость.
Также стоит отметить, что если вы не планируете использовать свои процедуры в других классах, модификатор public
не нужен, лучше использовать private void
.
[тысяча сто тридцать четыре] 2. Написание кода
Здесь я переписал вашу программу более простым способом. Не переопределяя метод рисования, вы экономите много хлопот.
Обратите внимание на ключевые изменения, которые я сделал:
Теперь я генерирую ваши игровые компоненты как JLabels. Они создаются процедурами в методе Game ().
Прямоугольники больше не окрашиваются, что позволит легче обнаруживать столкновения при написании этой части программы
Обратите внимание, как я создал функцию, которая генерирует числа случайным образом. Теперь я могу легко вызвать эту функцию из других мест программы позже.
Наконец я удалил твой код задержки Thread.sleep(delay);
. Лучше использовать поток таймера для обработки ваших событий, а не задержки. Я бы порекомендовал вам создать поток таймера, который работает параллельно вашей программе. Через полсекунды, когда поток работает, вы можете перемещать птицу в направлении ввода и затем проверять, не столкнулась ли она с объектом из массива columns
.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import java.awt.Rectangle;
public class Game extends JFrame implements ActionListener{
static final int NUMBER_OF_OBSTACLES = 30; // pre-assign the number of obstacles to be generated
// setup our objects for our game
JLabel[] columns = new JLabel [NUMBER_OF_OBSTACLES];
JLabel bird;
public Game() {
// setup the frame
setLayout(null);
setSize (404, 600);
setVisible(true); //sets everything to visable
setDefaultCloseOperation(EXIT_ON_CLOSE);
generateRectangles();
placeBird();
}
private void placeBird() {
// create the new bird
bird = new JLabel("B");
bird.setBackground(Color.red); // set background
bird.setOpaque(true); // make the label's background color not invisible but opaque
bird.setBounds(this.getWidth()/2, 0 , 30, 30); // place bird at top midpoint of screen, its size is a 30,30 square
this.add(bird); // add the bird to our frame
bird.setVisible(true); // show the bird on our frame
}
private void generateRectangles() {
for (int i = 0; i < columns.length; i ++ ) {
columns[i] = new JLabel("" + i); // the name of the object will be shown as the number it is
columns[i].setBackground(Color.green);
columns[i].setOpaque(true);
columns[i].setBounds(randomRect());
this.add(columns[i]);
columns[i].setVisible(true);
}
}
private Rectangle randomRect() {
// create a rectangle to store the bounds of our new object
Rectangle rect = new Rectangle();
rect.height = randomizer(5, 100); // random height 1 to 100
rect.width = randomizer(5, 100); // random width 1 to 100
rect.x = randomizer(1, this.getWidth()); // random x position up to the width of the frame
rect.y = randomizer(30, this.getHeight()); // random y position from 30 up to the height of the frame (NOTE: the 30 gap is so no obstacles start on top of the bird)
return rect;
}
private static int randomizer(int min, int max) {
Random random = new Random(); // create new randomizer
int number = random.nextInt(max - min); //randomizes a number from 0 to dist between them (for example if we are generating from 50 to 100 it will run from 0 to 50)
number = number + min; // then add 50 to match the bottom range
return number; // return this random number to the method that called for it
}
// main runs on startup creating our frame
public static void main(String[] args) {
Game game = new Game(); // start a new game
}
@Override
public void actionPerformed(ActionEvent e) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}