我在Java中寻找一种算法来创建一个对象,该对象将属性设置为对象字符串的第一个非空值。考虑这个数组(为了简单起见,我将使用JSON语法来表示数组):
{
"objects": [
{
"id": 1,
"val1": null,
"val2": null,
"val3": 2.0
},
{
"id": 2,
"val1": null,
"val2": 3.8,
"val3": 6.0
},
{
"id": 3,
"val1": 1.98,
"val2": 1.8,
"val3": 9.0
}
]
}
最后,我想要一个看起来像这样的对象:
{
"id": 1,
"val1": 1.98,
"val2": 3.8,
"val3": 2.0
}
val1
来自第三个对象,val2
来自第一个,而val3
和id
来自第一个,因为这些是找到属性的第一个对象't null。
到目前为止,我在Java中所做的是什么工作真的很好:
// Java code that proceeds a deserialized representation of the
// above mentioned array
int k = 0;
while (bs.getVal1() == null) {
k++;
bs.setVal1(objectArray.get(k).getVal1());
}
但是,我不满意,因为我必须四次编写此代码(getId
,getVal1
,getVal2
,getVal3
)。我相信必须有一个相当通用的方法。任何可以给Java初学者提供建议的Java大师吗?
答案 0 :(得分:2)
在回答实际问题之前,这是编写现有循环的更好方法:(将Object
替换为实际类型)
for (Object o : objectArray) {
Double d = o.getVal1();
if (d != null) {
bs.setVal1(d);
break;
}
}
考虑到现在布置物体的方式,没有更好的方法。但是如果你改变对象的结构,你可以做得更好。
一种方法是将不同的值字段(val1, val2, ...
)放入数组中:
Double val[] = new Double[3]; // for 3 val fields
public Double getVal(int index) {
return val[index];
}
public void setVal(int index, Double value) {
val[index] = value;
}
然后,您只需按索引访问字段,并在对象数组的一次迭代中设置bs
的所有字段:
for (Object o : objectArray) {
for (int i = 0; i < 3; i++) {
Double d = o.getVal(i);
if (d != null) {
bs.setVal(i, d);
break;
}
}
}
答案 1 :(得分:1)
+1 - 代码重复是Java中有时难以克服的问题。
一种解决方案是创建一个Iterable类,它允许您迭代其中一个对象中的值,就像它们在数组中一样。这会消除代码中的一些重复,而不会牺牲命名变量的易读性。
请注意,在下面的代码中,我创建了一个单独的可迭代类,但您也可以简单地使POD类可迭代(其中一个是最适合您的选项取决于您未覆盖的详细信息示例代码):
(警告 - 尚未测试)
import java.util.Iterator;
public class Main {
static class POD{
Integer id; Double val1; Double val2; Double val3;
public POD(Integer i, Double v1, Double v2, Double v3){
id=i; val1=v1; val2=v2; val3=v3;
}
public POD(){ }
}
static class PODFields implements Iterable<Number>{
private POD pod;
public PODFields(POD pod){
this.pod=pod;
}
public PODFieldsIterator iterator() {
return new PODFieldsIterator(pod);
}
}
static class PODFieldsIterator implements Iterator<Number>{
int cur=0;
POD pod;
public PODFieldsIterator(POD pod) {
this.pod=pod;
}
public boolean hasNext() { return cur<4; }
public Number next() {
switch(cur++){
case 0:
return pod.id;
case 1:
return pod.val1;
case 2:
return pod.val2;
case 3:
return pod.val3;
}
return null;//(there are better ways to handle this case, but whatever)
}
public void remove() { throw new UnsupportedOperationException("You cannot remove a POD field."); }
}
public static void main(String[] args) {
POD [] objectArray = {new POD(1,null,null,2.0),
new POD(1,null,null,2.0),
new POD(1,null,null,2.0),
new POD(1,null,null,2.0)};
POD finalObject=new POD();
for (POD cur : objectArray){
PODFieldsIterator curFields = new PODFields(cur).iterator();
for (Number finalValue : new PODFields(finalObject)){
Number curValue = curFields.next();
if (finalValue==null)
finalValue=curValue;
}
}
for (Number finalValue : new PODFields(finalObject))
System.out.println(finalValue);
}
}
编辑:糟糕 - 看起来我忘了数字是不可变的。我想你可以通过迭代器返回函子或其他东西来克服这个问题,但这可能会有点过分。
答案 2 :(得分:1)
每当您想要消除代码重复时,您首先要寻找的是您是否可以提取可重用的方法。反射可以帮助您以可重用的方式调用任意方法。它不是世界上最漂亮的东西,但这种方法适合你:
@SuppressWarnings("unchecked")
public <T> T firstNonNull(String methodName, TestObject... objs) {
try {
Method m = TestObject.class.getMethod(methodName, (Class[])null);
for (TestObject testObj : objs) {
T retVal = (T)m.invoke(testObj, (Object[])null);
if (retVal != null) return retVal;
}
return null;
} catch (Exception e) {
//log, at a minimum
return null;
}
}
使用这样的类进行测试:
public class TestObject {
Integer id;
String val1;
Map<String, Boolean> val2;
public int getId() {
return id;
}
public String getVal1() {
return val1;
}
public Map<String, Boolean> getVal2() {
return val2;
}
}
此JUnit测试演示了它的用法:
@org.junit.Test
public void testFirstNonNull() {
TestObject t1 = new TestObject();
t1.id = 1;
t1.val1 = "Hello";
t1.val2 = null;
TestObject t2 = new TestObject();
Map<String, Boolean> map = new HashMap<String, Boolean>();
t2.id = null;
t2.val1 = "World";
t2.val2 = map;
TestObject result = new TestObject();
result.id = firstNonNull("getId", t1, t2);
result.val1 = firstNonNull("getVal1", t1, t2);
result.val2 = firstNonNull("getVal2", t1, t2);
Assert.assertEquals(result.id, (Integer)1);
Assert.assertEquals(result.val1, "Hello");
Assert.assertSame(result.val2, map);
}